1 /* 2 * reqpool - request objects 3 * 4 * Copyright(c) 2020 Glenn Strauss gstrauss()gluelogic.com All rights reserved 5 * License: BSD 3-clause (same as lighttpd) 6 */ 7 #include "first.h" 8 #include "reqpool.h" 9 10 #include <stdlib.h> 11 #include <string.h> 12 13 #include "base.h" 14 #include "buffer.h" 15 #include "chunk.h" 16 #include "plugin.h" 17 #include "plugin_config.h" 18 #include "request.h" 19 #include "response.h" 20 21 22 static const request_config *request_config_defaults; 23 24 25 void 26 request_config_set_defaults (const request_config *config_defaults) 27 { 28 request_config_defaults = config_defaults; 29 } 30 31 32 __attribute_noinline__ 33 void 34 request_config_reset (request_st * const r) 35 { 36 memcpy(&r->conf, request_config_defaults, sizeof(request_config)); 37 } 38 39 40 void 41 request_init_data (request_st * const r, connection * const con, server * const srv) 42 { 43 chunkqueue_init(&r->write_queue); 44 chunkqueue_init(&r->read_queue); 45 chunkqueue_init(&r->reqbody_queue); 46 47 r->http_method = HTTP_METHOD_UNSET; 48 r->http_version = HTTP_VERSION_UNSET; 49 r->resp_header_len = 0; 50 r->loops_per_request = 0; 51 r->con = con; 52 r->tmp_buf = srv->tmp_buf; 53 r->resp_body_scratchpad = -1; 54 r->server_name = &r->uri.authority; 55 56 /* init plugin-specific per-request structures */ 57 r->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *)); 58 force_assert(NULL != r->plugin_ctx); 59 60 r->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); 61 force_assert(NULL != r->cond_cache); 62 63 #ifdef HAVE_PCRE_H 64 if (srv->config_context->used > 1) {/*(save 128b per con if no conditions)*/ 65 r->cond_match = 66 calloc(srv->config_context->used, sizeof(cond_match_t)); 67 force_assert(NULL != r->cond_match); 68 } 69 #endif 70 71 request_config_reset(r); 72 } 73 74 75 void 76 request_reset (request_st * const r) 77 { 78 plugins_call_handle_request_reset(r); 79 80 http_response_reset(r); 81 82 r->loops_per_request = 0; 83 r->keep_alive = 0; 84 85 r->h2state = 0; /* H2_STATE_IDLE */ 86 r->h2id = 0; 87 r->http_method = HTTP_METHOD_UNSET; 88 r->http_version = HTTP_VERSION_UNSET; 89 90 /*con->proto_default_port = 80;*//*set to default in connection_accepted()*/ 91 92 r->http_host = NULL; 93 r->reqbody_length = 0; 94 r->te_chunked = 0; 95 r->resp_body_scratchpad = -1; 96 r->rqst_htags = 0; 97 98 r->async_callback = 0; 99 r->error_handler_saved_status = 0; 100 /*r->error_handler_saved_method = HTTP_METHOD_UNSET;*/ 101 /*(error_handler_saved_method value is not valid 102 * unless error_handler_saved_status is set)*/ 103 104 buffer_clear(&r->uri.scheme); 105 106 if (r->rqst_header_len <= BUFFER_MAX_REUSE_SIZE) { 107 r->rqst_headers.used = 0; 108 /* (Note: total header size not recalculated on HANDLER_COMEBACK 109 * even if other request headers changed during processing) 110 * (While this might delay release of larger buffers, it is not 111 * expected to be the general case. For those systems where it 112 * is a typical case, the larger buffers are likely to be reused) */ 113 buffer_clear(&r->target); 114 buffer_clear(&r->pathinfo); 115 /*buffer_clear(&r->target_orig);*/ /* reset later; used by mod_status*/ 116 /*buffer_clear(&r->uri.path);*/ /* reset later; used by mod_status*/ 117 /*buffer_clear(&r->uri.query);*/ /* reset later; used by mod_status*/ 118 /*buffer_clear(&r->uri.authority);*//* reset later; used by mod_status*/ 119 /*buffer_clear(&r->server_name_buf);*//* reset when used */ 120 } 121 else { 122 buffer_reset(&r->target); 123 buffer_reset(&r->pathinfo); 124 /*buffer_reset(&r->target_orig);*/ /* reset later; used by mod_status*/ 125 /*buffer_reset(&r->uri.path);*/ /* reset later; used by mod_status*/ 126 /*buffer_reset(&r->uri.query);*/ /* reset later; used by mod_status*/ 127 /*buffer_clear(&r->uri.authority);*//* reset later; used by mod_status*/ 128 /*buffer_clear(&r->server_name_buf);*//* reset when used */ 129 array_reset_data_strings(&r->rqst_headers); 130 } 131 r->rqst_header_len = 0; 132 if (0 != r->env.used) 133 array_reset_data_strings(&r->env); 134 135 chunkqueue_reset(&r->reqbody_queue); 136 /* r->read_queue, r->write_queue are shared with con for HTTP/1.1 137 * but are different than con->read_queue, con->write_queue for HTTP/2 138 * For HTTP/1.1, when &r->read_queue == con->read_queue, r->read_queue 139 * is not cleared between requests since it might contain subsequent 140 * requests. (see also request_release()) */ 141 142 /* The cond_cache gets reset in response.c */ 143 /* config_cond_cache_reset(r); */ 144 145 request_config_reset(r); 146 } 147 148 149 #if 0 /* DEBUG_DEV */ 150 __attribute_cold__ 151 static void request_plugin_ctx_check(request_st * const r, server * const srv) { 152 /* plugins should have cleaned themselves up */ 153 for (uint32_t i = 0, used = srv->plugins.used; i < used; ++i) { 154 plugin *p = ((plugin **)(srv->plugins.ptr))[i]; 155 plugin_data_base *pd = p->data; 156 if (!pd) continue; 157 if (NULL == r->plugin_ctx[pd->id] 158 && NULL == r->con->plugin_ctx[pd->id]) continue; 159 log_error(r->conf.errh, __FILE__, __LINE__, 160 "missing cleanup in %s", p->name); 161 r->plugin_ctx[pd->id] = NULL; 162 r->con->plugin_ctx[pd->id] = NULL; 163 } 164 } 165 #endif 166 167 168 void 169 request_reset_ex (request_st * const r) 170 { 171 #if 0 /* DEBUG_DEV */ 172 /* plugins should have cleaned themselves up (id range: [1,used]) */ 173 connection * const con = r->con; 174 server * const srv = con->srv; 175 for (uint32_t i = 1; i <= srv->plugins.used; ++i) { 176 if (NULL != r->plugin_ctx[i] || NULL != con->plugin_ctx[i]) { 177 request_plugin_ctx_check(r, srv); 178 break; 179 } 180 } 181 #endif 182 183 r->server_name = &r->uri.authority; 184 buffer_clear(&r->uri.authority); 185 buffer_reset(&r->uri.path); 186 buffer_reset(&r->uri.query); 187 buffer_reset(&r->physical.path); 188 buffer_reset(&r->physical.rel_path); 189 buffer_reset(&r->target_orig); 190 buffer_reset(&r->target); /*(see comments in request_reset())*/ 191 buffer_reset(&r->pathinfo); /*(see comments in request_reset())*/ 192 193 /* preserve; callers must handle changes */ 194 /*r->state = CON_STATE_CONNECT;*/ 195 } 196 197 198 void 199 request_free_data (request_st * const r) 200 { 201 chunkqueue_reset(&r->reqbody_queue); 202 chunkqueue_reset(&r->write_queue); 203 chunkqueue_reset(&r->read_queue); 204 array_free_data(&r->rqst_headers); 205 array_free_data(&r->resp_headers); 206 array_free_data(&r->env); 207 208 free(r->target.ptr); 209 free(r->target_orig.ptr); 210 211 free(r->uri.scheme.ptr); 212 free(r->uri.authority.ptr); 213 free(r->uri.path.ptr); 214 free(r->uri.query.ptr); 215 216 free(r->physical.doc_root.ptr); 217 free(r->physical.path.ptr); 218 free(r->physical.basedir.ptr); 219 free(r->physical.etag.ptr); 220 free(r->physical.rel_path.ptr); 221 222 free(r->pathinfo.ptr); 223 free(r->server_name_buf.ptr); 224 225 free(r->plugin_ctx); 226 free(r->cond_cache); 227 free(r->cond_match); 228 229 /* note: r is not zeroed here and r is not freed here */ 230 } 231 232 233 /* linked list of (request_st *) cached for reuse */ 234 static request_st *reqpool; 235 /* max num of (request_st *) to cache */ 236 static uint32_t reqspace; 237 238 239 void 240 request_pool_extend (server *srv, const uint32_t sz) 241 { 242 for (uint32_t i = 0; i < sz; ++i) { 243 request_st * const x = calloc(1, sizeof(request_st)); 244 force_assert(x); 245 request_init_data(x, NULL, srv); 246 x->con = (connection *)reqpool; /*(reuse r->con as next ptr)*/ 247 reqpool = x; 248 } 249 } 250 251 252 void 253 request_pool_free (void) 254 { 255 while (reqpool) { 256 request_st * const r = reqpool; 257 reqpool = (request_st *)r->con; /*(reuse r->con as next ptr)*/ 258 request_free_data(r); 259 free(r); 260 ++reqspace; 261 } 262 } 263 264 265 void 266 request_release (request_st * const r) 267 { 268 /* (For HTTP/1.1, r == &con->request, and so request_release() not called) 269 * r->read_queue, r->write_queue are shared with con for HTTP/1.1 270 * but are different than con->read_queue, con->write_queue for HTTP/2 271 * For HTTP/1.1, when &r->read_queue == con->read_queue, r->read_queue 272 * is not cleared between requests since it might contain subsequent 273 * requests. (see also request_reset()) */ 274 chunkqueue_reset(&r->read_queue); 275 276 /*(r->cond_cache and r->cond_match are re-init in h2_init_stream())*/ 277 278 request_reset(r); 279 request_reset_ex(r); 280 r->state = CON_STATE_CONNECT; 281 282 if (reqspace) { 283 --reqspace; 284 r->con = (connection *)reqpool; /*(reuse r->con as next ptr)*/ 285 reqpool = r; 286 } 287 else { 288 request_free_data(r); 289 free(r); 290 } 291 } 292 293 294 request_st * 295 request_acquire (connection * const con) 296 { 297 request_st *r = reqpool; 298 if (r) { 299 reqpool = (request_st *)r->con; /*(reuse r->con as next ptr)*/ 300 ++reqspace; 301 } 302 else { 303 r = calloc(1, sizeof(request_st)); 304 force_assert(r); 305 request_init_data(r, con, con->srv); 306 } 307 308 r->con = con; 309 r->tmp_buf = con->srv->tmp_buf; 310 return r; 311 } 312