mirror of
https://github.com/apache/httpd.git
synced 2025-08-08 15:02:10 +03:00
new Protocols directive and core API changes to enable protocol switching on HTTP Upgrade or ALPN, implemented in mod_ssl and mod_h2
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1692486 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -1 +1 @@
|
|||||||
2905
|
2963
|
||||||
|
@@ -707,6 +707,9 @@ typedef struct {
|
|||||||
#define AP_HTTP_EXPECT_STRICT_ENABLE 1
|
#define AP_HTTP_EXPECT_STRICT_ENABLE 1
|
||||||
#define AP_HTTP_EXPECT_STRICT_DISABLE 2
|
#define AP_HTTP_EXPECT_STRICT_DISABLE 2
|
||||||
int http_expect_strict;
|
int http_expect_strict;
|
||||||
|
|
||||||
|
|
||||||
|
apr_array_header_t *protocols;
|
||||||
} core_server_config;
|
} core_server_config;
|
||||||
|
|
||||||
/* for AddOutputFiltersByType in core.c */
|
/* for AddOutputFiltersByType in core.c */
|
||||||
|
@@ -700,6 +700,109 @@ AP_DECLARE_HOOK(const char *,http_scheme,(const request_rec *r))
|
|||||||
*/
|
*/
|
||||||
AP_DECLARE_HOOK(apr_port_t,default_port,(const request_rec *r))
|
AP_DECLARE_HOOK(apr_port_t,default_port,(const request_rec *r))
|
||||||
|
|
||||||
|
|
||||||
|
#define AP_PROTOCOL_HTTP1 "http/1.1"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Negotiate a possible protocol switch on the connection. The negotiation
|
||||||
|
* may start without any request sent, in which case the request is NULL. Or
|
||||||
|
* it may be triggered by the request received, e.g. through the "Upgrade"
|
||||||
|
* header.
|
||||||
|
*
|
||||||
|
* The identifiers for protocols are taken from the TLS extension type ALPN:
|
||||||
|
* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xml
|
||||||
|
*
|
||||||
|
* If no protocols are added to the proposals, the server will always fallback
|
||||||
|
* to "http/1.1" which is the default protocol for connections that Apache
|
||||||
|
* handles. If the protocol selected from the proposals is the protocol
|
||||||
|
* already in place, no "protocol_switch" will be invoked.
|
||||||
|
*
|
||||||
|
* All hooks are run, unless one returns an error. Proposals may contain
|
||||||
|
* duplicates. The order in which proposals are added is usually ignored.
|
||||||
|
*
|
||||||
|
* @param c The current connection
|
||||||
|
* @param r The current request or NULL
|
||||||
|
* @param s The server/virtual host selected
|
||||||
|
* @param offers a list of protocol identifiers offered by the client
|
||||||
|
* @param proposals the list of protocol identifiers proposed by the hooks
|
||||||
|
* @return OK or DECLINED
|
||||||
|
*/
|
||||||
|
AP_DECLARE_HOOK(int,protocol_propose,(conn_rec *c, request_rec *r,
|
||||||
|
server_rec *s,
|
||||||
|
const apr_array_header_t *offers,
|
||||||
|
apr_array_header_t *proposals))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a protocol switch on the connection. The exact requirements for
|
||||||
|
* that depend on the protocol in place and the one switched to. The first
|
||||||
|
* protocol module to handle the switch is the last module run.
|
||||||
|
*
|
||||||
|
* For a connection level switch (r == NULL), the handler must on return
|
||||||
|
* leave the conn_rec in a state suitable for processing the switched
|
||||||
|
* protocol, e.g. correct filters in place.
|
||||||
|
*
|
||||||
|
* For a request triggered switch (r != NULL), the protocol switch is done
|
||||||
|
* before the response is sent out. When switching from "http/1.1" via Upgrade
|
||||||
|
* header, the 101 intermediate response will have been sent. The
|
||||||
|
* hook needs then to process the connection until it can be closed. Which
|
||||||
|
* the server will enforce on hook return.
|
||||||
|
* Any error the hook might encounter must already be sent by the hook itself
|
||||||
|
* to the client in whatever form the new protocol requires.
|
||||||
|
*
|
||||||
|
* @param c The current connection
|
||||||
|
* @param r The current request or NULL
|
||||||
|
* @param s The server/virtual host selected
|
||||||
|
* @param choices a list of protocol identifiers, normally the clients whishes
|
||||||
|
* @param proposals the list of protocol identifiers proposed by the hooks
|
||||||
|
* @return OK or DECLINED
|
||||||
|
*/
|
||||||
|
AP_DECLARE_HOOK(int,protocol_switch,(conn_rec *c, request_rec *r,
|
||||||
|
server_rec *s,
|
||||||
|
const char *protocol))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the protocol used on the connection. Modules implementing
|
||||||
|
* protocol switching must register here and return the correct protocol
|
||||||
|
* identifier for connections they switched.
|
||||||
|
*
|
||||||
|
* @param c The current connection
|
||||||
|
* @return The identifier of the protocol in place
|
||||||
|
*/
|
||||||
|
AP_DECLARE_HOOK(const char *,protocol_get,(const conn_rec *c))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a protocol for the given connection and optional request. Will return
|
||||||
|
* the protocol identifier selected which may be the protocol already in place
|
||||||
|
* on the connection. The server may ignore the choices given.
|
||||||
|
*
|
||||||
|
* @param c The current connection
|
||||||
|
* @param r The current request or NULL
|
||||||
|
* @param s The server/virtual host selected
|
||||||
|
* @param choices a list of protocol identifiers, normally the clients whishes
|
||||||
|
* @return the selected protocol
|
||||||
|
*/
|
||||||
|
AP_DECLARE(const char *) ap_select_protocol(conn_rec *c, request_rec *r,
|
||||||
|
server_rec *s,
|
||||||
|
apr_array_header_t *choices);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the actual protocol switch. The protocol given must have been
|
||||||
|
* selected before on the very same connection and request pair.
|
||||||
|
*
|
||||||
|
* @param c The current connection
|
||||||
|
* @param r The current request or NULL
|
||||||
|
* @param s The server/virtual host selected
|
||||||
|
* @param protocol the protocol to switch to
|
||||||
|
* @return APR_SUCCESS, if caller may continue processing as usual
|
||||||
|
* APR_EOF, if caller needs to stop processing the connection
|
||||||
|
* APR_EINVAL, if the protocol is already in place
|
||||||
|
* APR_NOTIMPL, if no module performed the switch
|
||||||
|
* Other errors where appropriate
|
||||||
|
*/
|
||||||
|
AP_DECLARE(apr_status_t) ap_switch_protocol(conn_rec *c, request_rec *r,
|
||||||
|
server_rec *s,
|
||||||
|
const char *protocol);
|
||||||
|
|
||||||
/** @see ap_bucket_type_error */
|
/** @see ap_bucket_type_error */
|
||||||
typedef struct ap_bucket_error ap_bucket_error;
|
typedef struct ap_bucket_error ap_bucket_error;
|
||||||
|
|
||||||
|
@@ -19,7 +19,6 @@ APACHE_MODPATH_INIT(http2)
|
|||||||
dnl # list of module object files
|
dnl # list of module object files
|
||||||
h2_objs="dnl
|
h2_objs="dnl
|
||||||
mod_h2.lo dnl
|
mod_h2.lo dnl
|
||||||
h2_alpn.lo dnl
|
|
||||||
h2_alt_svc.lo dnl
|
h2_alt_svc.lo dnl
|
||||||
h2_config.lo dnl
|
h2_config.lo dnl
|
||||||
h2_conn.lo dnl
|
h2_conn.lo dnl
|
||||||
@@ -35,12 +34,12 @@ h2_response.lo dnl
|
|||||||
h2_session.lo dnl
|
h2_session.lo dnl
|
||||||
h2_stream.lo dnl
|
h2_stream.lo dnl
|
||||||
h2_stream_set.lo dnl
|
h2_stream_set.lo dnl
|
||||||
|
h2_switch.lo dnl
|
||||||
h2_task.lo dnl
|
h2_task.lo dnl
|
||||||
h2_task_input.lo dnl
|
h2_task_input.lo dnl
|
||||||
h2_task_output.lo dnl
|
h2_task_output.lo dnl
|
||||||
h2_task_queue.lo dnl
|
h2_task_queue.lo dnl
|
||||||
h2_to_h1.lo dnl
|
h2_to_h1.lo dnl
|
||||||
h2_upgrade.lo dnl
|
|
||||||
h2_util.lo dnl
|
h2_util.lo dnl
|
||||||
h2_worker.lo dnl
|
h2_worker.lo dnl
|
||||||
h2_workers.lo dnl
|
h2_workers.lo dnl
|
||||||
|
@@ -1,299 +0,0 @@
|
|||||||
/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include <apr_strings.h>
|
|
||||||
#include <apr_optional.h>
|
|
||||||
#include <apr_optional_hooks.h>
|
|
||||||
|
|
||||||
#include <httpd.h>
|
|
||||||
#include <http_core.h>
|
|
||||||
#include <http_config.h>
|
|
||||||
#include <http_connection.h>
|
|
||||||
#include <http_protocol.h>
|
|
||||||
#include <http_log.h>
|
|
||||||
|
|
||||||
#include "h2_private.h"
|
|
||||||
|
|
||||||
#include "h2_config.h"
|
|
||||||
#include "h2_ctx.h"
|
|
||||||
#include "h2_conn.h"
|
|
||||||
#include "h2_h2.h"
|
|
||||||
#include "h2_alpn.h"
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* SSL var lookup
|
|
||||||
*/
|
|
||||||
APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
|
|
||||||
(apr_pool_t *, server_rec *,
|
|
||||||
conn_rec *, request_rec *,
|
|
||||||
char *));
|
|
||||||
static char *(*opt_ssl_var_lookup)(apr_pool_t *, server_rec *,
|
|
||||||
conn_rec *, request_rec *,
|
|
||||||
char *);
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* NPN callbacks and registry, deprecated
|
|
||||||
*/
|
|
||||||
typedef int (*ssl_npn_advertise_protos)(conn_rec *connection,
|
|
||||||
apr_array_header_t *protos);
|
|
||||||
|
|
||||||
typedef int (*ssl_npn_proto_negotiated)(conn_rec *connection,
|
|
||||||
const char *proto_name, apr_size_t proto_name_len);
|
|
||||||
|
|
||||||
APR_DECLARE_OPTIONAL_FN(int, modssl_register_npn,
|
|
||||||
(conn_rec *conn,
|
|
||||||
ssl_npn_advertise_protos advertisefn,
|
|
||||||
ssl_npn_proto_negotiated negotiatedfn));
|
|
||||||
|
|
||||||
static int (*opt_ssl_register_npn)(conn_rec*,
|
|
||||||
ssl_npn_advertise_protos,
|
|
||||||
ssl_npn_proto_negotiated);
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* ALPN callbacks and registry
|
|
||||||
*/
|
|
||||||
typedef int (*ssl_alpn_propose_protos)(conn_rec *connection,
|
|
||||||
apr_array_header_t *client_protos, apr_array_header_t *protos);
|
|
||||||
|
|
||||||
typedef int (*ssl_alpn_proto_negotiated)(conn_rec *connection,
|
|
||||||
const char *proto_name, apr_size_t proto_name_len);
|
|
||||||
|
|
||||||
APR_DECLARE_OPTIONAL_FN(int, modssl_register_alpn,
|
|
||||||
(conn_rec *conn,
|
|
||||||
ssl_alpn_propose_protos proposefn,
|
|
||||||
ssl_alpn_proto_negotiated negotiatedfn));
|
|
||||||
|
|
||||||
static int (*opt_ssl_register_alpn)(conn_rec*,
|
|
||||||
ssl_alpn_propose_protos,
|
|
||||||
ssl_alpn_proto_negotiated);
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* Hooks for processing incoming connections:
|
|
||||||
* - pre_conn_after_tls registers for ALPN handling
|
|
||||||
*/
|
|
||||||
static int h2_alpn_pre_conn(conn_rec* c, void *arg);
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* Once per lifetime init, retrieve optional functions
|
|
||||||
*/
|
|
||||||
apr_status_t h2_alpn_init(apr_pool_t *pool, server_rec *s)
|
|
||||||
{
|
|
||||||
(void)pool;
|
|
||||||
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "h2_alpn init");
|
|
||||||
opt_ssl_register_npn = APR_RETRIEVE_OPTIONAL_FN(modssl_register_npn);
|
|
||||||
opt_ssl_register_alpn = APR_RETRIEVE_OPTIONAL_FN(modssl_register_alpn);
|
|
||||||
opt_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
|
|
||||||
|
|
||||||
if (!opt_ssl_register_alpn && !opt_ssl_register_npn) {
|
|
||||||
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
|
|
||||||
"mod_ssl does not offer ALPN or NPN registration");
|
|
||||||
}
|
|
||||||
return APR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* Register various hooks
|
|
||||||
*/
|
|
||||||
static const char *const mod_ssl[] = { "mod_ssl.c", NULL};
|
|
||||||
static const char *const mod_core[] = { "core.c", NULL};
|
|
||||||
|
|
||||||
static void check_sni_host(conn_rec *c)
|
|
||||||
{
|
|
||||||
/* If we have not done so already, ask the connection for the
|
|
||||||
* hostname send to us via SNI. This information is later used
|
|
||||||
* to retrieve the correct server settings for this connection.
|
|
||||||
*/
|
|
||||||
h2_ctx *ctx = h2_ctx_get(c);
|
|
||||||
if (opt_ssl_var_lookup && !ctx->hostname) {
|
|
||||||
const char *p = opt_ssl_var_lookup(c->pool, c->base_server, c,
|
|
||||||
NULL, (char*)"SSL_TLS_SNI");
|
|
||||||
if (p && *p) {
|
|
||||||
ctx->hostname = apr_pstrdup(c->pool, p);
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
|
|
||||||
"h2_h2, connection, SNI %s",
|
|
||||||
ctx->hostname? ctx->hostname : "NULL");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void h2_alpn_register_hooks(void)
|
|
||||||
{
|
|
||||||
/* This hook runs on new connection after mod_ssl, but before the core
|
|
||||||
* httpd. Its purpose is to register, if TLS is used, the ALPN callbacks
|
|
||||||
* that enable us to chose "h2" as next procotol if the client supports it.
|
|
||||||
*/
|
|
||||||
ap_hook_pre_connection(h2_alpn_pre_conn,
|
|
||||||
mod_ssl, mod_core, APR_HOOK_LAST);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h2_util_array_index(apr_array_header_t *array, const char *s)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < array->nelts; i++) {
|
|
||||||
const char *p = APR_ARRAY_IDX(array, i, const char*);
|
|
||||||
if (!strcmp(p, s)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h2_npn_advertise(conn_rec *c, apr_array_header_t *protos)
|
|
||||||
{
|
|
||||||
h2_config *cfg;
|
|
||||||
apr_size_t i;
|
|
||||||
|
|
||||||
check_sni_host(c);
|
|
||||||
cfg = h2_config_get(c);
|
|
||||||
if (!h2_config_geti(cfg, H2_CONF_ENABLED)) {
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < h2_alpn_protos_len; ++i) {
|
|
||||||
const char *proto = h2_alpn_protos[i];
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
|
||||||
"NPN proposing %s from client selection", proto);
|
|
||||||
APR_ARRAY_PUSH(protos, const char*) = proto;
|
|
||||||
}
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h2_negotiated(conn_rec *c, const char *via,
|
|
||||||
const char *proto_name,
|
|
||||||
apr_size_t proto_name_len)
|
|
||||||
{
|
|
||||||
h2_ctx *ctx = h2_ctx_get(c);
|
|
||||||
apr_size_t i;
|
|
||||||
|
|
||||||
if (h2_ctx_is_task(ctx) ) {
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (h2_ctx_pnego_is_done(ctx)) {
|
|
||||||
/* called twice? refraing from overriding existing selection.
|
|
||||||
* NPN is fading...
|
|
||||||
*/
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
|
||||||
"protocol negotiated via %s called, but already set",
|
|
||||||
via);
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (APLOGctrace1(c)) {
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
|
||||||
"protocol negotiated via %s is %s", via,
|
|
||||||
apr_pstrndup(c->pool, proto_name, proto_name_len));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < h2_alpn_protos_len; ++i) {
|
|
||||||
const char *proto = h2_alpn_protos[i];
|
|
||||||
if (proto_name_len == strlen(proto)
|
|
||||||
&& strncmp(proto, proto_name, proto_name_len) == 0) {
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
|
|
||||||
"protocol set via %s to %s", via, proto);
|
|
||||||
h2_ctx_pnego_set_done(ctx, proto);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h2_npn_negotiated(conn_rec *c,
|
|
||||||
const char *proto_name,
|
|
||||||
apr_size_t proto_name_len)
|
|
||||||
{
|
|
||||||
return h2_negotiated(c, "NPN", proto_name, proto_name_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h2_alpn_propose(conn_rec *c,
|
|
||||||
apr_array_header_t *client_protos,
|
|
||||||
apr_array_header_t *protos)
|
|
||||||
{
|
|
||||||
h2_config *cfg;
|
|
||||||
apr_size_t i;
|
|
||||||
|
|
||||||
check_sni_host(c);
|
|
||||||
cfg = h2_config_get(c);
|
|
||||||
if (!h2_config_geti(cfg, H2_CONF_ENABLED)) {
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
|
|
||||||
"ALPN propose, h2 disabled for config %s", cfg->name);
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
|
|
||||||
"ALPN propose for config %s", cfg->name);
|
|
||||||
/* */
|
|
||||||
for (i = 0; i < h2_alpn_protos_len; ++i) {
|
|
||||||
const char *proto = h2_alpn_protos[i];
|
|
||||||
if (h2_util_array_index(client_protos, proto) >= 0) {
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
|
||||||
"ALPN proposing %s", proto);
|
|
||||||
APR_ARRAY_PUSH(protos, const char*) = proto;
|
|
||||||
return OK; /* propose only one, the first match from our list */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h2_alpn_negotiated(conn_rec *c,
|
|
||||||
const char *proto_name,
|
|
||||||
apr_size_t proto_name_len)
|
|
||||||
{
|
|
||||||
return h2_negotiated(c, "ALPN", proto_name, proto_name_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int h2_alpn_pre_conn(conn_rec* c, void *arg)
|
|
||||||
{
|
|
||||||
h2_ctx *ctx = h2_ctx_get(c);
|
|
||||||
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
|
|
||||||
"h2_h2, pre_connection, start");
|
|
||||||
|
|
||||||
if (h2_ctx_is_task(ctx)) {
|
|
||||||
/* our stream pseudo connection */
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (h2_h2_is_tls(c)) {
|
|
||||||
/* Brand new TLS connection: Does mod_ssl offer ALPN/NPN support?
|
|
||||||
* If so, register at all present, clients may use either/or.
|
|
||||||
*/
|
|
||||||
if (opt_ssl_register_alpn == NULL && opt_ssl_register_npn == NULL) {
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
|
|
||||||
"h2_h2, pre_connection, no ALPN/NPN "
|
|
||||||
"support in mod_ssl");
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt_ssl_register_alpn) {
|
|
||||||
opt_ssl_register_alpn(c, h2_alpn_propose, h2_alpn_negotiated);
|
|
||||||
}
|
|
||||||
if (opt_ssl_register_npn) {
|
|
||||||
opt_ssl_register_npn(c, h2_npn_advertise, h2_npn_negotiated);
|
|
||||||
}
|
|
||||||
|
|
||||||
h2_ctx_pnego_set_started(ctx);
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
|
|
||||||
"h2_alpn, pre_connection, ALPN callback registered");
|
|
||||||
}
|
|
||||||
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
@@ -144,7 +144,8 @@ apr_status_t h2_conn_rprocess(request_rec *r)
|
|||||||
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "h2_conn_process start");
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "h2_conn_process start");
|
||||||
if (!workers) {
|
if (!workers) {
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "workers not initialized");
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02911)
|
||||||
|
"workers not initialized");
|
||||||
return APR_EGENERAL;
|
return APR_EGENERAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +164,8 @@ apr_status_t h2_conn_main(conn_rec *c)
|
|||||||
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "h2_conn_main start");
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "h2_conn_main start");
|
||||||
if (!workers) {
|
if (!workers) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, "workers not initialized");
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02912)
|
||||||
|
"workers not initialized");
|
||||||
return APR_EGENERAL;
|
return APR_EGENERAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,6 +295,7 @@ apr_status_t h2_session_process(h2_session *session)
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ap_log_cerror( APLOG_MARK, APLOG_WARNING, status, session->c,
|
ap_log_cerror( APLOG_MARK, APLOG_WARNING, status, session->c,
|
||||||
|
APLOGNO(02950)
|
||||||
"h2_session(%ld): error reading, terminating",
|
"h2_session(%ld): error reading, terminating",
|
||||||
session->id);
|
session->id);
|
||||||
h2_session_abort(session, status, 0);
|
h2_session_abort(session, status, 0);
|
||||||
@@ -354,7 +357,7 @@ conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *pool)
|
|||||||
master->bucket_alloc);
|
master->bucket_alloc);
|
||||||
if (c == NULL) {
|
if (c == NULL) {
|
||||||
ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool,
|
ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool,
|
||||||
"h2_task: creating conn");
|
APLOGNO(02913) "h2_task: creating conn");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* TODO: we simulate that we had already a request on this connection.
|
/* TODO: we simulate that we had already a request on this connection.
|
||||||
@@ -440,7 +443,7 @@ apr_status_t h2_conn_init(struct h2_task_env *env, struct h2_worker *worker)
|
|||||||
master->bucket_alloc);
|
master->bucket_alloc);
|
||||||
if (c == NULL) {
|
if (c == NULL) {
|
||||||
ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, env->pool,
|
ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, env->pool,
|
||||||
"h2_task: creating conn");
|
APLOGNO(02914) "h2_task: creating conn");
|
||||||
return APR_ENOMEM;
|
return APR_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
#include "h2_ctx.h"
|
#include "h2_ctx.h"
|
||||||
#include "h2_private.h"
|
#include "h2_private.h"
|
||||||
|
|
||||||
static h2_ctx *h2_ctx_create(conn_rec *c)
|
static h2_ctx *h2_ctx_create(const conn_rec *c)
|
||||||
{
|
{
|
||||||
h2_ctx *ctx = apr_pcalloc(c->pool, sizeof(h2_ctx));
|
h2_ctx *ctx = apr_pcalloc(c->pool, sizeof(h2_ctx));
|
||||||
AP_DEBUG_ASSERT(ctx);
|
AP_DEBUG_ASSERT(ctx);
|
||||||
@@ -33,7 +33,7 @@ static h2_ctx *h2_ctx_create(conn_rec *c)
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2_ctx *h2_ctx_create_for(conn_rec *c, h2_task_env *env)
|
h2_ctx *h2_ctx_create_for(const conn_rec *c, h2_task_env *env)
|
||||||
{
|
{
|
||||||
h2_ctx *ctx = h2_ctx_create(c);
|
h2_ctx *ctx = h2_ctx_create(c);
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
@@ -42,7 +42,7 @@ h2_ctx *h2_ctx_create_for(conn_rec *c, h2_task_env *env)
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2_ctx *h2_ctx_get(conn_rec *c)
|
h2_ctx *h2_ctx_get(const conn_rec *c)
|
||||||
{
|
{
|
||||||
h2_ctx *ctx = (h2_ctx*)ap_get_module_config(c->conn_config, &h2_module);
|
h2_ctx *ctx = (h2_ctx*)ap_get_module_config(c->conn_config, &h2_module);
|
||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
@@ -51,22 +51,18 @@ h2_ctx *h2_ctx_get(conn_rec *c)
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2_ctx *h2_ctx_rget(request_rec *r)
|
h2_ctx *h2_ctx_rget(const request_rec *r)
|
||||||
{
|
{
|
||||||
return h2_ctx_get(r->connection);
|
return h2_ctx_get(r->connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *h2_ctx_pnego_get(h2_ctx *ctx)
|
const char *h2_ctx_protocol_get(const conn_rec *c)
|
||||||
{
|
{
|
||||||
|
h2_ctx *ctx = (h2_ctx*)ap_get_module_config(c->conn_config, &h2_module);
|
||||||
return ctx? ctx->protocol : NULL;
|
return ctx? ctx->protocol : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void h2_ctx_pnego_set_started(h2_ctx *ctx)
|
h2_ctx *h2_ctx_protocol_set(h2_ctx *ctx, const char *proto)
|
||||||
{
|
|
||||||
ctx->pnego_state = H2_PNEGO_STARTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2_ctx *h2_ctx_pnego_set_done(h2_ctx *ctx, const char *proto)
|
|
||||||
{
|
{
|
||||||
ctx->protocol = proto;
|
ctx->protocol = proto;
|
||||||
ctx->pnego_state = H2_PNEGO_DONE;
|
ctx->pnego_state = H2_PNEGO_DONE;
|
||||||
@@ -84,11 +80,6 @@ int h2_ctx_pnego_is_ongoing(h2_ctx *ctx)
|
|||||||
return ctx && (ctx->pnego_state == H2_PNEGO_STARTED);
|
return ctx && (ctx->pnego_state == H2_PNEGO_STARTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
int h2_ctx_pnego_is_done(h2_ctx *ctx)
|
|
||||||
{
|
|
||||||
return ctx && (ctx->pnego_state == H2_PNEGO_DONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int h2_ctx_is_active(h2_ctx *ctx)
|
int h2_ctx_is_active(h2_ctx *ctx)
|
||||||
{
|
{
|
||||||
return ctx && ctx->is_h2;
|
return ctx && ctx->is_h2;
|
||||||
|
@@ -43,18 +43,16 @@ typedef struct h2_ctx {
|
|||||||
struct h2_config *config; /* effective config in this context */
|
struct h2_config *config; /* effective config in this context */
|
||||||
} h2_ctx;
|
} h2_ctx;
|
||||||
|
|
||||||
h2_ctx *h2_ctx_get(conn_rec *c);
|
h2_ctx *h2_ctx_get(const conn_rec *c);
|
||||||
h2_ctx *h2_ctx_rget(request_rec *r);
|
h2_ctx *h2_ctx_rget(const request_rec *r);
|
||||||
h2_ctx *h2_ctx_create_for(conn_rec *c, struct h2_task_env *env);
|
h2_ctx *h2_ctx_create_for(const conn_rec *c, struct h2_task_env *env);
|
||||||
|
|
||||||
|
|
||||||
void h2_ctx_pnego_set_started(h2_ctx *ctx);
|
/* Set the h2 protocol established on this connection context or
|
||||||
h2_ctx *h2_ctx_pnego_set_done(h2_ctx *ctx, const char *proto);
|
* NULL when other protocols are in place.
|
||||||
/**
|
|
||||||
* Returns != 0 iff protocol negitiation did happen, not matter
|
|
||||||
* what the outcome was.
|
|
||||||
*/
|
*/
|
||||||
int h2_ctx_pnego_is_done(h2_ctx *ctx);
|
h2_ctx *h2_ctx_protocol_set(h2_ctx *ctx, const char *proto);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns != 0 iff protocol negotiation has started but is not
|
* Returns != 0 iff protocol negotiation has started but is not
|
||||||
* done yet.
|
* done yet.
|
||||||
@@ -64,7 +62,7 @@ int h2_ctx_pnego_is_ongoing(h2_ctx *ctx);
|
|||||||
/**
|
/**
|
||||||
* Get the h2 protocol negotiated for this connection, or NULL.
|
* Get the h2 protocol negotiated for this connection, or NULL.
|
||||||
*/
|
*/
|
||||||
const char *h2_ctx_pnego_get(h2_ctx *ctx);
|
const char *h2_ctx_protocol_get(const conn_rec *c);
|
||||||
|
|
||||||
int h2_ctx_is_task(h2_ctx *ctx);
|
int h2_ctx_is_task(h2_ctx *ctx);
|
||||||
int h2_ctx_is_active(h2_ctx *ctx);
|
int h2_ctx_is_active(h2_ctx *ctx);
|
||||||
|
@@ -83,6 +83,7 @@ static apr_status_t make_h2_headers(h2_from_h1 *from_h1, request_rec *r)
|
|||||||
from_h1->pool);
|
from_h1->pool);
|
||||||
if (from_h1->response == NULL) {
|
if (from_h1->response == NULL) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, r->connection,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, r->connection,
|
||||||
|
APLOGNO(02915)
|
||||||
"h2_from_h1(%d): unable to create resp_head",
|
"h2_from_h1(%d): unable to create resp_head",
|
||||||
from_h1->stream_id);
|
from_h1->stream_id);
|
||||||
return APR_EINVAL;
|
return APR_EINVAL;
|
||||||
|
@@ -33,20 +33,15 @@
|
|||||||
#include "h2_config.h"
|
#include "h2_config.h"
|
||||||
#include "h2_ctx.h"
|
#include "h2_ctx.h"
|
||||||
#include "h2_conn.h"
|
#include "h2_conn.h"
|
||||||
#include "h2_alpn.h"
|
|
||||||
#include "h2_h2.h"
|
#include "h2_h2.h"
|
||||||
|
|
||||||
const char *h2_alpn_protos[] = {
|
const char *h2_tls_protos[] = {
|
||||||
"h2",
|
"h2", NULL
|
||||||
};
|
};
|
||||||
apr_size_t h2_alpn_protos_len = (sizeof(h2_alpn_protos)
|
|
||||||
/ sizeof(h2_alpn_protos[0]));
|
|
||||||
|
|
||||||
const char *h2_upgrade_protos[] = {
|
const char *h2_clear_protos[] = {
|
||||||
"h2c",
|
"h2c", NULL
|
||||||
};
|
};
|
||||||
apr_size_t h2_upgrade_protos_len = (sizeof(h2_upgrade_protos)
|
|
||||||
/ sizeof(h2_upgrade_protos[0]));
|
|
||||||
|
|
||||||
const char *H2_MAGIC_TOKEN = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
const char *H2_MAGIC_TOKEN = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
||||||
|
|
||||||
@@ -80,7 +75,7 @@ apr_status_t h2_h2_init(apr_pool_t *pool, server_rec *s)
|
|||||||
|
|
||||||
if (!opt_ssl_is_https) {
|
if (!opt_ssl_is_https) {
|
||||||
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
|
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
|
||||||
"mod_ssl does not seem to be enabled");
|
APLOGNO(02951) "mod_ssl does not seem to be enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
return APR_SUCCESS;
|
return APR_SUCCESS;
|
||||||
@@ -107,7 +102,7 @@ static const char *const mod_reqtimeout[] = { "reqtimeout.c", NULL};
|
|||||||
void h2_h2_register_hooks(void)
|
void h2_h2_register_hooks(void)
|
||||||
{
|
{
|
||||||
/* When the connection processing actually starts, we might to
|
/* When the connection processing actually starts, we might to
|
||||||
* take over, if h2* was selected by ALPN on a TLS connection.
|
* take over, if h2* was selected as protocol.
|
||||||
*/
|
*/
|
||||||
ap_hook_process_connection(h2_h2_process_conn,
|
ap_hook_process_connection(h2_h2_process_conn,
|
||||||
NULL, NULL, APR_HOOK_FIRST);
|
NULL, NULL, APR_HOOK_FIRST);
|
||||||
@@ -156,7 +151,7 @@ int h2_h2_process_conn(conn_rec* c)
|
|||||||
apr_bucket_brigade* temp;
|
apr_bucket_brigade* temp;
|
||||||
|
|
||||||
if (h2_ctx_is_task(ctx)) {
|
if (h2_ctx_is_task(ctx)) {
|
||||||
/* out stream pseudo connection */
|
/* our stream pseudo connection */
|
||||||
return DECLINED;
|
return DECLINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,15 +165,19 @@ int h2_h2_process_conn(conn_rec* c)
|
|||||||
apr_brigade_destroy(temp);
|
apr_brigade_destroy(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we still do not know the protocol and H2Direct is enabled, check
|
/* If we have not already switched to a h2* protocol
|
||||||
* if we receive the magic PRIamble. A client sending this on connection
|
* and the connection is on "http/1.1"
|
||||||
|
* and H2Direct is enabled,
|
||||||
|
* -> sniff for the magic PRIamble. A client sending this on connection
|
||||||
* start should know what it is doing.
|
* start should know what it is doing.
|
||||||
*/
|
*/
|
||||||
if (!h2_ctx_pnego_is_done(ctx) && h2_config_geti(cfg, H2_CONF_DIRECT)) {
|
if (!h2_ctx_protocol_get(c)
|
||||||
|
&& !strcmp(AP_PROTOCOL_HTTP1, ap_run_protocol_get(c))
|
||||||
|
&& h2_config_geti(cfg, H2_CONF_DIRECT)) {
|
||||||
apr_status_t status;
|
apr_status_t status;
|
||||||
temp = apr_brigade_create(c->pool, c->bucket_alloc);
|
temp = apr_brigade_create(c->pool, c->bucket_alloc);
|
||||||
status = ap_get_brigade(c->input_filters, temp,
|
status = ap_get_brigade(c->input_filters, temp,
|
||||||
/*h2_h2_is_tls(c)? AP_MODE_READBYTES :*/ AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24);
|
AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24);
|
||||||
if (status == APR_SUCCESS) {
|
if (status == APR_SUCCESS) {
|
||||||
char *s = NULL;
|
char *s = NULL;
|
||||||
apr_size_t slen;
|
apr_size_t slen;
|
||||||
@@ -187,7 +186,7 @@ int h2_h2_process_conn(conn_rec* c)
|
|||||||
if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) {
|
if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
||||||
"h2_h2, direct mode detected");
|
"h2_h2, direct mode detected");
|
||||||
h2_ctx_pnego_set_done(ctx, "h2");
|
h2_ctx_protocol_set(ctx, "h2");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
||||||
|
@@ -17,18 +17,16 @@
|
|||||||
#define __mod_h2__h2_h2__
|
#define __mod_h2__h2_h2__
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of ALPN protocol identifiers that we support in ALPN/NPN
|
* List of ALPN protocol identifiers that we suport in cleartext
|
||||||
* negotiations.
|
* negotiations. NULL terminated.
|
||||||
*/
|
*/
|
||||||
extern const char *h2_alpn_protos[];
|
extern const char *h2_clear_protos[];
|
||||||
extern apr_size_t h2_alpn_protos_len;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of ALPN protocol identifiers that we suport in HTTP/1 Upgrade:
|
* List of ALPN protocol identifiers that we support in TLS encrypted
|
||||||
* negotiations.
|
* negotiations. NULL terminated.
|
||||||
*/
|
*/
|
||||||
extern const char *h2_upgrade_protos[];
|
extern const char *h2_tls_protos[];
|
||||||
extern apr_size_t h2_upgrade_protos_len;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The magic PRIamble of RFC 7540 that is always sent when starting
|
* The magic PRIamble of RFC 7540 that is always sent when starting
|
||||||
|
@@ -199,6 +199,7 @@ apr_status_t h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait)
|
|||||||
apr_thread_cond_timedwait(wait, m->lock, apr_time_from_sec(10));
|
apr_thread_cond_timedwait(wait, m->lock, apr_time_from_sec(10));
|
||||||
if (++attempts >= 6) {
|
if (++attempts >= 6) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c,
|
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c,
|
||||||
|
APLOGNO(02952)
|
||||||
"h2_mplx(%ld): join attempts exhausted, refs=%d",
|
"h2_mplx(%ld): join attempts exhausted, refs=%d",
|
||||||
m->id, m->refs);
|
m->id, m->refs);
|
||||||
break;
|
break;
|
||||||
@@ -497,7 +498,7 @@ h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_NOTFOUND, m->c,
|
ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_NOTFOUND, m->c,
|
||||||
"h2_mplx(%ld): stream for response %d",
|
APLOGNO(02953) "h2_mplx(%ld): stream for response %d",
|
||||||
m->id, response->stream_id);
|
m->id, response->stream_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -785,7 +786,7 @@ apr_status_t h2_mplx_create_task(h2_mplx *m, struct h2_stream *stream)
|
|||||||
conn_rec *c = h2_conn_create(m->c, stream->pool);
|
conn_rec *c = h2_conn_create(m->c, stream->pool);
|
||||||
if (c == NULL) {
|
if (c == NULL) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, m->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, m->c,
|
||||||
"h2_mplx(%ld-%d): start stream",
|
APLOGNO(02916) "h2_mplx(%ld-%d): start stream",
|
||||||
m->id, stream->id);
|
m->id, stream->id);
|
||||||
return APR_ENOMEM;
|
return APR_ENOMEM;
|
||||||
}
|
}
|
||||||
|
@@ -92,6 +92,7 @@ apr_status_t h2_request_write_header(h2_request *req,
|
|||||||
/* pseudo header, see ch. 8.1.2.3, always should come first */
|
/* pseudo header, see ch. 8.1.2.3, always should come first */
|
||||||
if (req->to_h1) {
|
if (req->to_h1) {
|
||||||
ap_log_perror(APLOG_MARK, APLOG_ERR, 0, req->pool,
|
ap_log_perror(APLOG_MARK, APLOG_ERR, 0, req->pool,
|
||||||
|
APLOGNO(02917)
|
||||||
"h2_request(%d): pseudo header after request start",
|
"h2_request(%d): pseudo header after request start",
|
||||||
req->id);
|
req->id);
|
||||||
return APR_EGENERAL;
|
return APR_EGENERAL;
|
||||||
@@ -118,6 +119,7 @@ apr_status_t h2_request_write_header(h2_request *req,
|
|||||||
memset(buffer, 0, 32);
|
memset(buffer, 0, 32);
|
||||||
strncpy(buffer, name, (nlen > 31)? 31 : nlen);
|
strncpy(buffer, name, (nlen > 31)? 31 : nlen);
|
||||||
ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, req->pool,
|
ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, req->pool,
|
||||||
|
APLOGNO(02954)
|
||||||
"h2_request(%d): ignoring unknown pseudo header %s",
|
"h2_request(%d): ignoring unknown pseudo header %s",
|
||||||
req->id, buffer);
|
req->id, buffer);
|
||||||
}
|
}
|
||||||
|
@@ -61,7 +61,7 @@ h2_response *h2_response_create(int stream_id,
|
|||||||
char *sep = strchr(hline, ':');
|
char *sep = strchr(hline, ':');
|
||||||
if (!sep) {
|
if (!sep) {
|
||||||
ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
|
ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
|
||||||
"h2_response(%d): invalid header[%d] '%s'",
|
APLOGNO(02955) "h2_response(%d): invalid header[%d] '%s'",
|
||||||
response->stream_id, i, (char*)hline);
|
response->stream_id, i, (char*)hline);
|
||||||
/* not valid format, abort */
|
/* not valid format, abort */
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -80,7 +80,8 @@ h2_response *h2_response_create(int stream_id,
|
|||||||
response->content_length = apr_strtoi64(sep, &end, 10);
|
response->content_length = apr_strtoi64(sep, &end, 10);
|
||||||
if (sep == end) {
|
if (sep == end) {
|
||||||
ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL,
|
ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL,
|
||||||
pool, "h2_response(%d): content-length"
|
pool, APLOGNO(02956)
|
||||||
|
"h2_response(%d): content-length"
|
||||||
" value not parsed: %s",
|
" value not parsed: %s",
|
||||||
response->stream_id, sep);
|
response->stream_id, sep);
|
||||||
response->content_length = -1;
|
response->content_length = -1;
|
||||||
@@ -199,7 +200,7 @@ static int add_header(void *ctx, const char *key, const char *value)
|
|||||||
if (!ignore_header(key)) {
|
if (!ignore_header(key)) {
|
||||||
nvctx_t *nvctx = (nvctx_t*)ctx;
|
nvctx_t *nvctx = (nvctx_t*)ctx;
|
||||||
if (nvctx->debug) {
|
if (nvctx->debug) {
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_EINVAL,
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL,
|
||||||
nvctx->r, "h2_response(%d) header -> %s: %s",
|
nvctx->r, "h2_response(%d) header -> %s: %s",
|
||||||
nvctx->response->stream_id, key, value);
|
nvctx->response->stream_id, key, value);
|
||||||
}
|
}
|
||||||
|
@@ -73,6 +73,7 @@ static int stream_open(h2_session *session, int stream_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, session->c,
|
||||||
|
APLOGNO(02918)
|
||||||
"h2_session: stream(%ld-%d): unable to create",
|
"h2_session: stream(%ld-%d): unable to create",
|
||||||
session->id, stream_id);
|
session->id, stream_id);
|
||||||
return NGHTTP2_ERR_INVALID_STREAM_ID;
|
return NGHTTP2_ERR_INVALID_STREAM_ID;
|
||||||
@@ -145,6 +146,7 @@ static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags,
|
|||||||
stream = h2_stream_set_get(session->streams, stream_id);
|
stream = h2_stream_set_get(session->streams, stream_id);
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
|
||||||
|
APLOGNO(02919)
|
||||||
"h2_session: stream(%ld-%d): on_data_chunk for unknown stream",
|
"h2_session: stream(%ld-%d): on_data_chunk for unknown stream",
|
||||||
session->id, (int)stream_id);
|
session->id, (int)stream_id);
|
||||||
rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
|
rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
|
||||||
@@ -279,6 +281,7 @@ static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame,
|
|||||||
frame->hd.stream_id);
|
frame->hd.stream_id);
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
|
||||||
|
APLOGNO(02920)
|
||||||
"h2_session: stream(%ld-%d): on_header for unknown stream",
|
"h2_session: stream(%ld-%d): on_header for unknown stream",
|
||||||
session->id, (int)frame->hd.stream_id);
|
session->id, (int)frame->hd.stream_id);
|
||||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||||
@@ -320,6 +323,7 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
|
|||||||
frame->hd.stream_id);
|
frame->hd.stream_id);
|
||||||
if (stream == NULL) {
|
if (stream == NULL) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
|
||||||
|
APLOGNO(02921)
|
||||||
"h2_session: stream(%ld-%d): HEADERS frame "
|
"h2_session: stream(%ld-%d): HEADERS frame "
|
||||||
"for unknown stream", session->id,
|
"for unknown stream", session->id,
|
||||||
(int)frame->hd.stream_id);
|
(int)frame->hd.stream_id);
|
||||||
@@ -342,6 +346,7 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
|
|||||||
frame->hd.stream_id);
|
frame->hd.stream_id);
|
||||||
if (stream == NULL) {
|
if (stream == NULL) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
|
||||||
|
APLOGNO(02922)
|
||||||
"h2_session: stream(%ld-%d): DATA frame "
|
"h2_session: stream(%ld-%d): DATA frame "
|
||||||
"for unknown stream", session->id,
|
"for unknown stream", session->id,
|
||||||
(int)frame->hd.stream_id);
|
(int)frame->hd.stream_id);
|
||||||
@@ -394,6 +399,7 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
|
|||||||
|
|
||||||
if (status != APR_SUCCESS) {
|
if (status != APR_SUCCESS) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
||||||
|
APLOGNO(02923)
|
||||||
"h2_session: stream(%ld-%d): error handling frame",
|
"h2_session: stream(%ld-%d): error handling frame",
|
||||||
session->id, (int)frame->hd.stream_id);
|
session->id, (int)frame->hd.stream_id);
|
||||||
rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
|
||||||
@@ -443,6 +449,7 @@ static int on_send_data_cb(nghttp2_session *ngh2,
|
|||||||
stream = h2_stream_set_get(session->streams, stream_id);
|
stream = h2_stream_set_get(session->streams, stream_id);
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
|
||||||
|
APLOGNO(02924)
|
||||||
"h2_stream(%ld-%d): send_data",
|
"h2_stream(%ld-%d): send_data",
|
||||||
session->id, (int)stream_id);
|
session->id, (int)stream_id);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
@@ -477,6 +484,7 @@ static int on_send_data_cb(nghttp2_session *ngh2,
|
|||||||
}
|
}
|
||||||
else if (status != APR_EOF) {
|
else if (status != APR_EOF) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
||||||
|
APLOGNO(02925)
|
||||||
"h2_stream(%ld-%d): failed send_data_cb",
|
"h2_stream(%ld-%d): failed send_data_cb",
|
||||||
session->id, (int)stream_id);
|
session->id, (int)stream_id);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
@@ -494,7 +502,7 @@ static apr_status_t init_callbacks(conn_rec *c, nghttp2_session_callbacks **pcb)
|
|||||||
int rv = nghttp2_session_callbacks_new(pcb);
|
int rv = nghttp2_session_callbacks_new(pcb);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
|
||||||
"nghttp2_session_callbacks_new: %s",
|
APLOGNO(02926) "nghttp2_session_callbacks_new: %s",
|
||||||
nghttp2_strerror(rv));
|
nghttp2_strerror(rv));
|
||||||
return APR_EGENERAL;
|
return APR_EGENERAL;
|
||||||
}
|
}
|
||||||
@@ -556,7 +564,7 @@ static h2_session *h2_session_create_int(conn_rec *c,
|
|||||||
|
|
||||||
status = init_callbacks(c, &callbacks);
|
status = init_callbacks(c, &callbacks);
|
||||||
if (status != APR_SUCCESS) {
|
if (status != APR_SUCCESS) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c, APLOGNO(02927)
|
||||||
"nghttp2: error in init_callbacks");
|
"nghttp2: error in init_callbacks");
|
||||||
h2_session_destroy(session);
|
h2_session_destroy(session);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -565,7 +573,8 @@ static h2_session *h2_session_create_int(conn_rec *c,
|
|||||||
rv = nghttp2_option_new(&options);
|
rv = nghttp2_option_new(&options);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
|
||||||
"nghttp2_option_new: %s", nghttp2_strerror(rv));
|
APLOGNO(02928) "nghttp2_option_new: %s",
|
||||||
|
nghttp2_strerror(rv));
|
||||||
h2_session_destroy(session);
|
h2_session_destroy(session);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -584,7 +593,7 @@ static h2_session *h2_session_create_int(conn_rec *c,
|
|||||||
|
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
|
||||||
"nghttp2_session_server_new: %s",
|
APLOGNO(02929) "nghttp2_session_server_new: %s",
|
||||||
nghttp2_strerror(rv));
|
nghttp2_strerror(rv));
|
||||||
h2_session_destroy(session);
|
h2_session_destroy(session);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -660,7 +669,7 @@ apr_status_t h2_session_goaway(h2_session *session, apr_status_t reason)
|
|||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
status = APR_EGENERAL;
|
status = APR_EGENERAL;
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
||||||
"session(%ld): submit goaway: %s",
|
APLOGNO(02930) "session(%ld): submit goaway: %s",
|
||||||
session->id, nghttp2_strerror(rv));
|
session->id, nghttp2_strerror(rv));
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
@@ -733,6 +742,7 @@ apr_status_t h2_session_start(h2_session *session, int *rv)
|
|||||||
s = apr_table_get(session->r->headers_in, "HTTP2-Settings");
|
s = apr_table_get(session->r->headers_in, "HTTP2-Settings");
|
||||||
if (!s) {
|
if (!s) {
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, session->r,
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, session->r,
|
||||||
|
APLOGNO(02931)
|
||||||
"HTTP2-Settings header missing in request");
|
"HTTP2-Settings header missing in request");
|
||||||
return APR_EINVAL;
|
return APR_EINVAL;
|
||||||
}
|
}
|
||||||
@@ -751,7 +761,8 @@ apr_status_t h2_session_start(h2_session *session, int *rv)
|
|||||||
if (*rv != 0) {
|
if (*rv != 0) {
|
||||||
status = APR_EINVAL;
|
status = APR_EINVAL;
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
|
||||||
"nghttp2_session_upgrade: %s", nghttp2_strerror(*rv));
|
APLOGNO(02932) "nghttp2_session_upgrade: %s",
|
||||||
|
nghttp2_strerror(*rv));
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -760,7 +771,8 @@ apr_status_t h2_session_start(h2_session *session, int *rv)
|
|||||||
if (*rv != 0) {
|
if (*rv != 0) {
|
||||||
status = APR_EGENERAL;
|
status = APR_EGENERAL;
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
|
||||||
"open stream 1: %s", nghttp2_strerror(*rv));
|
APLOGNO(02933) "open stream 1: %s",
|
||||||
|
nghttp2_strerror(*rv));
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,7 +780,7 @@ apr_status_t h2_session_start(h2_session *session, int *rv)
|
|||||||
if (stream == NULL) {
|
if (stream == NULL) {
|
||||||
status = APR_EGENERAL;
|
status = APR_EGENERAL;
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
|
||||||
"lookup of stream 1");
|
APLOGNO(02934) "lookup of stream 1");
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,7 +807,8 @@ apr_status_t h2_session_start(h2_session *session, int *rv)
|
|||||||
if (*rv != 0) {
|
if (*rv != 0) {
|
||||||
status = APR_EGENERAL;
|
status = APR_EGENERAL;
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
||||||
"nghttp2_submit_settings: %s", nghttp2_strerror(*rv));
|
APLOGNO(02935) "nghttp2_submit_settings: %s",
|
||||||
|
nghttp2_strerror(*rv));
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
@@ -826,6 +839,7 @@ static int resume_on_data(void *ctx, h2_stream *stream) {
|
|||||||
rv = nghttp2_session_resume_data(session->ngh2, stream->id);
|
rv = nghttp2_session_resume_data(session->ngh2, stream->id);
|
||||||
ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)?
|
ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)?
|
||||||
APLOG_ERR : APLOG_DEBUG, 0, session->c,
|
APLOG_ERR : APLOG_DEBUG, 0, session->c,
|
||||||
|
APLOGNO(02936)
|
||||||
"h2_stream(%ld-%d): resuming stream %s",
|
"h2_stream(%ld-%d): resuming stream %s",
|
||||||
session->id, stream->id, nghttp2_strerror(rv));
|
session->id, stream->id, nghttp2_strerror(rv));
|
||||||
}
|
}
|
||||||
@@ -1002,6 +1016,7 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
|
|||||||
stream = h2_stream_set_get(session->streams, stream_id);
|
stream = h2_stream_set_get(session->streams, stream_id);
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
|
||||||
|
APLOGNO(02937)
|
||||||
"h2_stream(%ld-%d): data requested but stream not found",
|
"h2_stream(%ld-%d): data requested but stream not found",
|
||||||
session->id, (int)stream_id);
|
session->id, (int)stream_id);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
@@ -1038,7 +1053,7 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
|
|||||||
default:
|
default:
|
||||||
nread = 0;
|
nread = 0;
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
||||||
"h2_stream(%ld-%d): reading data",
|
APLOGNO(02938) "h2_stream(%ld-%d): reading data",
|
||||||
session->id, (int)stream_id);
|
session->id, (int)stream_id);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -1074,7 +1089,7 @@ static int submit_response(h2_session *session, h2_response *response)
|
|||||||
|
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
|
||||||
"h2_stream(%ld-%d): submit_response: %s",
|
APLOGNO(02939) "h2_stream(%ld-%d): submit_response: %s",
|
||||||
session->id, response->stream_id, nghttp2_strerror(rv));
|
session->id, response->stream_id, nghttp2_strerror(rv));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1110,7 +1125,8 @@ apr_status_t h2_session_handle_response(h2_session *session, h2_stream *stream)
|
|||||||
status = APR_EGENERAL;
|
status = APR_EGENERAL;
|
||||||
h2_session_abort_int(session, rv);
|
h2_session_abort_int(session, rv);
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
|
||||||
"submit_response: %s", nghttp2_strerror(rv));
|
APLOGNO(02940) "submit_response: %s",
|
||||||
|
nghttp2_strerror(rv));
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
201
modules/http2/h2_switch.c
Normal file
201
modules/http2/h2_switch.c
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <apr_strings.h>
|
||||||
|
#include <apr_optional.h>
|
||||||
|
#include <apr_optional_hooks.h>
|
||||||
|
|
||||||
|
#include <httpd.h>
|
||||||
|
#include <http_core.h>
|
||||||
|
#include <http_config.h>
|
||||||
|
#include <http_connection.h>
|
||||||
|
#include <http_protocol.h>
|
||||||
|
#include <http_log.h>
|
||||||
|
|
||||||
|
#include "h2_private.h"
|
||||||
|
|
||||||
|
#include "h2_config.h"
|
||||||
|
#include "h2_ctx.h"
|
||||||
|
#include "h2_conn.h"
|
||||||
|
#include "h2_h2.h"
|
||||||
|
#include "h2_switch.h"
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* SSL var lookup
|
||||||
|
*/
|
||||||
|
APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
|
||||||
|
(apr_pool_t *, server_rec *,
|
||||||
|
conn_rec *, request_rec *,
|
||||||
|
char *));
|
||||||
|
static char *(*opt_ssl_var_lookup)(apr_pool_t *, server_rec *,
|
||||||
|
conn_rec *, request_rec *,
|
||||||
|
char *);
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Once per lifetime init, retrieve optional functions
|
||||||
|
*/
|
||||||
|
apr_status_t h2_switch_init(apr_pool_t *pool, server_rec *s)
|
||||||
|
{
|
||||||
|
(void)pool;
|
||||||
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "h2_switch init");
|
||||||
|
opt_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
|
||||||
|
|
||||||
|
return APR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *const mod_ssl[] = { "mod_ssl.c", NULL};
|
||||||
|
static const char *const mod_core[] = { "core.c", NULL};
|
||||||
|
|
||||||
|
static int h2_util_array_index(const apr_array_header_t *array, const char *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < array->nelts; i++) {
|
||||||
|
const char *p = APR_ARRAY_IDX(array, i, const char*);
|
||||||
|
if (!strcmp(p, s)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int h2_protocol_propose(conn_rec *c, request_rec *r,
|
||||||
|
server_rec *s,
|
||||||
|
const apr_array_header_t *offers,
|
||||||
|
apr_array_header_t *proposals)
|
||||||
|
{
|
||||||
|
h2_config *cfg;
|
||||||
|
int proposed = 0;
|
||||||
|
const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos;
|
||||||
|
|
||||||
|
if (strcmp(AP_PROTOCOL_HTTP1, ap_run_protocol_get(c))) {
|
||||||
|
/* We do not know how to switch from anything else but http/1.1.
|
||||||
|
*/
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
|
||||||
|
"protocol switch: current proto != http/1.1, declined");
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg = h2_config_sget(s);
|
||||||
|
|
||||||
|
if (!h2_config_geti(cfg, H2_CONF_ENABLED)) {
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
|
||||||
|
"protocol propose, h2 disabled for config %s", cfg->name);
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r) {
|
||||||
|
const char *p;
|
||||||
|
/* So far, this indicates an HTTP/1 Upgrade header initiated
|
||||||
|
* protocol switch. For that, the HTTP2-Settings header needs
|
||||||
|
* to be present and valid for the connection.
|
||||||
|
*/
|
||||||
|
p = apr_table_get(r->headers_in, "HTTP2-Settings");
|
||||||
|
if (!p) {
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
|
||||||
|
"upgrade without HTTP2-Settings declined");
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = apr_table_get(r->headers_in, "Connection");
|
||||||
|
if (!ap_find_token(r->pool, p, "http2-settings")) {
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
|
||||||
|
"upgrade without HTTP2-Settings declined");
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We also allow switching only for requests that have no body.
|
||||||
|
*/
|
||||||
|
p = apr_table_get(r->headers_in, "Content-Length");
|
||||||
|
if (p && strcmp(p, "0")) {
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
|
||||||
|
"upgrade with content-length: %s, declined", p);
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*protos) {
|
||||||
|
/* Add all protocols we know (tls or clear) and that
|
||||||
|
* were offered as options for the switch.
|
||||||
|
*/
|
||||||
|
if (h2_util_array_index(offers, *protos) >= 0) {
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
||||||
|
"proposing protocol '%s'", *protos);
|
||||||
|
APR_ARRAY_PUSH(proposals, const char*) = *protos;
|
||||||
|
proposed = 1;
|
||||||
|
}
|
||||||
|
++protos;
|
||||||
|
}
|
||||||
|
return proposed? DECLINED : OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
|
||||||
|
const char *protocol)
|
||||||
|
{
|
||||||
|
int found = 0;
|
||||||
|
const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos;
|
||||||
|
const char **p = protos;
|
||||||
|
|
||||||
|
while (*p) {
|
||||||
|
if (!strcmp(*p, protocol)) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
h2_ctx *ctx = h2_ctx_get(c);
|
||||||
|
h2_ctx_protocol_set(ctx, protocol);
|
||||||
|
|
||||||
|
if (r != NULL) {
|
||||||
|
/* Switching in the middle of a request means that
|
||||||
|
* we have to send out the response to this one in h2
|
||||||
|
* format. So we need to take over the connection
|
||||||
|
* right away.
|
||||||
|
*/
|
||||||
|
ap_remove_input_filter_byhandle(r->input_filters, "http_in");
|
||||||
|
ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout");
|
||||||
|
|
||||||
|
/* Ok, start an h2_conn on this one. */
|
||||||
|
apr_status_t status = h2_conn_rprocess(r);
|
||||||
|
if (status != DONE) {
|
||||||
|
/* Nothing really to do about this. */
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
|
||||||
|
"session proessed, unexpected status");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
}
|
||||||
|
return DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *h2_protocol_get(const conn_rec *c)
|
||||||
|
{
|
||||||
|
return h2_ctx_protocol_get(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void h2_switch_register_hooks(void)
|
||||||
|
{
|
||||||
|
ap_hook_protocol_propose(h2_protocol_propose, NULL, NULL, APR_HOOK_MIDDLE);
|
||||||
|
ap_hook_protocol_switch(h2_protocol_switch, NULL, NULL, APR_HOOK_MIDDLE);
|
||||||
|
ap_hook_protocol_get(h2_protocol_get, NULL, NULL, APR_HOOK_MIDDLE);
|
||||||
|
}
|
||||||
|
|
@@ -13,17 +13,17 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __mod_h2__h2_alpn__
|
#ifndef __mod_h2__h2_switch__
|
||||||
#define __mod_h2__h2_alpn__
|
#define __mod_h2__h2_switch__
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* One time, post config intialization.
|
* One time, post config intialization.
|
||||||
*/
|
*/
|
||||||
apr_status_t h2_alpn_init(apr_pool_t *pool, server_rec *s);
|
apr_status_t h2_switch_init(apr_pool_t *pool, server_rec *s);
|
||||||
|
|
||||||
/* Register apache hooks for ALPN protocol
|
/* Register apache hooks for protocol switching
|
||||||
*/
|
*/
|
||||||
void h2_alpn_register_hooks(void);
|
void h2_switch_register_hooks(void);
|
||||||
|
|
||||||
|
|
||||||
#endif /* defined(__mod_h2__h2_h2__) */
|
#endif /* defined(__mod_h2__h2_switch__) */
|
@@ -162,7 +162,7 @@ h2_task *h2_task_create(long session_id,
|
|||||||
h2_task *task = apr_pcalloc(stream_pool, sizeof(h2_task));
|
h2_task *task = apr_pcalloc(stream_pool, sizeof(h2_task));
|
||||||
if (task == NULL) {
|
if (task == NULL) {
|
||||||
ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, stream_pool,
|
ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, stream_pool,
|
||||||
"h2_task(%ld-%d): create stream task",
|
APLOGNO(02941) "h2_task(%ld-%d): create stream task",
|
||||||
session_id, stream_id);
|
session_id, stream_id);
|
||||||
h2_mplx_out_close(mplx, stream_id);
|
h2_mplx_out_close(mplx, stream_id);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -256,7 +256,8 @@ apr_status_t h2_task_do(h2_task *task, h2_worker *worker)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, &env.c,
|
ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, &env.c,
|
||||||
"h2_task(%s): error setting up h2_task_env", env.id);
|
APLOGNO(02957) "h2_task(%s): error setting up h2_task_env",
|
||||||
|
env.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env.input) {
|
if (env.input) {
|
||||||
|
@@ -117,7 +117,7 @@ apr_status_t h2_task_input_read(h2_task_input *input,
|
|||||||
status = apr_brigade_length(input->bb, 1, &bblen);
|
status = apr_brigade_length(input->bb, 1, &bblen);
|
||||||
if (status != APR_SUCCESS) {
|
if (status != APR_SUCCESS) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, f->c,
|
ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, f->c,
|
||||||
"h2_task_input(%s): brigade length fail",
|
APLOGNO(02958) "h2_task_input(%s): brigade length fail",
|
||||||
input->env->id);
|
input->env->id);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -201,8 +201,8 @@ apr_status_t h2_task_input_read(h2_task_input *input,
|
|||||||
/* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not
|
/* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not
|
||||||
* to support it. Seems to work. */
|
* to support it. Seems to work. */
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c,
|
||||||
"h2_task_input, unsupported READ mode %d",
|
APLOGNO(02942)
|
||||||
mode);
|
"h2_task_input, unsupported READ mode %d", mode);
|
||||||
return APR_ENOTIMPL;
|
return APR_ENOTIMPL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,11 +39,13 @@ h2_to_h1 *h2_to_h1_create(int stream_id, apr_pool_t *pool,
|
|||||||
h2_to_h1 *to_h1;
|
h2_to_h1 *to_h1;
|
||||||
if (!method) {
|
if (!method) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, m->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, m->c,
|
||||||
|
APLOGNO(02943)
|
||||||
"h2_to_h1: header start but :method missing");
|
"h2_to_h1: header start but :method missing");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!path) {
|
if (!path) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, m->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, m->c,
|
||||||
|
APLOGNO(02944)
|
||||||
"h2_to_h1: header start but :path missing");
|
"h2_to_h1: header start but :path missing");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -78,6 +80,7 @@ apr_status_t h2_to_h1_add_header(h2_to_h1 *to_h1,
|
|||||||
if (!apr_strnatcasecmp("chunked", value)) {
|
if (!apr_strnatcasecmp("chunked", value)) {
|
||||||
/* This should never arrive here in a HTTP/2 request */
|
/* This should never arrive here in a HTTP/2 request */
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_BADARG, to_h1->m->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_BADARG, to_h1->m->c,
|
||||||
|
APLOGNO(02945)
|
||||||
"h2_to_h1: 'transfer-encoding: chunked' received");
|
"h2_to_h1: 'transfer-encoding: chunked' received");
|
||||||
return APR_BADARG;
|
return APR_BADARG;
|
||||||
}
|
}
|
||||||
@@ -87,6 +90,7 @@ apr_status_t h2_to_h1_add_header(h2_to_h1 *to_h1,
|
|||||||
to_h1->content_len = apr_strtoi64(value, &end, 10);
|
to_h1->content_len = apr_strtoi64(value, &end, 10);
|
||||||
if (value == end) {
|
if (value == end) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, to_h1->m->c,
|
ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, to_h1->m->c,
|
||||||
|
APLOGNO(02959)
|
||||||
"h2_request(%d): content-length value not parsed: %s",
|
"h2_request(%d): content-length value not parsed: %s",
|
||||||
to_h1->stream_id, value);
|
to_h1->stream_id, value);
|
||||||
return APR_EINVAL;
|
return APR_EINVAL;
|
||||||
@@ -187,6 +191,7 @@ apr_status_t h2_to_h1_end_headers(h2_to_h1 *to_h1, h2_task *task, int eos)
|
|||||||
apr_status_t status = h2_to_h1_close(to_h1);
|
apr_status_t status = h2_to_h1_close(to_h1);
|
||||||
if (status != APR_SUCCESS) {
|
if (status != APR_SUCCESS) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, to_h1->m->c,
|
ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, to_h1->m->c,
|
||||||
|
APLOGNO(02960)
|
||||||
"h2_to_h1(%ld-%d): end headers, eos=%d",
|
"h2_to_h1(%ld-%d): end headers, eos=%d",
|
||||||
to_h1->m->id, to_h1->stream_id, eos);
|
to_h1->m->id, to_h1->stream_id, eos);
|
||||||
}
|
}
|
||||||
@@ -243,6 +248,7 @@ apr_status_t h2_to_h1_add_data(h2_to_h1 *to_h1,
|
|||||||
to_h1->remain_len -= len;
|
to_h1->remain_len -= len;
|
||||||
if (to_h1->remain_len < 0) {
|
if (to_h1->remain_len < 0) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, to_h1->m->c,
|
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, to_h1->m->c,
|
||||||
|
APLOGNO(02961)
|
||||||
"h2_to_h1(%ld-%d): got %ld more content bytes than announced "
|
"h2_to_h1(%ld-%d): got %ld more content bytes than announced "
|
||||||
"in content-length header: %ld",
|
"in content-length header: %ld",
|
||||||
to_h1->m->id, to_h1->stream_id,
|
to_h1->m->id, to_h1->stream_id,
|
||||||
@@ -263,7 +269,7 @@ apr_status_t h2_to_h1_flush(h2_to_h1 *to_h1)
|
|||||||
status = h2_mplx_in_write(to_h1->m, to_h1->stream_id, to_h1->bb);
|
status = h2_mplx_in_write(to_h1->m, to_h1->stream_id, to_h1->bb);
|
||||||
if (status != APR_SUCCESS) {
|
if (status != APR_SUCCESS) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, to_h1->m->c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, to_h1->m->c,
|
||||||
"h2_request(%d): pushing request data",
|
APLOGNO(02946) "h2_request(%d): pushing request data",
|
||||||
to_h1->stream_id);
|
to_h1->stream_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,201 +0,0 @@
|
|||||||
/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include <apr_optional.h>
|
|
||||||
#include <apr_optional_hooks.h>
|
|
||||||
|
|
||||||
#include <ap_mpm.h>
|
|
||||||
#include <httpd.h>
|
|
||||||
#include <http_core.h>
|
|
||||||
#include <http_config.h>
|
|
||||||
#include <http_connection.h>
|
|
||||||
#include <http_log.h>
|
|
||||||
#include <http_protocol.h>
|
|
||||||
#include <http_request.h>
|
|
||||||
|
|
||||||
#include "h2_private.h"
|
|
||||||
#include "h2_conn.h"
|
|
||||||
#include "h2_config.h"
|
|
||||||
#include "h2_ctx.h"
|
|
||||||
#include "h2_h2.h"
|
|
||||||
#include "h2_upgrade.h"
|
|
||||||
#include "h2_util.h"
|
|
||||||
|
|
||||||
static int h2_upgrade_request_handler(request_rec *r);
|
|
||||||
static const char *h2_get_upgrade_proto(request_rec *r);
|
|
||||||
static int h2_upgrade_to(request_rec *r, const char *proto);
|
|
||||||
static int h2_upgrade_options(request_rec *r);
|
|
||||||
|
|
||||||
void h2_upgrade_register_hooks(void)
|
|
||||||
{
|
|
||||||
ap_hook_handler(h2_upgrade_request_handler, NULL, NULL, APR_HOOK_FIRST - 1);
|
|
||||||
ap_hook_map_to_storage(h2_upgrade_options, NULL, NULL, APR_HOOK_FIRST);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h2_upgrade_options(request_rec *r)
|
|
||||||
{
|
|
||||||
if ((r->method_number == M_OPTIONS) && r->uri && (r->uri[0] == '*') &&
|
|
||||||
(r->uri[1] == '\0')) {
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
|
|
||||||
"h2c: request OPTIONS * seen");
|
|
||||||
return h2_upgrade_request_handler(r);
|
|
||||||
}
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h2_upgrade_request_handler(request_rec *r)
|
|
||||||
{
|
|
||||||
h2_ctx *ctx = h2_ctx_rget(r);
|
|
||||||
h2_config *cfg = h2_config_rget(r);
|
|
||||||
int enabled_for_request = h2_config_geti(cfg, H2_CONF_ENABLED);
|
|
||||||
|
|
||||||
if (h2_ctx_is_task(ctx) || h2_ctx_is_active(ctx)) {
|
|
||||||
/* talking h2 already, either task for main conn */
|
|
||||||
if (!enabled_for_request) {
|
|
||||||
/* we have a request for a server (vhost) where h2 is
|
|
||||||
* not enabled. This happened over a connection on which
|
|
||||||
* we talk h2.
|
|
||||||
* Tell the client, she should open a new connection to that
|
|
||||||
* vhost to get fresh protocol negotiations.
|
|
||||||
*/
|
|
||||||
r->status = 421;
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r->status, r,
|
|
||||||
"421-ing h2 request to host %s", r->hostname);
|
|
||||||
return DONE;
|
|
||||||
}
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* not talking h2 (yet) */
|
|
||||||
if (enabled_for_request) {
|
|
||||||
/* Check for the start of an h2c Upgrade dance. */
|
|
||||||
const char *proto = h2_get_upgrade_proto(r);
|
|
||||||
if (proto) {
|
|
||||||
const char *clen;
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
|
|
||||||
"seeing %s upgrade invitation", proto);
|
|
||||||
/* We do not handle upgradeable requests with a body.
|
|
||||||
* The reason being that we would need to read the body in full
|
|
||||||
* before we ca use HTTP2 frames on the wire.
|
|
||||||
*
|
|
||||||
* This seems to be consensus among server implemntations and
|
|
||||||
* clients are advised to use an "OPTIONS *" before a POST.
|
|
||||||
*/
|
|
||||||
clen = apr_table_get(r->headers_in, "Content-Length");
|
|
||||||
if (clen && strcmp(clen, "0")) {
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
|
|
||||||
"upgrade with content-length: %s, declined", clen);
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
return h2_upgrade_to(r, proto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *h2_get_upgrade_proto(request_rec *r)
|
|
||||||
{
|
|
||||||
const char *proto, *conn;
|
|
||||||
const char *upgrade = apr_table_get(r->headers_in, "Upgrade");
|
|
||||||
|
|
||||||
if (upgrade && *upgrade) {
|
|
||||||
|
|
||||||
conn = apr_table_get(r->headers_in, "Connection");
|
|
||||||
if (h2_util_contains_token(r->pool, conn, "Upgrade")
|
|
||||||
&& apr_table_get(r->headers_in, "HTTP2-Settings")) {
|
|
||||||
|
|
||||||
/* HTTP/1 Upgrade: is just another mechanism to switch
|
|
||||||
* protocols on a connection, same as ALPN or NPN.
|
|
||||||
* Security desirability aside, the bit protocol spoken
|
|
||||||
* afterwards is the same. Why require different identifier?
|
|
||||||
*
|
|
||||||
* We allow the same tokens as in ALPN negotiation, plus the
|
|
||||||
* special 'c' variants that RFC 7540 defines. We just do not
|
|
||||||
* care about the transport here.
|
|
||||||
*/
|
|
||||||
proto = h2_util_first_token_match(r->pool, upgrade,
|
|
||||||
h2_alpn_protos,
|
|
||||||
h2_alpn_protos_len);
|
|
||||||
if (!proto) {
|
|
||||||
proto = h2_util_first_token_match(r->pool, upgrade,
|
|
||||||
h2_upgrade_protos,
|
|
||||||
h2_upgrade_protos_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto) {
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
|
|
||||||
"suiteable upgrade detected: %s %s, "
|
|
||||||
"Upgrade: %s", r->method, r->uri, upgrade);
|
|
||||||
return proto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h2_upgrade_to(request_rec *r, const char *proto)
|
|
||||||
{
|
|
||||||
conn_rec *c = r->connection;
|
|
||||||
h2_ctx *ctx = h2_ctx_rget(r);
|
|
||||||
apr_status_t status;
|
|
||||||
|
|
||||||
h2_ctx_pnego_set_done(ctx, proto);
|
|
||||||
|
|
||||||
/* Let the client know what we are upgrading to. */
|
|
||||||
apr_table_clear(r->headers_out);
|
|
||||||
apr_table_setn(r->headers_out, "Upgrade", proto);
|
|
||||||
apr_table_setn(r->headers_out, "Connection", "Upgrade");
|
|
||||||
|
|
||||||
r->status = HTTP_SWITCHING_PROTOCOLS;
|
|
||||||
r->status_line = ap_get_status_line(r->status);
|
|
||||||
ap_send_interim_response(r, 1);
|
|
||||||
|
|
||||||
/* Make sure the core filter that parses http1 requests does
|
|
||||||
* not mess with our http2 frames. */
|
|
||||||
if (APLOGrtrace2(r)) {
|
|
||||||
ap_filter_t *filter = r->input_filters;
|
|
||||||
while (filter) {
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
|
||||||
"h2_conn(%ld), has request filter %s",
|
|
||||||
r->connection->id, filter->frec->name);
|
|
||||||
filter = filter->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ap_remove_input_filter_byhandle(r->input_filters, "http_in");
|
|
||||||
ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout");
|
|
||||||
|
|
||||||
/* Ok, start an h2_conn on this one. */
|
|
||||||
status = h2_conn_rprocess(r);
|
|
||||||
if (status != DONE) {
|
|
||||||
/* Nothing really to do about this. */
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
|
|
||||||
"session proessed, unexpected status");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* make sure httpd closes the connection after this */
|
|
||||||
c->keepalive = AP_CONN_CLOSE;
|
|
||||||
ap_lingering_close(c);
|
|
||||||
|
|
||||||
if (c->sbh) {
|
|
||||||
ap_update_child_status_from_conn(c->sbh, SERVER_CLOSING, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
return DONE;
|
|
||||||
}
|
|
||||||
|
|
@@ -1,24 +0,0 @@
|
|||||||
/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __mod_h2__h2_upgrade__
|
|
||||||
#define __mod_h2__h2_upgrade__
|
|
||||||
|
|
||||||
/* Specific function to HTTP/1 connection Upgradeds.
|
|
||||||
*/
|
|
||||||
void h2_upgrade_register_hooks(void);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* defined(__mod_h2__h2_upgrade__) */
|
|
@@ -109,7 +109,7 @@ static const int BASE64URL_TABLE[] = {
|
|||||||
-1, -1, -1, -1
|
-1, -1, -1, -1
|
||||||
};
|
};
|
||||||
|
|
||||||
apr_size_t h2_util_base64url_decode(unsigned char **decoded, const char *encoded,
|
apr_size_t h2_util_base64url_decode(const char **decoded, const char *encoded,
|
||||||
apr_pool_t *pool)
|
apr_pool_t *pool)
|
||||||
{
|
{
|
||||||
const unsigned char *e = (const unsigned char *)encoded;
|
const unsigned char *e = (const unsigned char *)encoded;
|
||||||
@@ -126,7 +126,7 @@ apr_size_t h2_util_base64url_decode(unsigned char **decoded, const char *encoded
|
|||||||
*decoded = apr_pcalloc(pool, len+1);
|
*decoded = apr_pcalloc(pool, len+1);
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
d = *decoded;
|
d = (unsigned char*)*decoded;
|
||||||
for (; i < mlen; i += 4) {
|
for (; i < mlen; i += 4) {
|
||||||
n = ((BASE64URL_TABLE[ e[i+0] ] << 18) +
|
n = ((BASE64URL_TABLE[ e[i+0] ] << 18) +
|
||||||
(BASE64URL_TABLE[ e[i+1] ] << 12) +
|
(BASE64URL_TABLE[ e[i+1] ] << 12) +
|
||||||
@@ -359,7 +359,8 @@ apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from,
|
|||||||
status = apr_file_setaside(&fd, fd, to->p);
|
status = apr_file_setaside(&fd, fd, to->p);
|
||||||
if (status != APR_SUCCESS) {
|
if (status != APR_SUCCESS) {
|
||||||
ap_log_perror(APLOG_MARK, APLOG_ERR, status, to->p,
|
ap_log_perror(APLOG_MARK, APLOG_ERR, status, to->p,
|
||||||
"h2_util: %s, setaside FILE", msg);
|
APLOGNO(02947) "h2_util: %s, setaside FILE",
|
||||||
|
msg);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -42,7 +42,7 @@ const char *h2_util_first_token_match(apr_pool_t *pool, const char *s,
|
|||||||
* I always wanted to write my own base64url decoder...not. See
|
* I always wanted to write my own base64url decoder...not. See
|
||||||
* https://tools.ietf.org/html/rfc4648#section-5 for description.
|
* https://tools.ietf.org/html/rfc4648#section-5 for description.
|
||||||
*/
|
*/
|
||||||
apr_size_t h2_util_base64url_decode(unsigned char **decoded,
|
apr_size_t h2_util_base64url_decode(const char **decoded,
|
||||||
const char *encoded,
|
const char *encoded,
|
||||||
apr_pool_t *pool);
|
apr_pool_t *pool);
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
* @macro
|
* @macro
|
||||||
* Version number of the h2 module as c string
|
* Version number of the h2 module as c string
|
||||||
*/
|
*/
|
||||||
#define MOD_H2_VERSION "0.8.1"
|
#define MOD_H2_VERSION "1.0.0"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @macro
|
* @macro
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
* release. This is a 24 bit number with 8 bits for major number, 8 bits
|
* release. This is a 24 bit number with 8 bits for major number, 8 bits
|
||||||
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
|
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
|
||||||
*/
|
*/
|
||||||
#define MOD_H2_VERSION_NUM 0x000801
|
#define MOD_H2_VERSION_NUM 0x010000
|
||||||
|
|
||||||
|
|
||||||
#endif /* mod_h2_h2_version_h */
|
#endif /* mod_h2_h2_version_h */
|
||||||
|
@@ -41,7 +41,8 @@ static void *execute(apr_thread_t *thread, void *wctx)
|
|||||||
APR_PROTO_TCP, worker->pool);
|
APR_PROTO_TCP, worker->pool);
|
||||||
if (status != APR_SUCCESS) {
|
if (status != APR_SUCCESS) {
|
||||||
ap_log_perror(APLOG_MARK, APLOG_ERR, status, worker->pool,
|
ap_log_perror(APLOG_MARK, APLOG_ERR, status, worker->pool,
|
||||||
"h2_worker(%d): alloc socket", worker->id);
|
APLOGNO(02948) "h2_worker(%d): alloc socket",
|
||||||
|
worker->id);
|
||||||
worker->worker_done(worker, worker->ctx);
|
worker->worker_done(worker, worker->ctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@@ -343,7 +343,7 @@ void h2_workers_set_max_idle_secs(h2_workers *workers, int idle_secs)
|
|||||||
{
|
{
|
||||||
if (idle_secs <= 0) {
|
if (idle_secs <= 0) {
|
||||||
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, workers->s,
|
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, workers->s,
|
||||||
"h2_workers: max_worker_idle_sec value of %d"
|
APLOGNO(02962) "h2_workers: max_worker_idle_sec value of %d"
|
||||||
" is not valid, ignored.", idle_secs);
|
" is not valid, ignored.", idle_secs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -31,8 +31,7 @@
|
|||||||
#include "h2_config.h"
|
#include "h2_config.h"
|
||||||
#include "h2_ctx.h"
|
#include "h2_ctx.h"
|
||||||
#include "h2_h2.h"
|
#include "h2_h2.h"
|
||||||
#include "h2_alpn.h"
|
#include "h2_switch.h"
|
||||||
#include "h2_upgrade.h"
|
|
||||||
#include "h2_version.h"
|
#include "h2_version.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -94,14 +93,14 @@ static int h2_post_config(apr_pool_t *p, apr_pool_t *plog,
|
|||||||
break;
|
break;
|
||||||
case H2_MPM_UNKNOWN:
|
case H2_MPM_UNKNOWN:
|
||||||
/* ??? */
|
/* ??? */
|
||||||
ap_log_error( APLOG_MARK, APLOG_ERR, 0, s,
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
|
||||||
"post_config: mpm type unknown");
|
"post_config: mpm type unknown");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = h2_h2_init(p, s);
|
status = h2_h2_init(p, s);
|
||||||
if (status == APR_SUCCESS) {
|
if (status == APR_SUCCESS) {
|
||||||
status = h2_alpn_init(p, s);
|
status = h2_switch_init(p, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
@@ -116,15 +115,10 @@ static void h2_child_init(apr_pool_t *pool, server_rec *s)
|
|||||||
apr_status_t status = h2_conn_child_init(pool, s);
|
apr_status_t status = h2_conn_child_init(pool, s);
|
||||||
if (status != APR_SUCCESS) {
|
if (status != APR_SUCCESS) {
|
||||||
ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
|
ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
|
||||||
"initializing connection handling");
|
APLOGNO(02949) "initializing connection handling");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *h2_get_protocol(conn_rec *c)
|
|
||||||
{
|
|
||||||
return h2_ctx_pnego_get(h2_ctx_get(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Install this module into the apache2 infrastructure.
|
/* Install this module into the apache2 infrastructure.
|
||||||
*/
|
*/
|
||||||
static void h2_hooks(apr_pool_t *pool)
|
static void h2_hooks(apr_pool_t *pool)
|
||||||
@@ -142,16 +136,11 @@ static void h2_hooks(apr_pool_t *pool)
|
|||||||
ap_hook_child_init(h2_child_init, NULL, NULL, APR_HOOK_MIDDLE);
|
ap_hook_child_init(h2_child_init, NULL, NULL, APR_HOOK_MIDDLE);
|
||||||
|
|
||||||
h2_h2_register_hooks();
|
h2_h2_register_hooks();
|
||||||
h2_alpn_register_hooks();
|
h2_switch_register_hooks();
|
||||||
h2_upgrade_register_hooks();
|
|
||||||
h2_task_register_hooks();
|
h2_task_register_hooks();
|
||||||
|
|
||||||
h2_alt_svc_register_hooks();
|
h2_alt_svc_register_hooks();
|
||||||
|
|
||||||
/* We offer a function to other modules that lets them retrieve
|
|
||||||
* the h2 protocol used on a connection (if any).
|
|
||||||
*/
|
|
||||||
APR_REGISTER_OPTIONAL_FN(h2_get_protocol);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -16,13 +16,4 @@
|
|||||||
#ifndef mod_h2_mod_h2_h
|
#ifndef mod_h2_mod_h2_h
|
||||||
#define mod_h2_mod_h2_h
|
#define mod_h2_mod_h2_h
|
||||||
|
|
||||||
const char *h2_get_protocol(conn_rec *c);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An optional function which returns the h2 protocol used on the given
|
|
||||||
* connection and NULL if no h2* protocol is active on it.
|
|
||||||
*/
|
|
||||||
APR_DECLARE_OPTIONAL_FN(const char *, h2_get_protocol, (conn_rec*));
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -283,12 +283,6 @@ static const command_rec ssl_config_cmds[] = {
|
|||||||
"OpenSSL configuration command")
|
"OpenSSL configuration command")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_TLS_ALPN
|
|
||||||
SSL_CMD_SRV(ALPNPreference, ITERATE,
|
|
||||||
"Preference in Application-Layer Protocol Negotiation (ALPN), "
|
|
||||||
"protocols are chosen in the specified order")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Deprecated directives. */
|
/* Deprecated directives. */
|
||||||
AP_INIT_RAW_ARGS("SSLLog", ap_set_deprecated, NULL, OR_ALL,
|
AP_INIT_RAW_ARGS("SSLLog", ap_set_deprecated, NULL, OR_ALL,
|
||||||
"SSLLog directive is no longer supported - use ErrorLog."),
|
"SSLLog directive is no longer supported - use ErrorLog."),
|
||||||
@@ -451,37 +445,6 @@ static int ssl_engine_disable(conn_rec *c)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int modssl_register_alpn(conn_rec *c,
|
|
||||||
ssl_alpn_propose_protos advertisefn,
|
|
||||||
ssl_alpn_proto_negotiated negotiatedfn)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_TLS_ALPN
|
|
||||||
SSLConnRec *sslconn = myConnConfig(c);
|
|
||||||
|
|
||||||
if (!sslconn) {
|
|
||||||
return DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sslconn->alpn_proposefns) {
|
|
||||||
sslconn->alpn_proposefns =
|
|
||||||
apr_array_make(c->pool, 5, sizeof(ssl_alpn_propose_protos));
|
|
||||||
sslconn->alpn_negofns =
|
|
||||||
apr_array_make(c->pool, 5, sizeof(ssl_alpn_proto_negotiated));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (advertisefn)
|
|
||||||
APR_ARRAY_PUSH(sslconn->alpn_proposefns, ssl_alpn_propose_protos) =
|
|
||||||
advertisefn;
|
|
||||||
if (negotiatedfn)
|
|
||||||
APR_ARRAY_PUSH(sslconn->alpn_negofns, ssl_alpn_proto_negotiated) =
|
|
||||||
negotiatedfn;
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
#else
|
|
||||||
return DECLINED;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
|
int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
|
||||||
{
|
{
|
||||||
SSLSrvConfigRec *sc;
|
SSLSrvConfigRec *sc;
|
||||||
@@ -650,7 +613,6 @@ static void ssl_register_hooks(apr_pool_t *p)
|
|||||||
|
|
||||||
APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
|
APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
|
||||||
APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
|
APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
|
||||||
APR_REGISTER_OPTIONAL_FN(modssl_register_alpn);
|
|
||||||
|
|
||||||
ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl",
|
ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl",
|
||||||
AUTHZ_PROVIDER_VERSION,
|
AUTHZ_PROVIDER_VERSION,
|
||||||
|
@@ -93,46 +93,5 @@ APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
|
|||||||
|
|
||||||
APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
|
APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
|
||||||
|
|
||||||
/** The alpn_propose_proto callback allows other modules to propose
|
|
||||||
* the name of the protocol that will be chosen during the
|
|
||||||
* Application-Layer Protocol Negotiation (ALPN) portion of the SSL handshake.
|
|
||||||
* The callback is given the connection and a list of NULL-terminated
|
|
||||||
* protocol strings as supported by the client. If this client_protos is
|
|
||||||
* non-empty, it must pick its preferred protocol from that list. Otherwise
|
|
||||||
* it should add its supported protocols in order of precedence.
|
|
||||||
* The callback should not yet modify the connection or install any filters
|
|
||||||
* as its proposal(s) may be overridden by another callback or server
|
|
||||||
* configuration.
|
|
||||||
* It should return OK or, to prevent further processing of (other modules')
|
|
||||||
* callbacks, return DONE.
|
|
||||||
*/
|
|
||||||
typedef int (*ssl_alpn_propose_protos)(conn_rec *connection,
|
|
||||||
apr_array_header_t *client_protos,
|
|
||||||
apr_array_header_t *proposed_protos);
|
|
||||||
|
|
||||||
/** The alpn_proto_negotiated callback allows other modules to discover
|
|
||||||
* the name of the protocol that was chosen during the Application-Layer
|
|
||||||
* Protocol Negotiation (ALPN) portion of the SSL handshake.
|
|
||||||
* The callback is given the connection, a
|
|
||||||
* non-NUL-terminated string containing the protocol name, and the
|
|
||||||
* length of the string; it should do something appropriate
|
|
||||||
* (i.e. insert or remove filters) and return OK. To prevent further
|
|
||||||
* processing of (other modules') callbacks, return DONE. */
|
|
||||||
typedef int (*ssl_alpn_proto_negotiated)(conn_rec *connection,
|
|
||||||
const char *proto_name,
|
|
||||||
apr_size_t proto_name_len);
|
|
||||||
|
|
||||||
/* An optional function which can be used to register a pair of callbacks
|
|
||||||
* for ALPN handling.
|
|
||||||
* This optional function should be invoked from a pre_connection hook
|
|
||||||
* which runs *after* mod_ssl.c's pre_connection hook. The function returns
|
|
||||||
* OK if the callbacks are registered, or DECLINED otherwise (for example if
|
|
||||||
* mod_ssl does not support ALPN).
|
|
||||||
*/
|
|
||||||
APR_DECLARE_OPTIONAL_FN(int, modssl_register_alpn,
|
|
||||||
(conn_rec *conn,
|
|
||||||
ssl_alpn_propose_protos proposefn,
|
|
||||||
ssl_alpn_proto_negotiated negotiatedfn));
|
|
||||||
|
|
||||||
#endif /* __MOD_SSL_H__ */
|
#endif /* __MOD_SSL_H__ */
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@@ -161,9 +161,6 @@ static void modssl_ctx_init(modssl_ctx_t *mctx, apr_pool_t *p)
|
|||||||
SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_CERTIFICATE);
|
SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_CERTIFICATE);
|
||||||
mctx->ssl_ctx_param = apr_array_make(p, 5, sizeof(ssl_ctx_param_t));
|
mctx->ssl_ctx_param = apr_array_make(p, 5, sizeof(ssl_ctx_param_t));
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_TLS_ALPN
|
|
||||||
mctx->ssl_alpn_pref = apr_array_make(p, 5, sizeof(const char *));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void modssl_ctx_init_proxy(SSLSrvConfigRec *sc,
|
static void modssl_ctx_init_proxy(SSLSrvConfigRec *sc,
|
||||||
@@ -308,9 +305,6 @@ static void modssl_ctx_cfg_merge(apr_pool_t *p,
|
|||||||
#ifdef HAVE_SSL_CONF_CMD
|
#ifdef HAVE_SSL_CONF_CMD
|
||||||
cfgMergeArray(ssl_ctx_param);
|
cfgMergeArray(ssl_ctx_param);
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_TLS_ALPN
|
|
||||||
cfgMergeArray(ssl_alpn_pref);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void modssl_ctx_cfg_merge_proxy(apr_pool_t *p,
|
static void modssl_ctx_cfg_merge_proxy(apr_pool_t *p,
|
||||||
@@ -1863,16 +1857,6 @@ const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_TLS_ALPN
|
|
||||||
const char *ssl_cmd_SSLALPNPreference(cmd_parms *cmd, void *dcfg,
|
|
||||||
const char *protocol)
|
|
||||||
{
|
|
||||||
SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
|
|
||||||
APR_ARRAY_PUSH(sc->server->ssl_alpn_pref, const char *) = protocol;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SRP
|
#ifdef HAVE_SRP
|
||||||
|
|
||||||
const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg,
|
const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg,
|
||||||
|
@@ -1495,24 +1495,26 @@ static apr_status_t ssl_io_filter_input(ap_filter_t *f,
|
|||||||
SSLConnRec *sslconn = myConnConfig(f->c);
|
SSLConnRec *sslconn = myConnConfig(f->c);
|
||||||
const unsigned char *next_proto = NULL;
|
const unsigned char *next_proto = NULL;
|
||||||
unsigned next_proto_len = 0;
|
unsigned next_proto_len = 0;
|
||||||
|
const char *protocol;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (sslconn->alpn_negofns) {
|
|
||||||
SSL_get0_alpn_selected(inctx->ssl, &next_proto, &next_proto_len);
|
SSL_get0_alpn_selected(inctx->ssl, &next_proto, &next_proto_len);
|
||||||
|
if (next_proto && next_proto_len) {
|
||||||
|
protocol = apr_pstrmemdup(f->c->pool, (const char *)next_proto,
|
||||||
|
next_proto_len);
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
|
||||||
APLOGNO(02836) "ALPN selected protocol: '%s'",
|
APLOGNO(02836) "ALPN selected protocol: '%s'",
|
||||||
(next_proto && next_proto_len) ?
|
protocol);
|
||||||
apr_pstrmemdup(f->c->pool,
|
|
||||||
(const char *)next_proto,
|
|
||||||
next_proto_len) :
|
|
||||||
"(null)");
|
|
||||||
for (n = 0; n < sslconn->alpn_negofns->nelts; n++) {
|
|
||||||
ssl_alpn_proto_negotiated fn =
|
|
||||||
APR_ARRAY_IDX(sslconn->alpn_negofns, n,
|
|
||||||
ssl_alpn_proto_negotiated);
|
|
||||||
|
|
||||||
if (fn(f->c, (const char *)next_proto, next_proto_len) == DONE)
|
if (strcmp(protocol, ap_run_protocol_get(f->c))) {
|
||||||
break;
|
status = ap_switch_protocol(f->c, NULL, sslconn->server,
|
||||||
|
protocol);
|
||||||
|
if (status != APR_SUCCESS) {
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, f->c,
|
||||||
|
APLOGNO(02908) "protocol switch to '%s' failed",
|
||||||
|
protocol);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inctx->alpn_finished = 1;
|
inctx->alpn_finished = 1;
|
||||||
|
@@ -1923,23 +1923,29 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc)
|
|||||||
|
|
||||||
#ifdef HAVE_TLSEXT
|
#ifdef HAVE_TLSEXT
|
||||||
/*
|
/*
|
||||||
* This callback function is executed when OpenSSL encounters an extended
|
* This function sets the virtual host from an extended
|
||||||
* client hello with a server name indication extension ("SNI", cf. RFC 6066).
|
* client hello with a server name indication extension ("SNI", cf. RFC 6066).
|
||||||
*/
|
*/
|
||||||
int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
|
static apr_status_t init_vhost(conn_rec *c, SSL *ssl)
|
||||||
{
|
{
|
||||||
const char *servername =
|
const char *servername;
|
||||||
SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
|
||||||
conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
|
|
||||||
|
|
||||||
if (c) {
|
if (c) {
|
||||||
|
SSLConnRec *sslcon = myConnConfig(c);
|
||||||
|
|
||||||
|
if (sslcon->server != c->base_server) {
|
||||||
|
/* already found the vhost */
|
||||||
|
return APR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||||
if (servername) {
|
if (servername) {
|
||||||
if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
|
if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
|
||||||
(void *)servername)) {
|
(void *)servername)) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
|
||||||
"SSL virtual host for servername %s found",
|
"SSL virtual host for servername %s found",
|
||||||
servername);
|
servername);
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return APR_SUCCESS;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
|
||||||
@@ -1970,7 +1976,19 @@ int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SSL_TLSEXT_ERR_NOACK;
|
return APR_NOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This callback function is executed when OpenSSL encounters an extended
|
||||||
|
* client hello with a server name indication extension ("SNI", cf. RFC 6066).
|
||||||
|
*/
|
||||||
|
int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
|
||||||
|
{
|
||||||
|
conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
|
||||||
|
apr_status_t status = init_vhost(c, ssl);
|
||||||
|
|
||||||
|
return (status == APR_SUCCESS)? SSL_TLSEXT_ERR_OK : SSL_TLSEXT_ERR_NOACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2170,41 +2188,6 @@ int ssl_callback_SessionTicket(SSL *ssl,
|
|||||||
#endif /* HAVE_TLS_SESSION_TICKETS */
|
#endif /* HAVE_TLS_SESSION_TICKETS */
|
||||||
|
|
||||||
#ifdef HAVE_TLS_ALPN
|
#ifdef HAVE_TLS_ALPN
|
||||||
static int ssl_array_index(apr_array_header_t *array, const char *s)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < array->nelts; i++) {
|
|
||||||
const char *p = APR_ARRAY_IDX(array, i, const char *);
|
|
||||||
if (!strcmp(p, s)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compare two ALPN protocol proposal. Result is similar to strcmp():
|
|
||||||
* 0 gives same precedence, >0 means proto1 is preferred.
|
|
||||||
*/
|
|
||||||
static int ssl_cmp_alpn_protos(modssl_ctx_t *ctx,
|
|
||||||
const char *proto1,
|
|
||||||
const char *proto2)
|
|
||||||
{
|
|
||||||
if (ctx && ctx->ssl_alpn_pref) {
|
|
||||||
int index1 = ssl_array_index(ctx->ssl_alpn_pref, proto1);
|
|
||||||
int index2 = ssl_array_index(ctx->ssl_alpn_pref, proto2);
|
|
||||||
if (index2 > index1) {
|
|
||||||
return (index1 >= 0) ? 1 : -1;
|
|
||||||
}
|
|
||||||
else if (index1 > index2) {
|
|
||||||
return (index2 >= 0) ? -1 : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* both have the same index (mabye -1 or no pref configured) and we compare
|
|
||||||
* the names so that spdy3 gets precedence over spdy2. That makes
|
|
||||||
* the outcome at least deterministic. */
|
|
||||||
return strcmp((const char *)proto1, (const char *)proto2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This callback function is executed when the TLS Application-Layer
|
* This callback function is executed when the TLS Application-Layer
|
||||||
@@ -2225,14 +2208,9 @@ int ssl_callback_alpn_select(SSL *ssl,
|
|||||||
{
|
{
|
||||||
conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
|
conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
|
||||||
SSLConnRec *sslconn = myConnConfig(c);
|
SSLConnRec *sslconn = myConnConfig(c);
|
||||||
server_rec *s = mySrvFromConn(c);
|
|
||||||
SSLSrvConfigRec *sc = mySrvConfig(s);
|
|
||||||
modssl_ctx_t *mctx = myCtxConfig(sslconn, sc);
|
|
||||||
const char *alpn_http1 = "http/1.1";
|
|
||||||
apr_array_header_t *client_protos;
|
apr_array_header_t *client_protos;
|
||||||
apr_array_header_t *proposed_protos;
|
|
||||||
int i;
|
|
||||||
size_t len;
|
size_t len;
|
||||||
|
int i;
|
||||||
|
|
||||||
/* If the connection object is not available,
|
/* If the connection object is not available,
|
||||||
* then there's nothing for us to do. */
|
* then there's nothing for us to do. */
|
||||||
@@ -2261,48 +2239,15 @@ int ssl_callback_alpn_select(SSL *ssl,
|
|||||||
i += plen;
|
i += plen;
|
||||||
}
|
}
|
||||||
|
|
||||||
proposed_protos = apr_array_make(c->pool, client_protos->nelts+1,
|
/* The order the callbacks are invoked from TLS extensions is, unfortunately
|
||||||
sizeof(char *));
|
* not defined and older openssl versions do call ALPN selection before
|
||||||
|
* they callback the SNI. We need to make sure that we know which vhost
|
||||||
if (sslconn->alpn_proposefns != NULL) {
|
* we are dealing with so we respect the correct protocols.
|
||||||
/* Invoke our alpn_propose functions, giving other modules a chance to
|
|
||||||
* propose protocol names for selection. We might have several such
|
|
||||||
* functions installed and if two make a proposal, we need to give
|
|
||||||
* preference to one.
|
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < sslconn->alpn_proposefns->nelts; i++) {
|
init_vhost(c, ssl);
|
||||||
ssl_alpn_propose_protos fn =
|
|
||||||
APR_ARRAY_IDX(sslconn->alpn_proposefns, i,
|
|
||||||
ssl_alpn_propose_protos);
|
|
||||||
|
|
||||||
if (fn(c, client_protos, proposed_protos) == DONE)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proposed_protos->nelts <= 0) {
|
|
||||||
/* Regardless of installed hooks, the http/1.1 protocol is always
|
|
||||||
* supported by us. Choose it if none other matches. */
|
|
||||||
if (ssl_array_index(client_protos, alpn_http1) < 0) {
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02839)
|
|
||||||
"none of the client ALPN protocols are supported");
|
|
||||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
||||||
}
|
|
||||||
*out = (const unsigned char*)alpn_http1;
|
|
||||||
*outlen = (unsigned char)strlen(alpn_http1);
|
|
||||||
return SSL_TLSEXT_ERR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now select the most preferred protocol from the proposals. */
|
|
||||||
*out = APR_ARRAY_IDX(proposed_protos, 0, const unsigned char *);
|
|
||||||
for (i = 1; i < proposed_protos->nelts; ++i) {
|
|
||||||
const char *proto = APR_ARRAY_IDX(proposed_protos, i, const char *);
|
|
||||||
/* Do we prefer it over existing candidate? */
|
|
||||||
if (ssl_cmp_alpn_protos(mctx, (const char *)*out, proto) < 0) {
|
|
||||||
*out = (const unsigned char *)proto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
*out = (const unsigned char *)ap_select_protocol(c, NULL, sslconn->server,
|
||||||
|
client_protos);
|
||||||
len = strlen((const char*)*out);
|
len = strlen((const char*)*out);
|
||||||
if (len > 255) {
|
if (len > 255) {
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02840)
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02840)
|
||||||
|
@@ -438,12 +438,6 @@ typedef struct {
|
|||||||
* connection */
|
* connection */
|
||||||
} reneg_state;
|
} reneg_state;
|
||||||
|
|
||||||
#ifdef HAVE_TLS_ALPN
|
|
||||||
/* Poor man's inter-module optional hooks for ALPN. */
|
|
||||||
apr_array_header_t *alpn_proposefns; /* list of ALPN propose callbacks */
|
|
||||||
apr_array_header_t *alpn_negofns; /* list of ALPN negotiation callbacks. */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
server_rec *server;
|
server_rec *server;
|
||||||
} SSLConnRec;
|
} SSLConnRec;
|
||||||
|
|
||||||
@@ -625,10 +619,6 @@ typedef struct {
|
|||||||
SSL_CONF_CTX *ssl_ctx_config; /* Configuration context */
|
SSL_CONF_CTX *ssl_ctx_config; /* Configuration context */
|
||||||
apr_array_header_t *ssl_ctx_param; /* parameters to pass to SSL_CTX */
|
apr_array_header_t *ssl_ctx_param; /* parameters to pass to SSL_CTX */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_TLS_ALPN
|
|
||||||
apr_array_header_t *ssl_alpn_pref; /* list of ALPN protocol IDs */
|
|
||||||
#endif
|
|
||||||
} modssl_ctx_t;
|
} modssl_ctx_t;
|
||||||
|
|
||||||
struct SSLSrvConfigRec {
|
struct SSLSrvConfigRec {
|
||||||
@@ -755,10 +745,6 @@ const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd, void *dcfg, int flag);
|
|||||||
const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg, const char *arg1, const char *arg2);
|
const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg, const char *arg1, const char *arg2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_TLS_ALPN
|
|
||||||
const char *ssl_cmd_SSLALPNPreference(cmd_parms *cmd, void *dcfg, const char *protocol);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SRP
|
#ifdef HAVE_SRP
|
||||||
const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg, const char *arg);
|
const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg, const char *arg);
|
||||||
const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg, const char *arg);
|
const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg, const char *arg);
|
||||||
|
@@ -478,6 +478,8 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
|
|||||||
|
|
||||||
conf->trace_enable = AP_TRACE_UNSET;
|
conf->trace_enable = AP_TRACE_UNSET;
|
||||||
|
|
||||||
|
conf->protocols = apr_array_make(a, 5, sizeof(const char *));
|
||||||
|
|
||||||
return (void *)conf;
|
return (void *)conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,6 +553,8 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
|
|||||||
? virt->merge_trailers
|
? virt->merge_trailers
|
||||||
: base->merge_trailers;
|
: base->merge_trailers;
|
||||||
|
|
||||||
|
conf->protocols = apr_array_append(p, base->protocols, virt->protocols);
|
||||||
|
|
||||||
return conf;
|
return conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3799,12 +3803,33 @@ static const char *set_trace_enable(cmd_parms *cmd, void *dummy,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *set_protocols(cmd_parms *cmd, void *dummy,
|
||||||
|
const char *arg)
|
||||||
|
{
|
||||||
|
core_server_config *conf =
|
||||||
|
ap_get_core_module_config(cmd->server->module_config);
|
||||||
|
const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Should we check for some ALPN valid char sequence here? */
|
||||||
|
const char **np = (const char **)apr_array_push(conf->protocols);
|
||||||
|
*np = arg;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static const char *set_http_protocol(cmd_parms *cmd, void *dummy,
|
static const char *set_http_protocol(cmd_parms *cmd, void *dummy,
|
||||||
const char *arg)
|
const char *arg)
|
||||||
{
|
{
|
||||||
core_server_config *conf =
|
core_server_config *conf =
|
||||||
ap_get_core_module_config(cmd->server->module_config);
|
ap_get_core_module_config(cmd->server->module_config);
|
||||||
|
|
||||||
|
if (!conf->protocols) {
|
||||||
|
|
||||||
|
}
|
||||||
if (strncmp(arg, "min=", 4) == 0) {
|
if (strncmp(arg, "min=", 4) == 0) {
|
||||||
arg += 4;
|
arg += 4;
|
||||||
if (strcmp(arg, "0.9") == 0)
|
if (strcmp(arg, "0.9") == 0)
|
||||||
@@ -4445,6 +4470,8 @@ AP_INIT_FLAG("HttpContentLengthHeadZero", set_cl_head_zero, NULL, OR_OPTIONS,
|
|||||||
"whether to permit Content-Length of 0 responses to HEAD requests"),
|
"whether to permit Content-Length of 0 responses to HEAD requests"),
|
||||||
AP_INIT_FLAG("HttpExpectStrict", set_expect_strict, NULL, OR_OPTIONS,
|
AP_INIT_FLAG("HttpExpectStrict", set_expect_strict, NULL, OR_OPTIONS,
|
||||||
"whether to return a 417 if a client doesn't send 100-Continue"),
|
"whether to return a 417 if a client doesn't send 100-Continue"),
|
||||||
|
AP_INIT_ITERATE("Protocols", set_protocols, NULL, RSRC_CONF,
|
||||||
|
"Controls which protocols are allowed, sorted by preference"),
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -5226,6 +5253,73 @@ static void core_dump_config(apr_pool_t *p, server_rec *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *core_protocol_get(const conn_rec *c)
|
||||||
|
{
|
||||||
|
return AP_PROTOCOL_HTTP1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int core_upgrade_handler(request_rec *r)
|
||||||
|
{
|
||||||
|
conn_rec *c = r->connection;
|
||||||
|
const char *upgrade = apr_table_get(r->headers_in, "Upgrade");
|
||||||
|
|
||||||
|
if (upgrade && *upgrade) {
|
||||||
|
const char *conn = apr_table_get(r->headers_in, "Connection");
|
||||||
|
if (ap_find_token(r->pool, conn, "upgrade")) {
|
||||||
|
apr_array_header_t *offers = NULL;
|
||||||
|
const char *err;
|
||||||
|
|
||||||
|
err = ap_parse_token_list_strict(r->pool, upgrade, &offers, 0);
|
||||||
|
if (err) {
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02910)
|
||||||
|
"parsing Upgrade header: %s", err);
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offers && offers->nelts > 0) {
|
||||||
|
const char *protocol = ap_select_protocol(c, r, r->server,
|
||||||
|
offers);
|
||||||
|
if (strcmp(protocol, ap_run_protocol_get(c))) {
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02909)
|
||||||
|
"Upgrade selects '%s'", protocol);
|
||||||
|
/* Let the client know what we are upgrading to. */
|
||||||
|
apr_table_clear(r->headers_out);
|
||||||
|
apr_table_setn(r->headers_out, "Upgrade", protocol);
|
||||||
|
apr_table_setn(r->headers_out, "Connection", "Upgrade");
|
||||||
|
|
||||||
|
r->status = HTTP_SWITCHING_PROTOCOLS;
|
||||||
|
r->status_line = ap_get_status_line(r->status);
|
||||||
|
ap_send_interim_response(r, 1);
|
||||||
|
|
||||||
|
ap_switch_protocol(c, r, r->server, protocol);
|
||||||
|
|
||||||
|
/* make sure httpd closes the connection after this */
|
||||||
|
c->keepalive = AP_CONN_CLOSE;
|
||||||
|
ap_lingering_close(c);
|
||||||
|
|
||||||
|
if (c->sbh) {
|
||||||
|
ap_update_child_status_from_conn(c->sbh,
|
||||||
|
SERVER_CLOSING, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int core_upgrade_storage(request_rec *r)
|
||||||
|
{
|
||||||
|
if ((r->method_number == M_OPTIONS) && r->uri && (r->uri[0] == '*') &&
|
||||||
|
(r->uri[1] == '\0')) {
|
||||||
|
return core_upgrade_handler(r);
|
||||||
|
}
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
static void register_hooks(apr_pool_t *p)
|
static void register_hooks(apr_pool_t *p)
|
||||||
{
|
{
|
||||||
errorlog_hash = apr_hash_make(p);
|
errorlog_hash = apr_hash_make(p);
|
||||||
@@ -5248,10 +5342,12 @@ static void register_hooks(apr_pool_t *p)
|
|||||||
ap_hook_check_config(core_check_config,NULL,NULL,APR_HOOK_FIRST);
|
ap_hook_check_config(core_check_config,NULL,NULL,APR_HOOK_FIRST);
|
||||||
ap_hook_test_config(core_dump_config,NULL,NULL,APR_HOOK_FIRST);
|
ap_hook_test_config(core_dump_config,NULL,NULL,APR_HOOK_FIRST);
|
||||||
ap_hook_translate_name(ap_core_translate,NULL,NULL,APR_HOOK_REALLY_LAST);
|
ap_hook_translate_name(ap_core_translate,NULL,NULL,APR_HOOK_REALLY_LAST);
|
||||||
|
ap_hook_map_to_storage(core_upgrade_storage,NULL,NULL,APR_HOOK_REALLY_FIRST);
|
||||||
ap_hook_map_to_storage(core_map_to_storage,NULL,NULL,APR_HOOK_REALLY_LAST);
|
ap_hook_map_to_storage(core_map_to_storage,NULL,NULL,APR_HOOK_REALLY_LAST);
|
||||||
ap_hook_open_logs(ap_open_logs,NULL,NULL,APR_HOOK_REALLY_FIRST);
|
ap_hook_open_logs(ap_open_logs,NULL,NULL,APR_HOOK_REALLY_FIRST);
|
||||||
ap_hook_child_init(core_child_init,NULL,NULL,APR_HOOK_REALLY_FIRST);
|
ap_hook_child_init(core_child_init,NULL,NULL,APR_HOOK_REALLY_FIRST);
|
||||||
ap_hook_child_init(ap_logs_child_init,NULL,NULL,APR_HOOK_MIDDLE);
|
ap_hook_child_init(ap_logs_child_init,NULL,NULL,APR_HOOK_MIDDLE);
|
||||||
|
ap_hook_handler(core_upgrade_handler,NULL,NULL,APR_HOOK_REALLY_FIRST);
|
||||||
ap_hook_handler(default_handler,NULL,NULL,APR_HOOK_REALLY_LAST);
|
ap_hook_handler(default_handler,NULL,NULL,APR_HOOK_REALLY_LAST);
|
||||||
/* FIXME: I suspect we can eliminate the need for these do_nothings - Ben */
|
/* FIXME: I suspect we can eliminate the need for these do_nothings - Ben */
|
||||||
ap_hook_type_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);
|
ap_hook_type_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);
|
||||||
@@ -5267,6 +5363,7 @@ static void register_hooks(apr_pool_t *p)
|
|||||||
ap_hook_open_htaccess(ap_open_htaccess, NULL, NULL, APR_HOOK_REALLY_LAST);
|
ap_hook_open_htaccess(ap_open_htaccess, NULL, NULL, APR_HOOK_REALLY_LAST);
|
||||||
ap_hook_optional_fn_retrieve(core_optional_fn_retrieve, NULL, NULL,
|
ap_hook_optional_fn_retrieve(core_optional_fn_retrieve, NULL, NULL,
|
||||||
APR_HOOK_MIDDLE);
|
APR_HOOK_MIDDLE);
|
||||||
|
ap_hook_protocol_get(core_protocol_get, NULL, NULL, APR_HOOK_REALLY_LAST);
|
||||||
|
|
||||||
/* register the core's insert_filter hook and register core-provided
|
/* register the core's insert_filter hook and register core-provided
|
||||||
* filters
|
* filters
|
||||||
|
@@ -67,6 +67,9 @@ APR_HOOK_STRUCT(
|
|||||||
APR_HOOK_LINK(http_scheme)
|
APR_HOOK_LINK(http_scheme)
|
||||||
APR_HOOK_LINK(default_port)
|
APR_HOOK_LINK(default_port)
|
||||||
APR_HOOK_LINK(note_auth_failure)
|
APR_HOOK_LINK(note_auth_failure)
|
||||||
|
APR_HOOK_LINK(protocol_propose)
|
||||||
|
APR_HOOK_LINK(protocol_switch)
|
||||||
|
APR_HOOK_LINK(protocol_get)
|
||||||
)
|
)
|
||||||
|
|
||||||
AP_DECLARE_DATA ap_filter_rec_t *ap_old_write_func = NULL;
|
AP_DECLARE_DATA ap_filter_rec_t *ap_old_write_func = NULL;
|
||||||
@@ -1944,6 +1947,125 @@ AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers)
|
|||||||
apr_brigade_destroy(x.bb);
|
apr_brigade_destroy(x.bb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Something like this must be in APR, only I do not find it... */
|
||||||
|
static int array_index(apr_array_header_t *array, const char *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < array->nelts; i++) {
|
||||||
|
const char *p = APR_ARRAY_IDX(array, i, const char *);
|
||||||
|
if (!strcmp(p, s)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare two protocol identifier. Result is similar to strcmp():
|
||||||
|
* 0 gives same precedence, >0 means proto1 is preferred.
|
||||||
|
*/
|
||||||
|
static int protocol_cmp(apr_array_header_t *preferences,
|
||||||
|
const char *proto1,
|
||||||
|
const char *proto2)
|
||||||
|
{
|
||||||
|
if (preferences && preferences->nelts > 0) {
|
||||||
|
int index1 = array_index(preferences, proto1);
|
||||||
|
int index2 = array_index(preferences, proto2);
|
||||||
|
if (index2 > index1) {
|
||||||
|
return (index1 >= 0) ? 1 : -1;
|
||||||
|
}
|
||||||
|
else if (index1 > index2) {
|
||||||
|
return (index2 >= 0) ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* both have the same index (mabye -1 or no pref configured) and we compare
|
||||||
|
* the names so that spdy3 gets precedence over spdy2. That makes
|
||||||
|
* the outcome at least deterministic. */
|
||||||
|
return strcmp(proto1, proto2);
|
||||||
|
}
|
||||||
|
|
||||||
|
AP_DECLARE(const char *) ap_select_protocol(conn_rec *c, request_rec *r,
|
||||||
|
server_rec *s,
|
||||||
|
apr_array_header_t *choices)
|
||||||
|
{
|
||||||
|
apr_pool_t *pool = r? r->pool : c->pool;
|
||||||
|
apr_array_header_t *proposals;
|
||||||
|
const char *protocol = NULL;
|
||||||
|
core_server_config *conf = ap_get_core_module_config(s->module_config);
|
||||||
|
|
||||||
|
if (APLOGcdebug(c)) {
|
||||||
|
const char *p = apr_array_pstrcat(pool, conf->protocols, ',');
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
|
||||||
|
"select protocol from %s, choices=%s for server %s",
|
||||||
|
p, apr_array_pstrcat(pool, choices, ','),
|
||||||
|
s->server_hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
proposals = apr_array_make(pool, choices->nelts+1, sizeof(char *));
|
||||||
|
ap_run_protocol_propose(c, r, s, choices, proposals);
|
||||||
|
|
||||||
|
if (proposals->nelts > 0) {
|
||||||
|
int i;
|
||||||
|
/* Select the most preferred protocol */
|
||||||
|
if (APLOGcdebug(c)) {
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
|
||||||
|
"select protocol, proposals=%s",
|
||||||
|
apr_array_pstrcat(pool, proposals, ','));
|
||||||
|
}
|
||||||
|
for (i = 0; i < proposals->nelts; ++i) {
|
||||||
|
const char *p = APR_ARRAY_IDX(proposals, i, const char *);
|
||||||
|
if (conf->protocols->nelts > 0
|
||||||
|
&& array_index(conf->protocols, p) < 0) {
|
||||||
|
/* not a permitted protocol here */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (!protocol
|
||||||
|
|| (protocol_cmp(conf->protocols, protocol, p) < 0)) {
|
||||||
|
/* none selected yet or this on has preference */
|
||||||
|
protocol = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (APLOGcdebug(c)) {
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "selected protocol=%s",
|
||||||
|
protocol? protocol : "(none)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol? protocol : ap_run_protocol_get(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
AP_DECLARE(apr_status_t) ap_switch_protocol(conn_rec *c, request_rec *r,
|
||||||
|
server_rec *s,
|
||||||
|
const char *protocol)
|
||||||
|
{
|
||||||
|
const char *current = ap_run_protocol_get(c);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!strcmp(current, protocol)) {
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(02906)
|
||||||
|
"already at it, protocol_switch to %s",
|
||||||
|
protocol);
|
||||||
|
return APR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ap_run_protocol_switch(c, r, s, protocol);
|
||||||
|
switch (rc) {
|
||||||
|
case DECLINED:
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02907)
|
||||||
|
"no implementation for protocol_switch to %s",
|
||||||
|
protocol);
|
||||||
|
return APR_ENOTIMPL;
|
||||||
|
case OK:
|
||||||
|
case DONE:
|
||||||
|
return APR_SUCCESS;
|
||||||
|
default:
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02905)
|
||||||
|
"unexpected return code %d from protocol_switch to %s"
|
||||||
|
, rc, protocol);
|
||||||
|
return APR_EOF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
AP_IMPLEMENT_HOOK_VOID(pre_read_request,
|
AP_IMPLEMENT_HOOK_VOID(pre_read_request,
|
||||||
(request_rec *r, conn_rec *c),
|
(request_rec *r, conn_rec *c),
|
||||||
@@ -1959,3 +2081,14 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(unsigned short,default_port,
|
|||||||
AP_IMPLEMENT_HOOK_RUN_FIRST(int, note_auth_failure,
|
AP_IMPLEMENT_HOOK_RUN_FIRST(int, note_auth_failure,
|
||||||
(request_rec *r, const char *auth_type),
|
(request_rec *r, const char *auth_type),
|
||||||
(r, auth_type), DECLINED)
|
(r, auth_type), DECLINED)
|
||||||
|
AP_IMPLEMENT_HOOK_RUN_ALL(int,protocol_propose,
|
||||||
|
(conn_rec *c, request_rec *r, server_rec *s,
|
||||||
|
const apr_array_header_t *offers,
|
||||||
|
apr_array_header_t *proposals),
|
||||||
|
(c, r, s, offers, proposals), OK, DECLINED)
|
||||||
|
AP_IMPLEMENT_HOOK_RUN_FIRST(int,protocol_switch,
|
||||||
|
(conn_rec *c, request_rec *r, server_rec *s,
|
||||||
|
const char *protocol),
|
||||||
|
(c, r, s, protocol), DECLINED)
|
||||||
|
AP_IMPLEMENT_HOOK_RUN_FIRST(const char *,protocol_get,
|
||||||
|
(const conn_rec *c), (c), NULL)
|
||||||
|
Reference in New Issue
Block a user