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