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)27INIT_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)33static 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)71SUBREQUEST_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)101SUBREQUEST_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)120URIHANDLER_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)138int 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