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