1517010f2SGlenn Strauss /* 2517010f2SGlenn Strauss * mod_echo - test/debugging module to echo request back to client as response 3517010f2SGlenn Strauss * 4517010f2SGlenn Strauss * Copyright(c) 2021 Glenn Strauss gstrauss()gluelogic.com All rights reserved 5517010f2SGlenn Strauss * License: BSD 3-clause (same as lighttpd) 6517010f2SGlenn Strauss * 7517010f2SGlenn Strauss * Note: module is hard-coded to handle requests to the exact uri-path: "/echo" 8517010f2SGlenn Strauss * 9517010f2SGlenn Strauss * Note: testing GET requests with Content-Length requires lighttpd.conf: 10517010f2SGlenn Strauss * server.http-parseopts += ("method-get-body" => "enable") 11517010f2SGlenn Strauss */ 12517010f2SGlenn Strauss #include "first.h" 13517010f2SGlenn Strauss 14517010f2SGlenn Strauss #include <stdlib.h> 15517010f2SGlenn Strauss 16517010f2SGlenn Strauss #include "base.h" 17517010f2SGlenn Strauss #include "fdevent.h" 18517010f2SGlenn Strauss #include "http_chunk.h" 19517010f2SGlenn Strauss #include "plugin.h" 20517010f2SGlenn Strauss #include "request.h" 21517010f2SGlenn Strauss #include "response.h" 22517010f2SGlenn Strauss 23517010f2SGlenn Strauss typedef struct { 24517010f2SGlenn Strauss PLUGIN_DATA; 25517010f2SGlenn Strauss } plugin_data; 26517010f2SGlenn Strauss INIT_FUNC(mod_echo_init)27517010f2SGlenn StraussINIT_FUNC(mod_echo_init) { 28*5e14db43SGlenn Strauss return ck_calloc(1, sizeof(plugin_data)); 29517010f2SGlenn Strauss } 30517010f2SGlenn Strauss 31517010f2SGlenn Strauss #if 1 /*(would be simpler if not supporting streaming w/ bufmin)*/ 32517010f2SGlenn Strauss mod_echo_request_body(request_st * const r)33517010f2SGlenn Straussstatic handler_t mod_echo_request_body(request_st * const r) { 34517010f2SGlenn Strauss chunkqueue * const cq = &r->reqbody_queue; 35517010f2SGlenn Strauss chunkqueue_remove_finished_chunks(cq); /* unnecessary? */ 36517010f2SGlenn Strauss off_t cqlen = chunkqueue_length(cq); 37517010f2SGlenn Strauss if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 38517010f2SGlenn Strauss && r->resp_body_started) { 39517010f2SGlenn Strauss if (chunkqueue_length(&r->write_queue) > 65536 - 4096) { 40517010f2SGlenn Strauss /* wait for more data to be sent to client */ 41517010f2SGlenn Strauss return HANDLER_WAIT_FOR_EVENT; 42517010f2SGlenn Strauss } 43517010f2SGlenn Strauss else { 44517010f2SGlenn Strauss if (cqlen > 65536) { 45517010f2SGlenn Strauss cqlen = 65536; 46517010f2SGlenn Strauss joblist_append(r->con); 47517010f2SGlenn Strauss } 48517010f2SGlenn Strauss } 49517010f2SGlenn Strauss } 50517010f2SGlenn Strauss 51517010f2SGlenn Strauss if (0 != http_chunk_transfer_cqlen(r, cq, (size_t)cqlen)) 52517010f2SGlenn Strauss return HANDLER_ERROR; 53517010f2SGlenn Strauss 54517010f2SGlenn Strauss if (cq->bytes_out == (off_t)r->reqbody_length) { 55517010f2SGlenn Strauss /* sent all request body input */ 56517010f2SGlenn Strauss http_response_backend_done(r); 57517010f2SGlenn Strauss return HANDLER_FINISHED; 58517010f2SGlenn Strauss } 59517010f2SGlenn Strauss 60517010f2SGlenn Strauss cqlen = chunkqueue_length(cq); 61517010f2SGlenn Strauss if (cq->bytes_in != (off_t)r->reqbody_length && cqlen < 65536 - 16384) { 62517010f2SGlenn Strauss /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ 63517010f2SGlenn Strauss if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { 64517010f2SGlenn Strauss r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; 65517010f2SGlenn Strauss r->con->is_readable = 1; /* trigger optimistic read from client */ 66517010f2SGlenn Strauss } 67517010f2SGlenn Strauss } 68517010f2SGlenn Strauss return HANDLER_WAIT_FOR_EVENT; 69517010f2SGlenn Strauss } 70517010f2SGlenn Strauss SUBREQUEST_FUNC(mod_echo_handle_subrequest)71517010f2SGlenn StraussSUBREQUEST_FUNC(mod_echo_handle_subrequest) { 72517010f2SGlenn Strauss UNUSED(p_d); 73517010f2SGlenn Strauss 74517010f2SGlenn Strauss handler_t rc = mod_echo_request_body(r); 75517010f2SGlenn Strauss if (rc != HANDLER_WAIT_FOR_EVENT) return rc; 76517010f2SGlenn Strauss 77517010f2SGlenn Strauss chunkqueue * const cq = &r->reqbody_queue; 78517010f2SGlenn Strauss if (cq->bytes_in != (off_t)r->reqbody_length) { 79517010f2SGlenn Strauss /*(64k - 4k to attempt to avoid temporary files 80517010f2SGlenn Strauss * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ 81517010f2SGlenn Strauss if (chunkqueue_length(cq) > 65536 - 4096 82517010f2SGlenn Strauss && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)) { 83517010f2SGlenn Strauss r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; 84517010f2SGlenn Strauss return HANDLER_WAIT_FOR_EVENT; 85517010f2SGlenn Strauss } 86517010f2SGlenn Strauss else { 87517010f2SGlenn Strauss rc = r->con->reqbody_read(r); 88517010f2SGlenn Strauss if (rc != HANDLER_GO_ON) return rc; 89517010f2SGlenn Strauss 90517010f2SGlenn Strauss if (-1 == r->reqbody_length 91517010f2SGlenn Strauss && !(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)) 92517010f2SGlenn Strauss return HANDLER_WAIT_FOR_EVENT; 93517010f2SGlenn Strauss } 94517010f2SGlenn Strauss } 95517010f2SGlenn Strauss 96517010f2SGlenn Strauss return mod_echo_request_body(r); 97517010f2SGlenn Strauss } 98517010f2SGlenn Strauss 99517010f2SGlenn Strauss #else /*(would be simpler if not supporting streaming w/ bufmin (above))*/ 100517010f2SGlenn Strauss SUBREQUEST_FUNC(mod_echo_handle_subrequest)101517010f2SGlenn StraussSUBREQUEST_FUNC(mod_echo_handle_subrequest) { 102517010f2SGlenn Strauss UNUSED(p_d); 103517010f2SGlenn Strauss 104517010f2SGlenn Strauss handler_t rc = r->con->reqbody_read(r); 105517010f2SGlenn Strauss if (rc != HANDLER_GO_ON) return rc; 106517010f2SGlenn Strauss 107517010f2SGlenn Strauss chunkqueue * const cq = &r->reqbody_queue; 108517010f2SGlenn Strauss if (0 != http_chunk_transfer_cqlen(r, cq, chunkqueue_length(cq))) 109517010f2SGlenn Strauss return HANDLER_ERROR; 110517010f2SGlenn Strauss 111517010f2SGlenn Strauss if (cq->bytes_out == (off_t)r->reqbody_length) { 112517010f2SGlenn Strauss http_response_backend_done(r); 113517010f2SGlenn Strauss return HANDLER_FINISHED; 114517010f2SGlenn Strauss } 115517010f2SGlenn Strauss return HANDLER_WAIT_FOR_EVENT; 116517010f2SGlenn Strauss } 117517010f2SGlenn Strauss 118517010f2SGlenn Strauss #endif 119517010f2SGlenn Strauss URIHANDLER_FUNC(mod_echo_handle_uri_clean)120517010f2SGlenn StraussURIHANDLER_FUNC(mod_echo_handle_uri_clean) { 121517010f2SGlenn Strauss plugin_data *p = p_d; 122517010f2SGlenn Strauss if (NULL == r->handler_module 123517010f2SGlenn Strauss && buffer_eq_slen(&r->uri.path, CONST_STR_LEN("/echo"))) { 124517010f2SGlenn Strauss r->handler_module = p->self; 125517010f2SGlenn Strauss r->resp_body_started = 1; 126517010f2SGlenn Strauss /* XXX: future: might echo request headers here */ 127517010f2SGlenn Strauss if (0 == r->reqbody_length) { 128517010f2SGlenn Strauss r->resp_body_finished = 1; 129517010f2SGlenn Strauss return HANDLER_FINISHED; 130517010f2SGlenn Strauss } 131517010f2SGlenn Strauss } 132517010f2SGlenn Strauss return HANDLER_GO_ON; 133517010f2SGlenn Strauss } 134517010f2SGlenn Strauss 135b82d7b8aSGlenn Strauss 136b82d7b8aSGlenn Strauss __attribute_cold__ 137517010f2SGlenn Strauss int mod_echo_plugin_init(plugin *p); mod_echo_plugin_init(plugin * p)138517010f2SGlenn Straussint mod_echo_plugin_init(plugin *p) { 139517010f2SGlenn Strauss p->version = LIGHTTPD_VERSION_ID; 140517010f2SGlenn Strauss p->name = "echo"; 141517010f2SGlenn Strauss 142517010f2SGlenn Strauss p->handle_uri_clean = mod_echo_handle_uri_clean; 143517010f2SGlenn Strauss p->handle_subrequest = mod_echo_handle_subrequest; 144517010f2SGlenn Strauss p->init = mod_echo_init; 145517010f2SGlenn Strauss 146517010f2SGlenn Strauss return 0; 147517010f2SGlenn Strauss } 148