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>
19 #include <apr_optional.h>
20 #include <apr_optional_hooks.h>
23 #include <http_core.h>
24 #include <http_config.h>
25 #include <http_connection.h>
26 #include <http_protocol.h>
29 #include "h2_private.h"
31 #include "h2_config.h"
35 #include "h2_switch.h"
37 /*******************************************************************************
40 APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
41 (apr_pool_t *, server_rec *,
42 conn_rec *, request_rec *,
44 static char *(*opt_ssl_var_lookup)(apr_pool_t *, server_rec *,
45 conn_rec *, request_rec *,
48 /*******************************************************************************
49 * Once per lifetime init, retrieve optional functions
51 apr_status_t h2_switch_init(apr_pool_t *pool, server_rec *s)
54 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "h2_switch init");
55 opt_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
60 static int h2_protocol_propose(conn_rec *c, request_rec *r,
62 const apr_array_header_t *offers,
63 apr_array_header_t *proposals)
66 const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos;
68 if (strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))) {
69 /* We do not know how to switch from anything else but http/1.1.
71 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
72 "protocol switch: current proto != http/1.1, declined");
78 /* So far, this indicates an HTTP/1 Upgrade header initiated
79 * protocol switch. For that, the HTTP2-Settings header needs
80 * to be present and valid for the connection.
82 p = apr_table_get(r->headers_in, "HTTP2-Settings");
84 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
85 "upgrade without HTTP2-Settings declined");
89 p = apr_table_get(r->headers_in, "Connection");
90 if (!ap_find_token(r->pool, p, "http2-settings")) {
91 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
92 "upgrade without HTTP2-Settings declined");
96 /* We also allow switching only for requests that have no body.
98 p = apr_table_get(r->headers_in, "Content-Length");
99 if (p && strcmp(p, "0")) {
100 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
101 "upgrade with content-length: %s, declined", p);
107 /* Add all protocols we know (tls or clear) and that
108 * are part of the offerings (if there have been any).
110 if (!offers || ap_array_contains(offers, *protos)) {
111 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
112 "proposing protocol '%s'", *protos);
113 APR_ARRAY_PUSH(proposals, const char*) = *protos;
118 return proposed? DECLINED : OK;
121 static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
122 const char *protocol)
125 const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos;
126 const char **p = protos;
129 if (!strcmp(*p, protocol)) {
137 h2_ctx *ctx = h2_ctx_get(c);
139 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
140 "switching protocol to '%s'", protocol);
141 h2_ctx_protocol_set(ctx, protocol);
145 /* Switching in the middle of a request means that
146 * we have to send out the response to this one in h2
147 * format. So we need to take over the connection
150 ap_remove_input_filter_byhandle(r->input_filters, "http_in");
151 ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout");
153 /* Ok, start an h2_conn on this one. */
154 status = h2_conn_rprocess(r);
155 if (status != DONE) {
156 /* Nothing really to do about this. */
157 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
158 "session proessed, unexpected status");
167 static const char *h2_protocol_get(const conn_rec *c)
169 return h2_ctx_protocol_get(c);
172 void h2_switch_register_hooks(void)
174 ap_hook_protocol_propose(h2_protocol_propose, NULL, NULL, APR_HOOK_MIDDLE);
175 ap_hook_protocol_switch(h2_protocol_switch, NULL, NULL, APR_HOOK_MIDDLE);
176 ap_hook_protocol_get(h2_protocol_get, NULL, NULL, APR_HOOK_MIDDLE);