"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "h2_request.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
1.15.16_vs_1.15.17.
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <assert.h>
18
19 #include <apr_strings.h>
20 #include <ap_mmn.h>
21
22 #include <httpd.h>
23 #include <http_core.h>
24 #include <http_connection.h>
25 #include <http_protocol.h>
26 #include <http_request.h>
27 #include <http_log.h>
28 #include <http_vhost.h>
29 #include <util_filter.h>
30 #include <ap_mpm.h>
31 #include <mod_core.h>
32 #include <scoreboard.h>
33
34 #include "h2_private.h"
35 #include "h2_config.h"
36 #include "h2_push.h"
37 #include "h2_request.h"
38 #include "h2_util.h"
39
40
41 typedef struct {
42 apr_table_t *headers;
43 apr_pool_t *pool;
44 apr_status_t status;
45 } h1_ctx;
46
47 static int set_h1_header(void *ctx, const char *key, const char *value)
48 {
49 h1_ctx *x = ctx;
50 int was_added;
51 h2_req_add_header(x->headers, x->pool, key, strlen(key), value, strlen(value), 0, &was_added);
52 return 1;
53 }
54
55 apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool,
56 request_rec *r)
57 {
58 h2_request *req;
59 const char *scheme, *authority, *path;
60 h1_ctx x;
61
62 *preq = NULL;
63 scheme = apr_pstrdup(pool, r->parsed_uri.scheme? r->parsed_uri.scheme
64 : ap_http_scheme(r));
65 authority = apr_pstrdup(pool, r->hostname);
66 path = apr_uri_unparse(pool, &r->parsed_uri, APR_URI_UNP_OMITSITEPART);
67
68 if (!r->method || !scheme || !r->hostname || !path) {
69 return APR_EINVAL;
70 }
71
72 if (!ap_strchr_c(authority, ':') && r->server && r->server->port) {
73 apr_port_t defport = apr_uri_port_of_scheme(scheme);
74 if (defport != r->server->port) {
75 /* port info missing and port is not default for scheme: append */
76 authority = apr_psprintf(pool, "%s:%d", authority,
77 (int)r->server->port);
78 }
79 }
80
81 req = apr_pcalloc(pool, sizeof(*req));
82 req->method = apr_pstrdup(pool, r->method);
83 req->scheme = scheme;
84 req->authority = authority;
85 req->path = path;
86 req->headers = apr_table_make(pool, 10);
87 req->http_status = H2_HTTP_STATUS_UNSET;
88 if (r->server) {
89 req->serialize = h2_config_rgeti(r, H2_CONF_SER_HEADERS);
90 }
91
92 x.pool = pool;
93 x.headers = req->headers;
94 x.status = APR_SUCCESS;
95 apr_table_do(set_h1_header, &x, r->headers_in, NULL);
96
97 *preq = req;
98 return x.status;
99 }
100
101 apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
102 const char *name, size_t nlen,
103 const char *value, size_t vlen,
104 size_t max_field_len, int *pwas_added)
105 {
106 apr_status_t status = APR_SUCCESS;
107
108 *pwas_added = 0;
109 if (nlen <= 0) {
110 return status;
111 }
112
113 if (name[0] == ':') {
114 /* pseudo header, see ch. 8.1.2.3, always should come first */
115 if (!apr_is_empty_table(req->headers)) {
116 ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool,
117 APLOGNO(02917)
118 "h2_request: pseudo header after request start");
119 return APR_EGENERAL;
120 }
121
122 if (H2_HEADER_METHOD_LEN == nlen
123 && !strncmp(H2_HEADER_METHOD, name, nlen)) {
124 req->method = apr_pstrndup(pool, value, vlen);
125 }
126 else if (H2_HEADER_SCHEME_LEN == nlen
127 && !strncmp(H2_HEADER_SCHEME, name, nlen)) {
128 req->scheme = apr_pstrndup(pool, value, vlen);
129 }
130 else if (H2_HEADER_PATH_LEN == nlen
131 && !strncmp(H2_HEADER_PATH, name, nlen)) {
132 req->path = apr_pstrndup(pool, value, vlen);
133 }
134 else if (H2_HEADER_AUTH_LEN == nlen
135 && !strncmp(H2_HEADER_AUTH, name, nlen)) {
136 req->authority = apr_pstrndup(pool, value, vlen);
137 }
138 else {
139 char buffer[32];
140 memset(buffer, 0, 32);
141 strncpy(buffer, name, (nlen > 31)? 31 : nlen);
142 ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, pool,
143 APLOGNO(02954)
144 "h2_request: ignoring unknown pseudo header %s",
145 buffer);
146 }
147 }
148 else {
149 /* non-pseudo header, add to table */
150 status = h2_req_add_header(req->headers, pool, name, nlen, value, vlen,
151 max_field_len, pwas_added);
152 }
153
154 return status;
155 }
156
157 apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos, size_t raw_bytes)
158 {
159 const char *s;
160
161 /* rfc7540, ch. 8.1.2.3:
162 * - if we have :authority, it overrides any Host header
163 * - :authority MUST be omitted when converting h1->h2, so we
164 * might get a stream without, but then Host needs to be there */
165 if (!req->authority) {
166 const char *host = apr_table_get(req->headers, "Host");
167 if (!host) {
168 return APR_BADARG;
169 }
170 req->authority = host;
171 }
172 else {
173 apr_table_setn(req->headers, "Host", req->authority);
174 }
175
176 s = apr_table_get(req->headers, "Content-Length");
177 if (!s) {
178 /* HTTP/2 does not need a Content-Length for framing, but our
179 * internal request processing is used to HTTP/1.1, so we
180 * need to either add a Content-Length or a Transfer-Encoding
181 * if any content can be expected. */
182 if (!eos) {
183 /* We have not seen a content-length and have no eos,
184 * simulate a chunked encoding for our HTTP/1.1 infrastructure,
185 * in case we have "H2SerializeHeaders on" here
186 */
187 req->chunked = 1;
188 apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
189 }
190 else if (apr_table_get(req->headers, "Content-Type")) {
191 /* If we have a content-type, but already seen eos, no more
192 * data will come. Signal a zero content length explicitly.
193 */
194 apr_table_setn(req->headers, "Content-Length", "0");
195 }
196 }
197 req->raw_bytes += raw_bytes;
198
199 return APR_SUCCESS;
200 }
201
202 h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src)
203 {
204 h2_request *dst = apr_pmemdup(p, src, sizeof(*dst));
205 dst->method = apr_pstrdup(p, src->method);
206 dst->scheme = apr_pstrdup(p, src->scheme);
207 dst->authority = apr_pstrdup(p, src->authority);
208 dst->path = apr_pstrdup(p, src->path);
209 dst->headers = apr_table_clone(p, src->headers);
210 return dst;
211 }
212
213 #if !AP_MODULE_MAGIC_AT_LEAST(20150222, 13)
214 static request_rec *my_ap_create_request(conn_rec *c)
215 {
216 apr_pool_t *p;
217 request_rec *r;
218
219 apr_pool_create(&p, c->pool);
220 apr_pool_tag(p, "request");
221 r = apr_pcalloc(p, sizeof(request_rec));
222 AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)c);
223 r->pool = p;
224 r->connection = c;
225 r->server = c->base_server;
226
227 r->user = NULL;
228 r->ap_auth_type = NULL;
229
230 r->allowed_methods = ap_make_method_list(p, 2);
231
232 r->headers_in = apr_table_make(r->pool, 5);
233 r->trailers_in = apr_table_make(r->pool, 5);
234 r->subprocess_env = apr_table_make(r->pool, 25);
235 r->headers_out = apr_table_make(r->pool, 12);
236 r->err_headers_out = apr_table_make(r->pool, 5);
237 r->trailers_out = apr_table_make(r->pool, 5);
238 r->notes = apr_table_make(r->pool, 5);
239
240 r->request_config = ap_create_request_config(r->pool);
241 /* Must be set before we run create request hook */
242
243 r->proto_output_filters = c->output_filters;
244 r->output_filters = r->proto_output_filters;
245 r->proto_input_filters = c->input_filters;
246 r->input_filters = r->proto_input_filters;
247 ap_run_create_request(r);
248 r->per_dir_config = r->server->lookup_defaults;
249
250 r->sent_bodyct = 0; /* bytect isn't for body */
251
252 r->read_length = 0;
253 r->read_body = REQUEST_NO_BODY;
254
255 r->status = HTTP_OK; /* Until further notice */
256 r->header_only = 0;
257 r->the_request = NULL;
258
259 /* Begin by presuming any module can make its own path_info assumptions,
260 * until some module interjects and changes the value.
261 */
262 r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
263
264 r->useragent_addr = c->client_addr;
265 r->useragent_ip = c->client_ip;
266
267 return r;
268 }
269 #endif
270
271 request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
272 {
273 int access_status;
274
275 #if AP_MODULE_MAGIC_AT_LEAST(20150222, 13)
276 request_rec *r = ap_create_request(c);
277 #else
278 request_rec *r = my_ap_create_request(c);
279 #endif
280
281 #if AP_MODULE_MAGIC_AT_LEAST(20200331, 3)
282 ap_run_pre_read_request(r, c);
283
284 /* Time to populate r with the data we have. */
285 r->request_time = req->request_time;
286 r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0",
287 req->method, req->path ? req->path : "");
288 r->headers_in = apr_table_clone(r->pool, req->headers);
289
290 /* Start with r->hostname = NULL, ap_check_request_header() will get it
291 * form Host: header, otherwise we get complains about port numbers.
292 */
293 r->hostname = NULL;
294
295 /* Validate HTTP/1 request and select vhost. */
296 if (!ap_parse_request_line(r) || !ap_check_request_header(r)) {
297 /* we may have switched to another server still */
298 r->per_dir_config = r->server->lookup_defaults;
299 if (req->http_status != H2_HTTP_STATUS_UNSET) {
300 access_status = req->http_status;
301 /* Be safe and close the connection */
302 c->keepalive = AP_CONN_CLOSE;
303 }
304 else {
305 access_status = r->status;
306 }
307 r->status = HTTP_OK;
308 goto die;
309 }
310 #else
311 {
312 const char *s;
313
314 r->headers_in = apr_table_clone(r->pool, req->headers);
315 ap_run_pre_read_request(r, c);
316
317 /* Time to populate r with the data we have. */
318 r->request_time = req->request_time;
319 r->method = apr_pstrdup(r->pool, req->method);
320 /* Provide quick information about the request method as soon as known */
321 r->method_number = ap_method_number_of(r->method);
322 if (r->method_number == M_GET && r->method[0] == 'H') {
323 r->header_only = 1;
324 }
325 ap_parse_uri(r, req->path ? req->path : "");
326 r->protocol = (char*)"HTTP/2.0";
327 r->proto_num = HTTP_VERSION(2, 0);
328 r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0",
329 r->method, req->path ? req->path : "");
330
331 /* Start with r->hostname = NULL, ap_check_request_header() will get it
332 * form Host: header, otherwise we get complains about port numbers.
333 */
334 r->hostname = NULL;
335 ap_update_vhost_from_headers(r);
336
337 /* we may have switched to another server */
338 r->per_dir_config = r->server->lookup_defaults;
339
340 s = apr_table_get(r->headers_in, "Expect");
341 if (s && s[0]) {
342 if (ap_cstr_casecmp(s, "100-continue") == 0) {
343 r->expecting_100 = 1;
344 }
345 else {
346 r->status = HTTP_EXPECTATION_FAILED;
347 access_status = r->status;
348 goto die;
349 }
350 }
351 }
352 #endif
353
354 /* we may have switched to another server */
355 r->per_dir_config = r->server->lookup_defaults;
356
357 if (req->http_status != H2_HTTP_STATUS_UNSET) {
358 access_status = req->http_status;
359 r->status = HTTP_OK;
360 /* Be safe and close the connection */
361 c->keepalive = AP_CONN_CLOSE;
362 goto die;
363 }
364
365 /*
366 * Add the HTTP_IN filter here to ensure that ap_discard_request_body
367 * called by ap_die and by ap_send_error_response works correctly on
368 * status codes that do not cause the connection to be dropped and
369 * in situations where the connection should be kept alive.
370 */
371 ap_add_input_filter_handle(ap_http_input_filter_handle,
372 NULL, r, r->connection);
373
374 if ((access_status = ap_run_post_read_request(r))) {
375 /* Request check post hooks failed. An example of this would be a
376 * request for a vhost where h2 is disabled --> 421.
377 */
378 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03367)
379 "h2_request: access_status=%d, request_create failed",
380 access_status);
381 goto die;
382 }
383
384 AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method,
385 (char *)r->uri, (char *)r->server->defn_name,
386 r->status);
387 return r;
388
389 die:
390 ap_die(access_status, r);
391
392 /* ap_die() sent the response through the output filters, we must now
393 * end the request with an EOR bucket for stream/pipeline accounting.
394 */
395 {
396 apr_bucket_brigade *eor_bb;
397 #if AP_MODULE_MAGIC_AT_LEAST(20180905, 1)
398 eor_bb = ap_acquire_brigade(c);
399 APR_BRIGADE_INSERT_TAIL(eor_bb,
400 ap_bucket_eor_create(c->bucket_alloc, r));
401 ap_pass_brigade(c->output_filters, eor_bb);
402 ap_release_brigade(c, eor_bb);
403 #else
404 eor_bb = apr_brigade_create(c->pool, c->bucket_alloc);
405 APR_BRIGADE_INSERT_TAIL(eor_bb,
406 ap_bucket_eor_create(c->bucket_alloc, r));
407 ap_pass_brigade(c->output_filters, eor_bb);
408 apr_brigade_destroy(eor_bb);
409 #endif
410 }
411
412 r = NULL;
413 AP_READ_REQUEST_FAILURE((uintptr_t)r);
414 return NULL;
415 }
416
417
418