xref: /lighttpd1.4/src/mod_echo.c (revision 5e14db43)
1 /*
2  * mod_echo - test/debugging module to echo request back to client as response
3  *
4  * Copyright(c) 2021 Glenn Strauss gstrauss()gluelogic.com  All rights reserved
5  * License: BSD 3-clause (same as lighttpd)
6  *
7  * Note: module is hard-coded to handle requests to the exact uri-path: "/echo"
8  *
9  * Note: testing GET requests with Content-Length requires lighttpd.conf:
10  *   server.http-parseopts += ("method-get-body" => "enable")
11  */
12 #include "first.h"
13 
14 #include <stdlib.h>
15 
16 #include "base.h"
17 #include "fdevent.h"
18 #include "http_chunk.h"
19 #include "plugin.h"
20 #include "request.h"
21 #include "response.h"
22 
23 typedef struct {
24     PLUGIN_DATA;
25 } plugin_data;
26 
INIT_FUNC(mod_echo_init)27 INIT_FUNC(mod_echo_init) {
28     return ck_calloc(1, sizeof(plugin_data));
29 }
30 
31 #if 1 /*(would be simpler if not supporting streaming w/ bufmin)*/
32 
mod_echo_request_body(request_st * const r)33 static handler_t mod_echo_request_body(request_st * const r) {
34     chunkqueue * const cq = &r->reqbody_queue;
35     chunkqueue_remove_finished_chunks(cq); /* unnecessary? */
36     off_t cqlen = chunkqueue_length(cq);
37     if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
38         && r->resp_body_started) {
39         if (chunkqueue_length(&r->write_queue) > 65536 - 4096) {
40             /* wait for more data to be sent to client */
41             return HANDLER_WAIT_FOR_EVENT;
42         }
43         else {
44             if (cqlen > 65536) {
45                 cqlen = 65536;
46                 joblist_append(r->con);
47             }
48         }
49     }
50 
51     if (0 != http_chunk_transfer_cqlen(r, cq, (size_t)cqlen))
52         return HANDLER_ERROR;
53 
54     if (cq->bytes_out == (off_t)r->reqbody_length) {
55         /* sent all request body input */
56         http_response_backend_done(r);
57         return HANDLER_FINISHED;
58     }
59 
60     cqlen = chunkqueue_length(cq);
61     if (cq->bytes_in != (off_t)r->reqbody_length && cqlen < 65536 - 16384) {
62         /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
63         if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
64             r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
65             r->con->is_readable = 1; /* trigger optimistic read from client */
66         }
67     }
68     return HANDLER_WAIT_FOR_EVENT;
69 }
70 
SUBREQUEST_FUNC(mod_echo_handle_subrequest)71 SUBREQUEST_FUNC(mod_echo_handle_subrequest) {
72     UNUSED(p_d);
73 
74     handler_t rc = mod_echo_request_body(r);
75     if (rc != HANDLER_WAIT_FOR_EVENT) return rc;
76 
77     chunkqueue * const cq = &r->reqbody_queue;
78     if (cq->bytes_in != (off_t)r->reqbody_length) {
79         /*(64k - 4k to attempt to avoid temporary files
80          * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
81         if (chunkqueue_length(cq) > 65536 - 4096
82             && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)) {
83             r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
84             return HANDLER_WAIT_FOR_EVENT;
85         }
86         else {
87             rc = r->con->reqbody_read(r);
88             if (rc != HANDLER_GO_ON) return rc;
89 
90             if (-1 == r->reqbody_length
91                 && !(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST))
92                 return HANDLER_WAIT_FOR_EVENT;
93         }
94     }
95 
96     return mod_echo_request_body(r);
97 }
98 
99 #else /*(would be simpler if not supporting streaming w/ bufmin (above))*/
100 
SUBREQUEST_FUNC(mod_echo_handle_subrequest)101 SUBREQUEST_FUNC(mod_echo_handle_subrequest) {
102     UNUSED(p_d);
103 
104     handler_t rc = r->con->reqbody_read(r);
105     if (rc != HANDLER_GO_ON) return rc;
106 
107     chunkqueue * const cq = &r->reqbody_queue;
108     if (0 != http_chunk_transfer_cqlen(r, cq, chunkqueue_length(cq)))
109         return HANDLER_ERROR;
110 
111     if (cq->bytes_out == (off_t)r->reqbody_length) {
112         http_response_backend_done(r);
113         return HANDLER_FINISHED;
114     }
115     return HANDLER_WAIT_FOR_EVENT;
116 }
117 
118 #endif
119 
URIHANDLER_FUNC(mod_echo_handle_uri_clean)120 URIHANDLER_FUNC(mod_echo_handle_uri_clean) {
121     plugin_data *p = p_d;
122     if (NULL == r->handler_module
123         && buffer_eq_slen(&r->uri.path, CONST_STR_LEN("/echo"))) {
124         r->handler_module = p->self;
125         r->resp_body_started = 1;
126         /* XXX: future: might echo request headers here */
127         if (0 == r->reqbody_length) {
128             r->resp_body_finished = 1;
129             return HANDLER_FINISHED;
130         }
131     }
132     return HANDLER_GO_ON;
133 }
134 
135 
136 __attribute_cold__
137 int mod_echo_plugin_init(plugin *p);
mod_echo_plugin_init(plugin * p)138 int mod_echo_plugin_init(plugin *p) {
139     p->version                 = LIGHTTPD_VERSION_ID;
140     p->name                    = "echo";
141 
142     p->handle_uri_clean        = mod_echo_handle_uri_clean;
143     p->handle_subrequest       = mod_echo_handle_subrequest;
144     p->init                    = mod_echo_init;
145 
146     return 0;
147 }
148