|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <assert.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <apr_strings.h>
|
|
Packit |
90a5c9 |
#include <apr_optional.h>
|
|
Packit |
90a5c9 |
#include <apr_optional_hooks.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <httpd.h>
|
|
Packit |
90a5c9 |
#include <http_core.h>
|
|
Packit |
90a5c9 |
#include <http_config.h>
|
|
Packit |
90a5c9 |
#include <http_connection.h>
|
|
Packit |
90a5c9 |
#include <http_protocol.h>
|
|
Packit |
90a5c9 |
#include <http_log.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "h2_private.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "h2_config.h"
|
|
Packit |
90a5c9 |
#include "h2_ctx.h"
|
|
Packit |
90a5c9 |
#include "h2_conn.h"
|
|
Packit |
90a5c9 |
#include "h2_h2.h"
|
|
Packit |
90a5c9 |
#include "h2_switch.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*******************************************************************************
|
|
Packit |
90a5c9 |
* Once per lifetime init, retrieve optional functions
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
apr_status_t h2_switch_init(apr_pool_t *pool, server_rec *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
(void)pool;
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "h2_switch init");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int h2_protocol_propose(conn_rec *c, request_rec *r,
|
|
Packit |
90a5c9 |
server_rec *s,
|
|
Packit |
90a5c9 |
const apr_array_header_t *offers,
|
|
Packit |
90a5c9 |
apr_array_header_t *proposals)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int proposed = 0;
|
|
Packit |
90a5c9 |
int is_tls = h2_h2_is_tls(c);
|
|
Packit |
90a5c9 |
const char **protos = is_tls? h2_tls_protos : h2_clear_protos;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)s;
|
|
Packit |
90a5c9 |
if (!h2_mpm_supported()) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))) {
|
|
Packit |
90a5c9 |
/* We do not know how to switch from anything else but http/1.1.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03083)
|
|
Packit |
90a5c9 |
"protocol switch: current proto != http/1.1, declined");
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!h2_is_acceptable_connection(c, 0)) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03084)
|
|
Packit |
90a5c9 |
"protocol propose: connection requirements not met");
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r) {
|
|
Packit |
90a5c9 |
/* So far, this indicates an HTTP/1 Upgrade header initiated
|
|
Packit |
90a5c9 |
* protocol switch. For that, the HTTP2-Settings header needs
|
|
Packit |
90a5c9 |
* to be present and valid for the connection.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
const char *p;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!h2_allows_h2_upgrade(c)) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
p = apr_table_get(r->headers_in, "HTTP2-Settings");
|
|
Packit |
90a5c9 |
if (!p) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03085)
|
|
Packit |
90a5c9 |
"upgrade without HTTP2-Settings declined");
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
p = apr_table_get(r->headers_in, "Connection");
|
|
Packit |
90a5c9 |
if (!ap_find_token(r->pool, p, "http2-settings")) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03086)
|
|
Packit |
90a5c9 |
"upgrade without HTTP2-Settings declined");
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We also allow switching only for requests that have no body.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
p = apr_table_get(r->headers_in, "Content-Length");
|
|
Packit |
90a5c9 |
if (p && strcmp(p, "0")) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03087)
|
|
Packit |
90a5c9 |
"upgrade with content-length: %s, declined", p);
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
while (*protos) {
|
|
Packit |
90a5c9 |
/* Add all protocols we know (tls or clear) and that
|
|
Packit |
90a5c9 |
* are part of the offerings (if there have been any).
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (!offers || ap_array_str_contains(offers, *protos)) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
|
Packit |
90a5c9 |
"proposing protocol '%s'", *protos);
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(proposals, const char*) = *protos;
|
|
Packit |
90a5c9 |
proposed = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
++protos;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return proposed? DECLINED : OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
|
|
Packit |
90a5c9 |
const char *protocol)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int found = 0;
|
|
Packit |
90a5c9 |
const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos;
|
|
Packit |
90a5c9 |
const char **p = protos;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)s;
|
|
Packit |
90a5c9 |
if (!h2_mpm_supported()) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
while (*p) {
|
|
Packit |
90a5c9 |
if (!strcmp(*p, protocol)) {
|
|
Packit |
90a5c9 |
found = 1;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
p++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (found) {
|
|
Packit |
90a5c9 |
h2_ctx *ctx = h2_ctx_get(c, 1);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
|
Packit |
90a5c9 |
"switching protocol to '%s'", protocol);
|
|
Packit |
90a5c9 |
h2_ctx_protocol_set(ctx, protocol);
|
|
Packit |
90a5c9 |
h2_ctx_server_set(ctx, s);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r != NULL) {
|
|
Packit |
90a5c9 |
apr_status_t status;
|
|
Packit |
90a5c9 |
/* Switching in the middle of a request means that
|
|
Packit |
90a5c9 |
* we have to send out the response to this one in h2
|
|
Packit |
90a5c9 |
* format. So we need to take over the connection
|
|
Packit |
90a5c9 |
* right away.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ap_remove_input_filter_byhandle(r->input_filters, "http_in");
|
|
Packit |
90a5c9 |
ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout");
|
|
Packit |
90a5c9 |
ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Ok, start an h2_conn on this one. */
|
|
Packit |
90a5c9 |
h2_ctx_server_set(ctx, r->server);
|
|
Packit |
90a5c9 |
status = h2_conn_setup(ctx, r->connection, r);
|
|
Packit |
90a5c9 |
if (status != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(03088)
|
|
Packit |
90a5c9 |
"session setup");
|
|
Packit |
90a5c9 |
h2_ctx_clear(c);
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
h2_conn_run(ctx, c);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *h2_protocol_get(const conn_rec *c)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return h2_ctx_protocol_get(c);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void h2_switch_register_hooks(void)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_hook_protocol_propose(h2_protocol_propose, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
ap_hook_protocol_switch(h2_protocol_switch, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
ap_hook_protocol_get(h2_protocol_get, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
}
|