Blame modules/http2/h2_alt_svc.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#include <apr_strings.h>
Packit 90a5c9
#include <httpd.h>
Packit 90a5c9
#include <http_core.h>
Packit 90a5c9
#include <http_connection.h>
Packit 90a5c9
#include <http_protocol.h>
Packit 90a5c9
#include <http_log.h>
Packit 90a5c9
Packit 90a5c9
#include "h2_private.h"
Packit 90a5c9
#include "h2_alt_svc.h"
Packit 90a5c9
#include "h2_ctx.h"
Packit 90a5c9
#include "h2_config.h"
Packit 90a5c9
#include "h2_h2.h"
Packit 90a5c9
#include "h2_util.h"
Packit 90a5c9
Packit 90a5c9
static int h2_alt_svc_handler(request_rec *r);
Packit 90a5c9
Packit 90a5c9
void h2_alt_svc_register_hooks(void)
Packit 90a5c9
{
Packit 90a5c9
    ap_hook_post_read_request(h2_alt_svc_handler, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/**
Packit 90a5c9
 * Parse an Alt-Svc specifier as described in "HTTP Alternative Services"
Packit 90a5c9
 * (https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-04)
Packit 90a5c9
 * with the following changes:
Packit 90a5c9
 * - do not percent encode token values
Packit 90a5c9
 * - do not use quotation marks
Packit 90a5c9
 */
Packit 90a5c9
h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool)
Packit 90a5c9
{
Packit 90a5c9
    const char *sep = ap_strchr_c(s, '=');
Packit 90a5c9
    if (sep) {
Packit 90a5c9
        const char *alpn = apr_pstrmemdup(pool, s, sep - s);
Packit 90a5c9
        const char *host = NULL;
Packit 90a5c9
        int port = 0;
Packit 90a5c9
        s = sep + 1;
Packit 90a5c9
        sep = ap_strchr_c(s, ':');  /* mandatory : */
Packit 90a5c9
        if (sep) {
Packit 90a5c9
            if (sep != s) {    /* optional host */
Packit 90a5c9
                host = apr_pstrmemdup(pool, s, sep - s);
Packit 90a5c9
            }
Packit 90a5c9
            s = sep + 1;
Packit 90a5c9
            if (*s) {          /* must be a port number */
Packit 90a5c9
                port = (int)apr_atoi64(s);
Packit 90a5c9
                if (port > 0 && port < (0x1 << 16)) {
Packit 90a5c9
                    h2_alt_svc *as = apr_pcalloc(pool, sizeof(*as));
Packit 90a5c9
                    as->alpn = alpn;
Packit 90a5c9
                    as->host = host;
Packit 90a5c9
                    as->port = port;
Packit 90a5c9
                    return as;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
#define h2_alt_svc_IDX(list, i) ((h2_alt_svc**)(list)->elts)[i]
Packit 90a5c9
Packit 90a5c9
static int h2_alt_svc_handler(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    const h2_config *cfg;
Packit 90a5c9
    int i;
Packit 90a5c9
    
Packit 90a5c9
    if (r->connection->keepalives > 0) {
Packit 90a5c9
        /* Only announce Alt-Svc on the first response */
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (h2_ctx_rget(r)) {
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    cfg = h2_config_sget(r->server);
Packit 90a5c9
    if (r->hostname && cfg && cfg->alt_svcs && cfg->alt_svcs->nelts > 0) {
Packit 90a5c9
        const char *alt_svc_used = apr_table_get(r->headers_in, "Alt-Svc-Used");
Packit 90a5c9
        if (!alt_svc_used) {
Packit 90a5c9
            /* We have alt-svcs defined and client is not already using
Packit 90a5c9
             * one, announce the services that were configured and match. 
Packit 90a5c9
             * The security of this connection determines if we allow
Packit 90a5c9
             * other host names or ports only.
Packit 90a5c9
             */
Packit 90a5c9
            const char *alt_svc = "";
Packit 90a5c9
            const char *svc_ma = "";
Packit 90a5c9
            int secure = h2_h2_is_tls(r->connection);
Packit 90a5c9
            int ma = h2_config_geti(cfg, H2_CONF_ALT_SVC_MAX_AGE);
Packit 90a5c9
            if (ma >= 0) {
Packit 90a5c9
                svc_ma = apr_psprintf(r->pool, "; ma=%d", ma);
Packit 90a5c9
            }
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03043)
Packit 90a5c9
                          "h2_alt_svc: announce %s for %s:%d", 
Packit 90a5c9
                          (secure? "secure" : "insecure"), 
Packit 90a5c9
                          r->hostname, (int)r->server->port);
Packit 90a5c9
            for (i = 0; i < cfg->alt_svcs->nelts; ++i) {
Packit 90a5c9
                h2_alt_svc *as = h2_alt_svc_IDX(cfg->alt_svcs, i);
Packit 90a5c9
                const char *ahost = as->host;
Packit 90a5c9
                if (ahost && !apr_strnatcasecmp(ahost, r->hostname)) {
Packit 90a5c9
                    ahost = NULL;
Packit 90a5c9
                }
Packit 90a5c9
                if (secure || !ahost) {
Packit 90a5c9
                    alt_svc = apr_psprintf(r->pool, "%s%s%s=\"%s:%d\"%s", 
Packit 90a5c9
                                           alt_svc,
Packit 90a5c9
                                           (*alt_svc? ", " : ""), as->alpn,
Packit 90a5c9
                                           ahost? ahost : "", as->port,
Packit 90a5c9
                                           svc_ma);
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            if (*alt_svc) {
Packit 90a5c9
                apr_table_setn(r->headers_out, "Alt-Svc", alt_svc);
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    return DECLINED;
Packit 90a5c9
}