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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
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
23 * http://www.apache.org/licenses/LICENSE-2.0
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.
34 #include <apr_strings.h>
35 #include <apr_optional.h>
36 #include <apr_optional_hooks.h>
39 #include <http_core.h>
40 #include <http_config.h>
41 #include <http_connection.h>
42 #include <http_protocol.h>
45 #include "h2_private.h"
47 #include "h2_config.h"
51 #include "h2_switch.h"
53 /*******************************************************************************
54 * Once per lifetime init, retrieve optional functions
56 apr_status_t h2_switch_init(apr_pool_t *pool, server_rec *s)
59 ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "h2_switch init");
64 static int h2_protocol_propose(conn_rec *c, request_rec *r,
66 const apr_array_header_t *offers,
67 apr_array_header_t *proposals)
70 int is_tls = h2_h2_is_tls(c);
71 const char **protos = is_tls? h2_tls_protos : h2_clear_protos;
74 if (!h2_mpm_supported()) {
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.
81 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03083)
82 "protocol switch: current proto != http/1.1, declined");
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");
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.
99 if (!h2_allows_h2_upgrade(c)) {
103 p = apr_table_get(r->headers_in, "HTTP2-Settings");
105 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03085)
106 "upgrade without HTTP2-Settings declined");
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");
117 /* We also allow switching only for requests that have no body.
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);
128 /* Add all protocols we know (tls or clear) and that
129 * are part of the offerings (if there have been any).
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;
139 return proposed? DECLINED : OK;
142 static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
143 const char *protocol)
146 const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos;
147 const char **p = protos;
150 if (!h2_mpm_supported()) {
155 if (!strcmp(*p, protocol)) {
163 h2_ctx *ctx = h2_ctx_get(c, 1);
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);
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
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");
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)
199 static const char *h2_protocol_get(const conn_rec *c)
201 return h2_ctx_protocol_get(c);
204 void h2_switch_register_hooks(void)
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);