]> granicus.if.org Git - apache/blob - modules/http2/h2_request.c
fixing default port handling in authority composition for upgraded requests
[apache] / modules / http2 / h2_request.c
1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
2  *
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
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  
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.
14  */
15
16 #include <assert.h>
17
18 #include <apr_strings.h>
19
20 #include <httpd.h>
21 #include <http_core.h>
22 #include <http_connection.h>
23 #include <http_protocol.h>
24 #include <http_request.h>
25 #include <http_log.h>
26 #include <http_vhost.h>
27 #include <util_filter.h>
28 #include <ap_mpm.h>
29 #include <mod_core.h>
30 #include <scoreboard.h>
31
32 #include "h2_private.h"
33 #include "h2_config.h"
34 #include "h2_mplx.h"
35 #include "h2_request.h"
36 #include "h2_task.h"
37 #include "h2_util.h"
38
39
40 h2_request *h2_request_create(int id, apr_pool_t *pool,
41                               const struct h2_config *config)
42 {
43     return h2_request_createn(id, pool, config, 
44                               NULL, NULL, NULL, NULL, NULL);
45 }
46
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,
51                                apr_table_t *header)
52 {
53     h2_request *req = apr_pcalloc(pool, sizeof(h2_request));
54     
55     req->id             = id;
56     req->config         = config;
57     req->method         = method;
58     req->scheme         = scheme;
59     req->authority      = authority;
60     req->path           = path;
61     req->headers        = header? header : apr_table_make(pool, 10);
62     req->request_time   = apr_time_now();
63
64     return req;
65 }
66
67 void h2_request_destroy(h2_request *req)
68 {
69 }
70
71 static apr_status_t inspect_clen(h2_request *req, const char *s)
72 {
73     char *end;
74     req->content_length = apr_strtoi64(s, &end, 10);
75     return (s == end)? APR_EINVAL : APR_SUCCESS;
76 }
77
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)
81 {
82     char *hname, *hvalue;
83     
84     if (h2_req_ignore_header(name, nlen)) {
85         return APR_SUCCESS;
86     }
87     else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
88         const char *existing = apr_table_get(req->headers, "cookie");
89         if (existing) {
90             char *nval;
91             
92             /* Cookie header come separately in HTTP/2, but need
93              * to be merged by "; " (instead of default ", ")
94              */
95             hvalue = apr_pstrndup(pool, value, vlen);
96             nval = apr_psprintf(pool, "%s; %s", existing, hvalue);
97             apr_table_setn(req->headers, "Cookie", nval);
98             return APR_SUCCESS;
99         }
100     }
101     else if (H2_HD_MATCH_LIT("host", name, nlen)) {
102         if (apr_table_get(req->headers, "Host")) {
103             return APR_SUCCESS; /* ignore duplicate */
104         }
105     }
106     
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);
111     
112     return APR_SUCCESS;
113 }
114
115 typedef struct {
116     h2_request *req;
117     apr_pool_t *pool;
118 } h1_ctx;
119
120 static int set_h1_header(void *ctx, const char *key, const char *value)
121 {
122     h1_ctx *x = ctx;
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));
126     }
127     return 1;
128 }
129
130 static apr_status_t add_all_h1_header(h2_request *req, apr_pool_t *pool, 
131                                       apr_table_t *header)
132 {
133     h1_ctx x;
134     x.req = req;
135     x.pool = pool;
136     apr_table_do(set_h1_header, &x, header, NULL);
137     return APR_SUCCESS;
138 }
139
140
141 apr_status_t h2_request_rwrite(h2_request *req, request_rec *r)
142 {
143     apr_status_t status;
144     
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);
152
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);
159         }
160     }
161     
162     AP_DEBUG_ASSERT(req->scheme);
163     AP_DEBUG_ASSERT(req->authority);
164     AP_DEBUG_ASSERT(req->path);
165     AP_DEBUG_ASSERT(req->method);
166
167     status = add_all_h1_header(req, r->pool, r->headers_in);
168
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);
172                   
173     return status;
174 }
175
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)
179 {
180     apr_status_t status = APR_SUCCESS;
181     
182     if (nlen <= 0) {
183         return status;
184     }
185     
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,
190                           APLOGNO(02917) 
191                           "h2_request(%d): pseudo header after request start",
192                           req->id);
193             return APR_EGENERAL;
194         }
195         
196         if (H2_HEADER_METHOD_LEN == nlen
197             && !strncmp(H2_HEADER_METHOD, name, nlen)) {
198             req->method = apr_pstrndup(pool, value, vlen);
199         }
200         else if (H2_HEADER_SCHEME_LEN == nlen
201                  && !strncmp(H2_HEADER_SCHEME, name, nlen)) {
202             req->scheme = apr_pstrndup(pool, value, vlen);
203         }
204         else if (H2_HEADER_PATH_LEN == nlen
205                  && !strncmp(H2_HEADER_PATH, name, nlen)) {
206             req->path = apr_pstrndup(pool, value, vlen);
207         }
208         else if (H2_HEADER_AUTH_LEN == nlen
209                  && !strncmp(H2_HEADER_AUTH, name, nlen)) {
210             req->authority = apr_pstrndup(pool, value, vlen);
211         }
212         else {
213             char buffer[32];
214             memset(buffer, 0, 32);
215             strncpy(buffer, name, (nlen > 31)? 31 : nlen);
216             ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, pool,
217                           APLOGNO(02954) 
218                           "h2_request(%d): ignoring unknown pseudo header %s",
219                           req->id, buffer);
220         }
221     }
222     else {
223         /* non-pseudo header, append to work bucket of stream */
224         status = add_h1_header(req, pool, name, nlen, value, vlen);
225     }
226     
227     return status;
228 }
229
230 apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
231 {
232     const char *s;
233     
234     if (req->eoh) {
235         return APR_EINVAL;
236     }
237
238     /* Always set the "Host" header from :authority, see rfc7540, ch. 8.1.2.3 */
239     if (!req->authority) {
240         return APR_BADARG;
241     }
242     apr_table_setn(req->headers, "Host", req->authority);
243
244     s = apr_table_get(req->headers, "Content-Length");
245     if (s) {
246         if (inspect_clen(req, s) != APR_SUCCESS) {
247             ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
248                           APLOGNO(02959) 
249                           "h2_request(%d): content-length value not parsed: %s",
250                           req->id, s);
251             return APR_EINVAL;
252         }
253     }
254     else {
255         /* no content-length given */
256         req->content_length = -1;
257         if (!eos) {
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
261              */
262             req->chunked = 1;
263             apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
264         }
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.
268              */
269             apr_table_setn(req->headers, "Content-Length", "0");
270         }
271     }
272
273     req->eoh = 1;
274     
275     /* In the presence of trailers, force behaviour of chunked encoding */
276     s = apr_table_get(req->headers, "Trailer");
277     if (s && s[0]) {
278         req->trailers = apr_table_make(pool, 5);
279         if (!req->chunked) {
280             req->chunked = 1;
281             apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
282         }
283     }
284     
285     return APR_SUCCESS;
286 }
287
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)
291 {
292     char *hname, *hvalue;
293     
294     if (h2_req_ignore_trailer(name, nlen)) {
295         return APR_SUCCESS;
296     }
297     
298     hname = apr_pstrndup(pool, name, nlen);
299     hvalue = apr_pstrndup(pool, value, vlen);
300     h2_util_camel_case_header(hname, nlen);
301
302     apr_table_mergen(req->trailers, hname, hvalue);
303     
304     return APR_SUCCESS;
305 }
306
307
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)
311 {
312     if (!req->trailers) {
313         ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool,
314                       "h2_request(%d): unanounced trailers",
315                       req->id);
316         return APR_EINVAL;
317     }
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",
321                       req->id);
322         return APR_EINVAL;
323     }
324     return add_h1_trailer(req, pool, name, nlen, value, vlen);
325 }
326
327 #define OPT_COPY(p, s)  ((s)? apr_pstrdup(p, s) : NULL)
328
329 void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src)
330 {
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;
339     dst->eoh            = src->eoh;
340 }
341
342 request_rec *h2_request_create_rec(const h2_request *req, conn_rec *conn)
343 {
344     request_rec *r;
345     apr_pool_t *p;
346     int access_status = HTTP_OK;    
347     
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);
352     r->pool            = p;
353     r->connection      = conn;
354     r->server          = conn->base_server;
355     
356     r->user            = NULL;
357     r->ap_auth_type    = NULL;
358     
359     r->allowed_methods = ap_make_method_list(p, 2);
360     
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);
368     
369     r->request_config  = ap_create_request_config(r->pool);
370     /* Must be set before we run create request hook */
371     
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;
378     
379     r->sent_bodyct     = 0;                      /* bytect isn't for body */
380     
381     r->read_length     = 0;
382     r->read_body       = REQUEST_NO_BODY;
383     
384     r->status          = HTTP_OK;  /* Until further notice */
385     r->header_only     = 0;
386     r->the_request     = NULL;
387     
388     /* Begin by presuming any module can make its own path_info assumptions,
389      * until some module interjects and changes the value.
390      */
391     r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
392     
393     r->useragent_addr = conn->client_addr;
394     r->useragent_ip = conn->client_ip;
395     
396     ap_run_pre_read_request(r, conn);
397     
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') {
404         r->header_only = 1;
405     }
406
407     ap_parse_uri(r, req->path);
408     r->protocol = (char*)"HTTP/2";
409     r->proto_num = HTTP_VERSION(2, 0);
410
411     r->the_request = apr_psprintf(r->pool, "%s %s %s", 
412                                   r->method, req->path, r->protocol);
413     
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.
418      */
419     r->hostname = NULL;
420     ap_update_vhost_from_headers(r);
421     
422     /* we may have switched to another server */
423     r->per_dir_config = r->server->lookup_defaults;
424     
425     /*
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.
430      */
431     ap_add_input_filter_handle(ap_http_input_filter_handle,
432                                NULL, r, r->connection);
433     
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.
438          */
439         ap_die(access_status, r);
440         ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
441         ap_run_log_transaction(r);
442         r = NULL;
443         goto traceout;
444     }
445     
446     AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, 
447                             (char *)r->uri, (char *)r->server->defn_name, 
448                             r->status);
449     return r;
450 traceout:
451     AP_READ_REQUEST_FAILURE((uintptr_t)r);
452     return r;
453 }
454
455