]> granicus.if.org Git - apache/blob - modules/http2/h2_switch.c
Merge r1818804, r1818951, r1818958, r1818960, r1819027, r1819214, r1820035 from trunk:
[apache] / modules / http2 / h2_switch.c
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 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
18  *
19  * Licensed under the Apache License, Version 2.0 (the "License");
20  * you may not use this file except in compliance with the License.
21  * You may obtain a copy of the License at
22  *
23  * http://www.apache.org/licenses/LICENSE-2.0
24  
25  * Unless required by applicable law or agreed to in writing, software
26  * distributed under the License is distributed on an "AS IS" BASIS,
27  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28  * See the License for the specific language governing permissions and
29  * limitations under the License.
30  */
31
32 #include <assert.h>
33
34 #include <apr_strings.h>
35 #include <apr_optional.h>
36 #include <apr_optional_hooks.h>
37
38 #include <httpd.h>
39 #include <http_core.h>
40 #include <http_config.h>
41 #include <http_connection.h>
42 #include <http_protocol.h>
43 #include <http_log.h>
44
45 #include "h2_private.h"
46
47 #include "h2_config.h"
48 #include "h2_ctx.h"
49 #include "h2_conn.h"
50 #include "h2_h2.h"
51 #include "h2_switch.h"
52
53 /*******************************************************************************
54  * Once per lifetime init, retrieve optional functions
55  */
56 apr_status_t h2_switch_init(apr_pool_t *pool, server_rec *s)
57 {
58     (void)pool;
59     ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "h2_switch init");
60
61     return APR_SUCCESS;
62 }
63
64 static int h2_protocol_propose(conn_rec *c, request_rec *r,
65                                server_rec *s,
66                                const apr_array_header_t *offers,
67                                apr_array_header_t *proposals)
68 {
69     int proposed = 0;
70     int is_tls = h2_h2_is_tls(c);
71     const char **protos = is_tls? h2_tls_protos : h2_clear_protos;
72     
73     (void)s;
74     if (!h2_mpm_supported()) {
75         return DECLINED;
76     }
77     
78     if (strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))) {
79         /* We do not know how to switch from anything else but http/1.1.
80          */
81         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03083)
82                       "protocol switch: current proto != http/1.1, declined");
83         return DECLINED;
84     }
85     
86     if (!h2_is_acceptable_connection(c, 0)) {
87         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03084)
88                       "protocol propose: connection requirements not met");
89         return DECLINED;
90     }
91     
92     if (r) {
93         /* So far, this indicates an HTTP/1 Upgrade header initiated
94          * protocol switch. For that, the HTTP2-Settings header needs
95          * to be present and valid for the connection.
96          */
97         const char *p;
98         
99         if (!h2_allows_h2_upgrade(c)) {
100             return DECLINED;
101         }
102          
103         p = apr_table_get(r->headers_in, "HTTP2-Settings");
104         if (!p) {
105             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03085)
106                           "upgrade without HTTP2-Settings declined");
107             return DECLINED;
108         }
109         
110         p = apr_table_get(r->headers_in, "Connection");
111         if (!ap_find_token(r->pool, p, "http2-settings")) {
112             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03086)
113                           "upgrade without HTTP2-Settings declined");
114             return DECLINED;
115         }
116         
117         /* We also allow switching only for requests that have no body.
118          */
119         p = apr_table_get(r->headers_in, "Content-Length");
120         if (p && strcmp(p, "0")) {
121             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03087)
122                           "upgrade with content-length: %s, declined", p);
123             return DECLINED;
124         }
125     }
126     
127     while (*protos) {
128         /* Add all protocols we know (tls or clear) and that
129          * are part of the offerings (if there have been any). 
130          */
131         if (!offers || ap_array_str_contains(offers, *protos)) {
132             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
133                           "proposing protocol '%s'", *protos);
134             APR_ARRAY_PUSH(proposals, const char*) = *protos;
135             proposed = 1;
136         }
137         ++protos;
138     }
139     return proposed? DECLINED : OK;
140 }
141
142 static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
143                               const char *protocol)
144 {
145     int found = 0;
146     const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos;
147     const char **p = protos;
148     
149     (void)s;
150     if (!h2_mpm_supported()) {
151         return DECLINED;
152     }
153
154     while (*p) {
155         if (!strcmp(*p, protocol)) {
156             found = 1;
157             break;
158         }
159         p++;
160     }
161     
162     if (found) {
163         h2_ctx *ctx = h2_ctx_get(c, 1);
164         
165         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
166                       "switching protocol to '%s'", protocol);
167         h2_ctx_protocol_set(ctx, protocol);
168         h2_ctx_server_set(ctx, s);
169         
170         if (r != NULL) {
171             apr_status_t status;
172             /* Switching in the middle of a request means that
173              * we have to send out the response to this one in h2
174              * format. So we need to take over the connection
175              * right away.
176              */
177             ap_remove_input_filter_byhandle(r->input_filters, "http_in");
178             ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout");
179             ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
180             
181             /* Ok, start an h2_conn on this one. */
182             h2_ctx_server_set(ctx, r->server);
183             status = h2_conn_setup(ctx, r->connection, r);
184             if (status != APR_SUCCESS) {
185                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(03088)
186                               "session setup");
187                 h2_ctx_clear(c);
188                 return !OK;
189             }
190             
191             h2_conn_run(ctx, c);
192         }
193         return OK;
194     }
195     
196     return DECLINED;
197 }
198
199 static const char *h2_protocol_get(const conn_rec *c)
200 {
201     return h2_ctx_protocol_get(c);
202 }
203
204 void h2_switch_register_hooks(void)
205 {
206     ap_hook_protocol_propose(h2_protocol_propose, NULL, NULL, APR_HOOK_MIDDLE);
207     ap_hook_protocol_switch(h2_protocol_switch, NULL, NULL, APR_HOOK_MIDDLE);
208     ap_hook_protocol_get(h2_protocol_get, NULL, NULL, APR_HOOK_MIDDLE);
209 }