Blame modules/http2/h2_switch.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 <assert.h>
Packit 90a5c9
Packit 90a5c9
#include <apr_strings.h>
Packit 90a5c9
#include <apr_optional.h>
Packit 90a5c9
#include <apr_optional_hooks.h>
Packit 90a5c9
Packit 90a5c9
#include <httpd.h>
Packit 90a5c9
#include <http_core.h>
Packit 90a5c9
#include <http_config.h>
Packit 90a5c9
#include <http_connection.h>
Packit 90a5c9
#include <http_protocol.h>
Packit 90a5c9
#include <http_log.h>
Packit 90a5c9
Packit 90a5c9
#include "h2_private.h"
Packit 90a5c9
Packit 90a5c9
#include "h2_config.h"
Packit 90a5c9
#include "h2_ctx.h"
Packit 90a5c9
#include "h2_conn.h"
Packit 90a5c9
#include "h2_h2.h"
Packit 90a5c9
#include "h2_switch.h"
Packit 90a5c9
Packit 90a5c9
/*******************************************************************************
Packit 90a5c9
 * Once per lifetime init, retrieve optional functions
Packit 90a5c9
 */
Packit 90a5c9
apr_status_t h2_switch_init(apr_pool_t *pool, server_rec *s)
Packit 90a5c9
{
Packit 90a5c9
    (void)pool;
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "h2_switch init");
Packit 90a5c9
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int h2_protocol_propose(conn_rec *c, request_rec *r,
Packit 90a5c9
                               server_rec *s,
Packit 90a5c9
                               const apr_array_header_t *offers,
Packit 90a5c9
                               apr_array_header_t *proposals)
Packit 90a5c9
{
Packit 90a5c9
    int proposed = 0;
Packit 90a5c9
    int is_tls = h2_h2_is_tls(c);
Packit 90a5c9
    const char **protos = is_tls? h2_tls_protos : h2_clear_protos;
Packit 90a5c9
    
Packit 90a5c9
    (void)s;
Packit 90a5c9
    if (!h2_mpm_supported()) {
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))) {
Packit 90a5c9
        /* We do not know how to switch from anything else but http/1.1.
Packit 90a5c9
         */
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03083)
Packit 90a5c9
                      "protocol switch: current proto != http/1.1, declined");
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (!h2_is_acceptable_connection(c, 0)) {
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03084)
Packit 90a5c9
                      "protocol propose: connection requirements not met");
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (r) {
Packit 90a5c9
        /* So far, this indicates an HTTP/1 Upgrade header initiated
Packit 90a5c9
         * protocol switch. For that, the HTTP2-Settings header needs
Packit 90a5c9
         * to be present and valid for the connection.
Packit 90a5c9
         */
Packit 90a5c9
        const char *p;
Packit 90a5c9
        
Packit 90a5c9
        if (!h2_allows_h2_upgrade(c)) {
Packit 90a5c9
            return DECLINED;
Packit 90a5c9
        }
Packit 90a5c9
         
Packit 90a5c9
        p = apr_table_get(r->headers_in, "HTTP2-Settings");
Packit 90a5c9
        if (!p) {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03085)
Packit 90a5c9
                          "upgrade without HTTP2-Settings declined");
Packit 90a5c9
            return DECLINED;
Packit 90a5c9
        }
Packit 90a5c9
        
Packit 90a5c9
        p = apr_table_get(r->headers_in, "Connection");
Packit 90a5c9
        if (!ap_find_token(r->pool, p, "http2-settings")) {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03086)
Packit 90a5c9
                          "upgrade without HTTP2-Settings declined");
Packit 90a5c9
            return DECLINED;
Packit 90a5c9
        }
Packit 90a5c9
        
Packit 90a5c9
        /* We also allow switching only for requests that have no body.
Packit 90a5c9
         */
Packit 90a5c9
        p = apr_table_get(r->headers_in, "Content-Length");
Packit 90a5c9
        if (p && strcmp(p, "0")) {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03087)
Packit 90a5c9
                          "upgrade with content-length: %s, declined", p);
Packit 90a5c9
            return DECLINED;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    while (*protos) {
Packit 90a5c9
        /* Add all protocols we know (tls or clear) and that
Packit 90a5c9
         * are part of the offerings (if there have been any). 
Packit 90a5c9
         */
Packit 90a5c9
        if (!offers || ap_array_str_contains(offers, *protos)) {
Packit 90a5c9
            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
Packit 90a5c9
                          "proposing protocol '%s'", *protos);
Packit 90a5c9
            APR_ARRAY_PUSH(proposals, const char*) = *protos;
Packit 90a5c9
            proposed = 1;
Packit 90a5c9
        }
Packit 90a5c9
        ++protos;
Packit 90a5c9
    }
Packit 90a5c9
    return proposed? DECLINED : OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
Packit 90a5c9
                              const char *protocol)
Packit 90a5c9
{
Packit 90a5c9
    int found = 0;
Packit 90a5c9
    const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos;
Packit 90a5c9
    const char **p = protos;
Packit 90a5c9
    
Packit 90a5c9
    (void)s;
Packit 90a5c9
    if (!h2_mpm_supported()) {
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    while (*p) {
Packit 90a5c9
        if (!strcmp(*p, protocol)) {
Packit 90a5c9
            found = 1;
Packit 90a5c9
            break;
Packit 90a5c9
        }
Packit 90a5c9
        p++;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (found) {
Packit 90a5c9
        h2_ctx *ctx = h2_ctx_get(c, 1);
Packit 90a5c9
        
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
Packit 90a5c9
                      "switching protocol to '%s'", protocol);
Packit 90a5c9
        h2_ctx_protocol_set(ctx, protocol);
Packit 90a5c9
        h2_ctx_server_set(ctx, s);
Packit 90a5c9
        
Packit 90a5c9
        if (r != NULL) {
Packit 90a5c9
            apr_status_t status;
Packit 90a5c9
            /* Switching in the middle of a request means that
Packit 90a5c9
             * we have to send out the response to this one in h2
Packit 90a5c9
             * format. So we need to take over the connection
Packit 90a5c9
             * right away.
Packit 90a5c9
             */
Packit 90a5c9
            ap_remove_input_filter_byhandle(r->input_filters, "http_in");
Packit 90a5c9
            ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout");
Packit 90a5c9
            ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
Packit 90a5c9
            
Packit 90a5c9
            /* Ok, start an h2_conn on this one. */
Packit 90a5c9
            h2_ctx_server_set(ctx, r->server);
Packit 90a5c9
            status = h2_conn_setup(ctx, r->connection, r);
Packit 90a5c9
            if (status != APR_SUCCESS) {
Packit 90a5c9
                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(03088)
Packit 90a5c9
                              "session setup");
Packit 90a5c9
                h2_ctx_clear(c);
Packit 90a5c9
                return !OK;
Packit 90a5c9
            }
Packit 90a5c9
            
Packit 90a5c9
            h2_conn_run(ctx, c);
Packit 90a5c9
        }
Packit 90a5c9
        return OK;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    return DECLINED;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *h2_protocol_get(const conn_rec *c)
Packit 90a5c9
{
Packit 90a5c9
    return h2_ctx_protocol_get(c);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
void h2_switch_register_hooks(void)
Packit 90a5c9
{
Packit 90a5c9
    ap_hook_protocol_propose(h2_protocol_propose, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
    ap_hook_protocol_switch(h2_protocol_switch, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
    ap_hook_protocol_get(h2_protocol_get, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
}