xref: /lighttpd1.4/src/reqpool.c (revision 5bc92071)
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