1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 #include <apr_strings.h>
21 #include <http_core.h>
22 #include <http_connection.h>
23 #include <http_protocol.h>
24 #include <http_request.h>
26 #include <http_vhost.h>
27 #include <util_filter.h>
30 #include <scoreboard.h>
32 #include "h2_private.h"
33 #include "h2_config.h"
35 #include "h2_request.h"
40 h2_request *h2_request_create(int id, apr_pool_t *pool,
41 const struct h2_config *config)
43 return h2_request_createn(id, pool, config,
44 NULL, NULL, NULL, NULL, NULL);
47 h2_request *h2_request_createn(int id, apr_pool_t *pool,
48 const struct h2_config *config,
49 const char *method, const char *scheme,
50 const char *authority, const char *path,
53 h2_request *req = apr_pcalloc(pool, sizeof(h2_request));
59 req->authority = authority;
61 req->headers = header? header : apr_table_make(pool, 10);
62 req->request_time = apr_time_now();
67 void h2_request_destroy(h2_request *req)
71 static apr_status_t inspect_clen(h2_request *req, const char *s)
74 req->content_length = apr_strtoi64(s, &end, 10);
75 return (s == end)? APR_EINVAL : APR_SUCCESS;
78 static apr_status_t add_h1_header(h2_request *req, apr_pool_t *pool,
79 const char *name, size_t nlen,
80 const char *value, size_t vlen)
84 if (h2_req_ignore_header(name, nlen)) {
87 else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
88 const char *existing = apr_table_get(req->headers, "cookie");
92 /* Cookie header come separately in HTTP/2, but need
93 * to be merged by "; " (instead of default ", ")
95 hvalue = apr_pstrndup(pool, value, vlen);
96 nval = apr_psprintf(pool, "%s; %s", existing, hvalue);
97 apr_table_setn(req->headers, "Cookie", nval);
101 else if (H2_HD_MATCH_LIT("host", name, nlen)) {
102 if (apr_table_get(req->headers, "Host")) {
103 return APR_SUCCESS; /* ignore duplicate */
107 hname = apr_pstrndup(pool, name, nlen);
108 hvalue = apr_pstrndup(pool, value, vlen);
109 h2_util_camel_case_header(hname, nlen);
110 apr_table_mergen(req->headers, hname, hvalue);
120 static int set_h1_header(void *ctx, const char *key, const char *value)
123 size_t klen = strlen(key);
124 if (!h2_req_ignore_header(key, klen)) {
125 add_h1_header(x->req, x->pool, key, klen, value, strlen(value));
130 static apr_status_t add_all_h1_header(h2_request *req, apr_pool_t *pool,
136 apr_table_do(set_h1_header, &x, header, NULL);
141 apr_status_t h2_request_rwrite(h2_request *req, request_rec *r)
145 req->config = h2_config_rget(r);
146 req->method = r->method;
147 req->scheme = (r->parsed_uri.scheme? r->parsed_uri.scheme
148 : ap_http_scheme(r));
149 req->authority = r->hostname;
150 req->path = apr_uri_unparse(r->pool, &r->parsed_uri,
151 APR_URI_UNP_OMITSITEPART);
153 if (!ap_strchr_c(req->authority, ':') && r->server && r->server->port) {
154 apr_port_t defport = apr_uri_port_of_scheme(req->scheme);
155 if (defport != r->server->port) {
156 /* port info missing and port is not default for scheme: append */
157 req->authority = apr_psprintf(r->pool, "%s:%d", req->authority,
158 (int)r->server->port);
162 AP_DEBUG_ASSERT(req->scheme);
163 AP_DEBUG_ASSERT(req->authority);
164 AP_DEBUG_ASSERT(req->path);
165 AP_DEBUG_ASSERT(req->method);
167 status = add_all_h1_header(req, r->pool, r->headers_in);
169 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
170 "h2_request(%d): rwrite %s host=%s://%s%s",
171 req->id, req->method, req->scheme, req->authority, req->path);
176 apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
177 const char *name, size_t nlen,
178 const char *value, size_t vlen)
180 apr_status_t status = APR_SUCCESS;
186 if (name[0] == ':') {
187 /* pseudo header, see ch. 8.1.2.3, always should come first */
188 if (!apr_is_empty_table(req->headers)) {
189 ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool,
191 "h2_request(%d): pseudo header after request start",
196 if (H2_HEADER_METHOD_LEN == nlen
197 && !strncmp(H2_HEADER_METHOD, name, nlen)) {
198 req->method = apr_pstrndup(pool, value, vlen);
200 else if (H2_HEADER_SCHEME_LEN == nlen
201 && !strncmp(H2_HEADER_SCHEME, name, nlen)) {
202 req->scheme = apr_pstrndup(pool, value, vlen);
204 else if (H2_HEADER_PATH_LEN == nlen
205 && !strncmp(H2_HEADER_PATH, name, nlen)) {
206 req->path = apr_pstrndup(pool, value, vlen);
208 else if (H2_HEADER_AUTH_LEN == nlen
209 && !strncmp(H2_HEADER_AUTH, name, nlen)) {
210 req->authority = apr_pstrndup(pool, value, vlen);
214 memset(buffer, 0, 32);
215 strncpy(buffer, name, (nlen > 31)? 31 : nlen);
216 ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, pool,
218 "h2_request(%d): ignoring unknown pseudo header %s",
223 /* non-pseudo header, append to work bucket of stream */
224 status = add_h1_header(req, pool, name, nlen, value, vlen);
230 apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
238 /* Always set the "Host" header from :authority, see rfc7540, ch. 8.1.2.3 */
239 if (!req->authority) {
242 apr_table_setn(req->headers, "Host", req->authority);
244 s = apr_table_get(req->headers, "Content-Length");
246 if (inspect_clen(req, s) != APR_SUCCESS) {
247 ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
249 "h2_request(%d): content-length value not parsed: %s",
255 /* no content-length given */
256 req->content_length = -1;
258 /* We have not seen a content-length and have no eos,
259 * simulate a chunked encoding for our HTTP/1.1 infrastructure,
260 * in case we have "H2SerializeHeaders on" here
263 apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
265 else if (apr_table_get(req->headers, "Content-Type")) {
266 /* If we have a content-type, but already see eos, no more
267 * data will come. Signal a zero content length explicitly.
269 apr_table_setn(req->headers, "Content-Length", "0");
275 /* In the presence of trailers, force behaviour of chunked encoding */
276 s = apr_table_get(req->headers, "Trailer");
278 req->trailers = apr_table_make(pool, 5);
281 apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
288 static apr_status_t add_h1_trailer(h2_request *req, apr_pool_t *pool,
289 const char *name, size_t nlen,
290 const char *value, size_t vlen)
292 char *hname, *hvalue;
294 if (h2_req_ignore_trailer(name, nlen)) {
298 hname = apr_pstrndup(pool, name, nlen);
299 hvalue = apr_pstrndup(pool, value, vlen);
300 h2_util_camel_case_header(hname, nlen);
302 apr_table_mergen(req->trailers, hname, hvalue);
308 apr_status_t h2_request_add_trailer(h2_request *req, apr_pool_t *pool,
309 const char *name, size_t nlen,
310 const char *value, size_t vlen)
312 if (!req->trailers) {
313 ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool,
314 "h2_request(%d): unanounced trailers",
318 if (nlen == 0 || name[0] == ':') {
319 ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool,
320 "h2_request(%d): pseudo header in trailer",
324 return add_h1_trailer(req, pool, name, nlen, value, vlen);
327 #define OPT_COPY(p, s) ((s)? apr_pstrdup(p, s) : NULL)
329 void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src)
331 /* keep the dst id */
332 dst->method = OPT_COPY(p, src->method);
333 dst->scheme = OPT_COPY(p, src->scheme);
334 dst->authority = OPT_COPY(p, src->authority);
335 dst->path = OPT_COPY(p, src->path);
336 dst->headers = apr_table_clone(p, src->headers);
337 dst->content_length = src->content_length;
338 dst->chunked = src->chunked;
342 request_rec *h2_request_create_rec(const h2_request *req, conn_rec *conn)
346 int access_status = HTTP_OK;
348 apr_pool_create(&p, conn->pool);
349 apr_pool_tag(p, "request");
350 r = apr_pcalloc(p, sizeof(request_rec));
351 AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn);
353 r->connection = conn;
354 r->server = conn->base_server;
357 r->ap_auth_type = NULL;
359 r->allowed_methods = ap_make_method_list(p, 2);
361 r->headers_in = apr_table_copy(r->pool, req->headers);
362 r->trailers_in = apr_table_make(r->pool, 5);
363 r->subprocess_env = apr_table_make(r->pool, 25);
364 r->headers_out = apr_table_make(r->pool, 12);
365 r->err_headers_out = apr_table_make(r->pool, 5);
366 r->trailers_out = apr_table_make(r->pool, 5);
367 r->notes = apr_table_make(r->pool, 5);
369 r->request_config = ap_create_request_config(r->pool);
370 /* Must be set before we run create request hook */
372 r->proto_output_filters = conn->output_filters;
373 r->output_filters = r->proto_output_filters;
374 r->proto_input_filters = conn->input_filters;
375 r->input_filters = r->proto_input_filters;
376 ap_run_create_request(r);
377 r->per_dir_config = r->server->lookup_defaults;
379 r->sent_bodyct = 0; /* bytect isn't for body */
382 r->read_body = REQUEST_NO_BODY;
384 r->status = HTTP_OK; /* Until further notice */
386 r->the_request = NULL;
388 /* Begin by presuming any module can make its own path_info assumptions,
389 * until some module interjects and changes the value.
391 r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
393 r->useragent_addr = conn->client_addr;
394 r->useragent_ip = conn->client_ip;
396 ap_run_pre_read_request(r, conn);
398 /* Time to populate r with the data we have. */
399 r->request_time = req->request_time;
400 r->method = req->method;
401 /* Provide quick information about the request method as soon as known */
402 r->method_number = ap_method_number_of(r->method);
403 if (r->method_number == M_GET && r->method[0] == 'H') {
407 ap_parse_uri(r, req->path);
408 r->protocol = (char*)"HTTP/2";
409 r->proto_num = HTTP_VERSION(2, 0);
411 r->the_request = apr_psprintf(r->pool, "%s %s %s",
412 r->method, req->path, r->protocol);
414 /* update what we think the virtual host is based on the headers we've
415 * now read. may update status.
416 * Leave r->hostname empty, vhost will parse if form our Host: header,
417 * otherwise we get complains about port numbers.
420 ap_update_vhost_from_headers(r);
422 /* we may have switched to another server */
423 r->per_dir_config = r->server->lookup_defaults;
426 * Add the HTTP_IN filter here to ensure that ap_discard_request_body
427 * called by ap_die and by ap_send_error_response works correctly on
428 * status codes that do not cause the connection to be dropped and
429 * in situations where the connection should be kept alive.
431 ap_add_input_filter_handle(ap_http_input_filter_handle,
432 NULL, r, r->connection);
434 if (access_status != HTTP_OK
435 || (access_status = ap_run_post_read_request(r))) {
436 /* Request check post hooks failed. An example of this would be a
437 * request for a vhost where h2 is disabled --> 421.
439 ap_die(access_status, r);
440 ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
441 ap_run_log_transaction(r);
446 AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method,
447 (char *)r->uri, (char *)r->server->defn_name,
451 AP_READ_REQUEST_FAILURE((uintptr_t)r);