diff options
Diffstat (limited to 'modules/http2/h2_alt_svc.c')
-rw-r--r-- | modules/http2/h2_alt_svc.c | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/modules/http2/h2_alt_svc.c b/modules/http2/h2_alt_svc.c new file mode 100644 index 0000000..295a16d --- /dev/null +++ b/modules/http2/h2_alt_svc.c @@ -0,0 +1,131 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 <apr_strings.h> +#include <httpd.h> +#include <http_core.h> +#include <http_connection.h> +#include <http_protocol.h> +#include <http_log.h> + +#include "h2_private.h" +#include "h2_alt_svc.h" +#include "h2_ctx.h" +#include "h2_config.h" +#include "h2_h2.h" +#include "h2_util.h" + +static int h2_alt_svc_handler(request_rec *r); + +void h2_alt_svc_register_hooks(void) +{ + ap_hook_post_read_request(h2_alt_svc_handler, NULL, NULL, APR_HOOK_MIDDLE); +} + +/** + * Parse an Alt-Svc specifier as described in "HTTP Alternative Services" + * (https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-04) + * with the following changes: + * - do not percent encode token values + * - do not use quotation marks + */ +h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool) +{ + const char *sep = ap_strchr_c(s, '='); + if (sep) { + const char *alpn = apr_pstrmemdup(pool, s, sep - s); + const char *host = NULL; + int port = 0; + s = sep + 1; + sep = ap_strchr_c(s, ':'); /* mandatory : */ + if (sep) { + if (sep != s) { /* optional host */ + host = apr_pstrmemdup(pool, s, sep - s); + } + s = sep + 1; + if (*s) { /* must be a port number */ + port = (int)apr_atoi64(s); + if (port > 0 && port < (0x1 << 16)) { + h2_alt_svc *as = apr_pcalloc(pool, sizeof(*as)); + as->alpn = alpn; + as->host = host; + as->port = port; + return as; + } + } + } + } + return NULL; +} + +#define h2_alt_svc_IDX(list, i) ((h2_alt_svc**)(list)->elts)[i] + +static int h2_alt_svc_handler(request_rec *r) +{ + const h2_config *cfg; + int i; + + if (r->connection->keepalives > 0) { + /* Only announce Alt-Svc on the first response */ + return DECLINED; + } + + if (h2_ctx_rget(r)) { + return DECLINED; + } + + cfg = h2_config_sget(r->server); + if (r->hostname && cfg && cfg->alt_svcs && cfg->alt_svcs->nelts > 0) { + const char *alt_svc_used = apr_table_get(r->headers_in, "Alt-Svc-Used"); + if (!alt_svc_used) { + /* We have alt-svcs defined and client is not already using + * one, announce the services that were configured and match. + * The security of this connection determines if we allow + * other host names or ports only. + */ + const char *alt_svc = ""; + const char *svc_ma = ""; + int secure = h2_h2_is_tls(r->connection); + int ma = h2_config_geti(cfg, H2_CONF_ALT_SVC_MAX_AGE); + if (ma >= 0) { + svc_ma = apr_psprintf(r->pool, "; ma=%d", ma); + } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03043) + "h2_alt_svc: announce %s for %s:%d", + (secure? "secure" : "insecure"), + r->hostname, (int)r->server->port); + for (i = 0; i < cfg->alt_svcs->nelts; ++i) { + h2_alt_svc *as = h2_alt_svc_IDX(cfg->alt_svcs, i); + const char *ahost = as->host; + if (ahost && !apr_strnatcasecmp(ahost, r->hostname)) { + ahost = NULL; + } + if (secure || !ahost) { + alt_svc = apr_psprintf(r->pool, "%s%s%s=\"%s:%d\"%s", + alt_svc, + (*alt_svc? ", " : ""), as->alpn, + ahost? ahost : "", as->port, + svc_ma); + } + } + if (*alt_svc) { + apr_table_setn(r->headers_out, "Alt-Svc", alt_svc); + } + } + } + + return DECLINED; +} |