Blame modules/http2/mod_http2.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_optional.h>
Packit 90a5c9
#include <apr_optional_hooks.h>
Packit 90a5c9
#include <apr_strings.h>
Packit 90a5c9
#include <apr_time.h>
Packit 90a5c9
#include <apr_want.h>
Packit 90a5c9
Packit 90a5c9
#include <httpd.h>
Packit 90a5c9
#include <http_protocol.h>
Packit 90a5c9
#include <http_request.h>
Packit 90a5c9
#include <http_log.h>
Packit 90a5c9
Packit 90a5c9
#include "mod_http2.h"
Packit 90a5c9
Packit 90a5c9
#include <nghttp2/nghttp2.h>
Packit 90a5c9
#include "h2_stream.h"
Packit 90a5c9
#include "h2_alt_svc.h"
Packit 90a5c9
#include "h2_conn.h"
Packit 90a5c9
#include "h2_filter.h"
Packit 90a5c9
#include "h2_task.h"
Packit 90a5c9
#include "h2_session.h"
Packit 90a5c9
#include "h2_config.h"
Packit 90a5c9
#include "h2_ctx.h"
Packit 90a5c9
#include "h2_h2.h"
Packit 90a5c9
#include "h2_mplx.h"
Packit 90a5c9
#include "h2_push.h"
Packit 90a5c9
#include "h2_request.h"
Packit 90a5c9
#include "h2_switch.h"
Packit 90a5c9
#include "h2_version.h"
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static void h2_hooks(apr_pool_t *pool);
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(http2) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    h2_config_create_dir, /* func to create per dir config */
Packit 90a5c9
    h2_config_merge_dir,  /* func to merge per dir config */
Packit 90a5c9
    h2_config_create_svr, /* func to create per server config */
Packit 90a5c9
    h2_config_merge_svr,  /* func to merge per server config */
Packit 90a5c9
    h2_cmds,              /* command handlers */
Packit 90a5c9
    h2_hooks,
Packit 90a5c9
#if defined(AP_MODULE_FLAG_NONE)
Packit 90a5c9
    AP_MODULE_FLAG_ALWAYS_MERGE
Packit 90a5c9
#endif
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
static int h2_h2_fixups(request_rec *r);
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    unsigned int change_prio : 1;
Packit 90a5c9
    unsigned int sha256 : 1;
Packit 90a5c9
    unsigned int inv_headers : 1;
Packit 90a5c9
    unsigned int dyn_windows : 1;
Packit 90a5c9
} features;
Packit 90a5c9
Packit 90a5c9
static features myfeats;
Packit 90a5c9
static int mpm_warned;
Packit 90a5c9
Packit 90a5c9
/* The module initialization. Called once as apache hook, before any multi
Packit 90a5c9
 * processing (threaded or not) happens. It is typically at least called twice, 
Packit 90a5c9
 * see
Packit 90a5c9
 * http://wiki.apache.org/httpd/ModuleLife
Packit 90a5c9
 * Since the first run is just a "practise" run, we want to initialize for real
Packit 90a5c9
 * only on the second try. This defeats the purpose of the first dry run a bit, 
Packit 90a5c9
 * since apache wants to verify that a new configuration actually will work. 
Packit 90a5c9
 * So if we have trouble with the configuration, this will only be detected 
Packit 90a5c9
 * when the server has already switched.
Packit 90a5c9
 * On the other hand, when we initialize lib nghttp2, all possible crazy things 
Packit 90a5c9
 * might happen and this might even eat threads. So, better init on the real 
Packit 90a5c9
 * invocation, for now at least.
Packit 90a5c9
 */
Packit 90a5c9
static int h2_post_config(apr_pool_t *p, apr_pool_t *plog,
Packit 90a5c9
                          apr_pool_t *ptemp, server_rec *s)
Packit 90a5c9
{
Packit 90a5c9
    void *data = NULL;
Packit 90a5c9
    const char *mod_h2_init_key = "mod_http2_init_counter";
Packit 90a5c9
    nghttp2_info *ngh2;
Packit 90a5c9
    apr_status_t status;
Packit 90a5c9
    
Packit 90a5c9
    (void)plog;(void)ptemp;
Packit 90a5c9
#ifdef H2_NG2_CHANGE_PRIO
Packit 90a5c9
    myfeats.change_prio = 1;
Packit 90a5c9
#endif
Packit 90a5c9
#ifdef H2_OPENSSL
Packit 90a5c9
    myfeats.sha256 = 1;
Packit 90a5c9
#endif
Packit 90a5c9
#ifdef H2_NG2_INVALID_HEADER_CB
Packit 90a5c9
    myfeats.inv_headers = 1;
Packit 90a5c9
#endif
Packit 90a5c9
#ifdef H2_NG2_LOCAL_WIN_SIZE
Packit 90a5c9
    myfeats.dyn_windows = 1;
Packit 90a5c9
#endif
Packit 90a5c9
    
Packit 90a5c9
    apr_pool_userdata_get(&data, mod_h2_init_key, s->process->pool);
Packit 90a5c9
    if ( data == NULL ) {
Packit 90a5c9
        ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03089)
Packit 90a5c9
                     "initializing post config dry run");
Packit 90a5c9
        apr_pool_userdata_set((const void *)1, mod_h2_init_key,
Packit 90a5c9
                              apr_pool_cleanup_null, s->process->pool);
Packit 90a5c9
        return APR_SUCCESS;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    ngh2 = nghttp2_version(0);
Packit 90a5c9
    ap_log_error( APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(03090)
Packit 90a5c9
                 "mod_http2 (v%s, feats=%s%s%s%s, nghttp2 %s), initializing...",
Packit 90a5c9
                 MOD_HTTP2_VERSION, 
Packit 90a5c9
                 myfeats.change_prio? "CHPRIO"  : "", 
Packit 90a5c9
                 myfeats.sha256?      "+SHA256" : "",
Packit 90a5c9
                 myfeats.inv_headers? "+INVHD"  : "",
Packit 90a5c9
                 myfeats.dyn_windows? "+DWINS"  : "",
Packit 90a5c9
                 ngh2?                ngh2->version_str : "unknown");
Packit 90a5c9
    
Packit 90a5c9
    switch (h2_conn_mpm_type()) {
Packit 90a5c9
        case H2_MPM_SIMPLE:
Packit 90a5c9
        case H2_MPM_MOTORZ:
Packit 90a5c9
        case H2_MPM_NETWARE:
Packit 90a5c9
        case H2_MPM_WINNT:
Packit 90a5c9
            /* not sure we need something extra for those. */
Packit 90a5c9
            break;
Packit 90a5c9
        case H2_MPM_EVENT:
Packit 90a5c9
        case H2_MPM_WORKER:
Packit 90a5c9
            /* all fine, we know these ones */
Packit 90a5c9
            break;
Packit 90a5c9
        case H2_MPM_PREFORK:
Packit 90a5c9
            /* ok, we now know how to handle that one */
Packit 90a5c9
            break;
Packit 90a5c9
        case H2_MPM_UNKNOWN:
Packit 90a5c9
            /* ??? */
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03091)
Packit 90a5c9
                         "post_config: mpm type unknown");
Packit 90a5c9
            break;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (!h2_mpm_supported() && !mpm_warned) {
Packit 90a5c9
        mpm_warned = 1;
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10034)
Packit 90a5c9
                     "The mpm module (%s) is not supported by mod_http2. The mpm determines "
Packit 90a5c9
                     "how things are processed in your server. HTTP/2 has more demands in "
Packit 90a5c9
                     "this regard and the currently selected mpm will just not do. "
Packit 90a5c9
                     "This is an advisory warning. Your server will continue to work, but "
Packit 90a5c9
                     "the HTTP/2 protocol will be inactive.", 
Packit 90a5c9
                     h2_conn_mpm_name());
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    status = h2_h2_init(p, s);
Packit 90a5c9
    if (status == APR_SUCCESS) {
Packit 90a5c9
        status = h2_switch_init(p, s);
Packit 90a5c9
    }
Packit 90a5c9
    if (status == APR_SUCCESS) {
Packit 90a5c9
        status = h2_task_init(p, s);
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    return status;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static char *http2_var_lookup(apr_pool_t *, server_rec *,
Packit 90a5c9
                         conn_rec *, request_rec *, char *name);
Packit 90a5c9
static int http2_is_h2(conn_rec *);
Packit 90a5c9
Packit 90a5c9
static apr_status_t http2_req_engine_push(const char *ngn_type, 
Packit 90a5c9
                                          request_rec *r, 
Packit 90a5c9
                                          http2_req_engine_init *einit)
Packit 90a5c9
{
Packit 90a5c9
    return h2_mplx_req_engine_push(ngn_type, r, einit);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t http2_req_engine_pull(h2_req_engine *ngn, 
Packit 90a5c9
                                          apr_read_type_e block, 
Packit 90a5c9
                                          int capacity, 
Packit 90a5c9
                                          request_rec **pr)
Packit 90a5c9
{
Packit 90a5c9
    return h2_mplx_req_engine_pull(ngn, block, capacity, pr);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void http2_req_engine_done(h2_req_engine *ngn, conn_rec *r_conn,
Packit 90a5c9
                                  apr_status_t status)
Packit 90a5c9
{
Packit 90a5c9
    h2_mplx_req_engine_done(ngn, r_conn, status);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void http2_get_num_workers(server_rec *s, int *minw, int *maxw)
Packit 90a5c9
{
Packit 90a5c9
    h2_get_num_workers(s, minw, maxw);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* Runs once per created child process. Perform any process 
Packit 90a5c9
 * related initionalization here.
Packit 90a5c9
 */
Packit 90a5c9
static void h2_child_init(apr_pool_t *pool, server_rec *s)
Packit 90a5c9
{
Packit 90a5c9
    /* Set up our connection processing */
Packit 90a5c9
    apr_status_t status = h2_conn_child_init(pool, s);
Packit 90a5c9
    if (status != APR_SUCCESS) {
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
Packit 90a5c9
                     APLOGNO(02949) "initializing connection handling");
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* Install this module into the apache2 infrastructure.
Packit 90a5c9
 */
Packit 90a5c9
static void h2_hooks(apr_pool_t *pool)
Packit 90a5c9
{
Packit 90a5c9
    static const char *const mod_ssl[] = { "mod_ssl.c", NULL};
Packit 90a5c9
    
Packit 90a5c9
    APR_REGISTER_OPTIONAL_FN(http2_is_h2);
Packit 90a5c9
    APR_REGISTER_OPTIONAL_FN(http2_var_lookup);
Packit 90a5c9
    APR_REGISTER_OPTIONAL_FN(http2_req_engine_push);
Packit 90a5c9
    APR_REGISTER_OPTIONAL_FN(http2_req_engine_pull);
Packit 90a5c9
    APR_REGISTER_OPTIONAL_FN(http2_req_engine_done);
Packit 90a5c9
    APR_REGISTER_OPTIONAL_FN(http2_get_num_workers);
Packit 90a5c9
Packit 90a5c9
    ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks");
Packit 90a5c9
    
Packit 90a5c9
    /* Run once after configuration is set, but before mpm children initialize.
Packit 90a5c9
     */
Packit 90a5c9
    ap_hook_post_config(h2_post_config, mod_ssl, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
    
Packit 90a5c9
    /* Run once after a child process has been created.
Packit 90a5c9
     */
Packit 90a5c9
    ap_hook_child_init(h2_child_init, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
Packit 90a5c9
    h2_h2_register_hooks();
Packit 90a5c9
    h2_switch_register_hooks();
Packit 90a5c9
    h2_task_register_hooks();
Packit 90a5c9
Packit 90a5c9
    h2_alt_svc_register_hooks();
Packit 90a5c9
    
Packit 90a5c9
    /* Setup subprocess env for certain variables 
Packit 90a5c9
     */
Packit 90a5c9
    ap_hook_fixups(h2_h2_fixups, NULL,NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
    
Packit 90a5c9
    /* test http2 connection status handler */
Packit 90a5c9
    ap_hook_handler(h2_filter_h2_status_handler, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *val_HTTP2(apr_pool_t *p, server_rec *s,
Packit 90a5c9
                             conn_rec *c, request_rec *r, h2_ctx *ctx)
Packit 90a5c9
{
Packit 90a5c9
    return ctx? "on" : "off";
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *val_H2_PUSH(apr_pool_t *p, server_rec *s,
Packit 90a5c9
                               conn_rec *c, request_rec *r, h2_ctx *ctx)
Packit 90a5c9
{
Packit 90a5c9
    if (ctx) {
Packit 90a5c9
        if (r) {
Packit 90a5c9
            h2_task *task = h2_ctx_get_task(ctx);
Packit 90a5c9
            if (task) {
Packit 90a5c9
                h2_stream *stream = h2_mplx_stream_get(task->mplx, task->stream_id);
Packit 90a5c9
                if (stream && stream->push_policy != H2_PUSH_NONE) {
Packit 90a5c9
                    return "on";
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else if (c && h2_session_push_enabled(ctx->session)) {
Packit 90a5c9
            return "on";
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    else if (s) {
Packit 90a5c9
        const h2_config *cfg = h2_config_sget(s);
Packit 90a5c9
        if (cfg && h2_config_geti(cfg, H2_CONF_PUSH)) {
Packit 90a5c9
            return "on";
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return "off";
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *val_H2_PUSHED(apr_pool_t *p, server_rec *s,
Packit 90a5c9
                                 conn_rec *c, request_rec *r, h2_ctx *ctx)
Packit 90a5c9
{
Packit 90a5c9
    if (ctx) {
Packit 90a5c9
        h2_task *task = h2_ctx_get_task(ctx);
Packit 90a5c9
        if (task && !H2_STREAM_CLIENT_INITIATED(task->stream_id)) {
Packit 90a5c9
            return "PUSHED";
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return "";
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *val_H2_PUSHED_ON(apr_pool_t *p, server_rec *s,
Packit 90a5c9
                                    conn_rec *c, request_rec *r, h2_ctx *ctx)
Packit 90a5c9
{
Packit 90a5c9
    if (ctx) {
Packit 90a5c9
        h2_task *task = h2_ctx_get_task(ctx);
Packit 90a5c9
        if (task && !H2_STREAM_CLIENT_INITIATED(task->stream_id)) {
Packit 90a5c9
            h2_stream *stream = h2_mplx_stream_get(task->mplx, task->stream_id);
Packit 90a5c9
            if (stream) {
Packit 90a5c9
                return apr_itoa(p, stream->initiated_on);
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return "";
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *val_H2_STREAM_TAG(apr_pool_t *p, server_rec *s,
Packit 90a5c9
                                     conn_rec *c, request_rec *r, h2_ctx *ctx)
Packit 90a5c9
{
Packit 90a5c9
    if (ctx) {
Packit 90a5c9
        h2_task *task = h2_ctx_get_task(ctx);
Packit 90a5c9
        if (task) {
Packit 90a5c9
            return task->id;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return "";
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *val_H2_STREAM_ID(apr_pool_t *p, server_rec *s,
Packit 90a5c9
                                    conn_rec *c, request_rec *r, h2_ctx *ctx)
Packit 90a5c9
{
Packit 90a5c9
    const char *cp = val_H2_STREAM_TAG(p, s, c, r, ctx);
Packit 90a5c9
    if (cp && (cp = ap_strchr_c(cp, '-'))) {
Packit 90a5c9
        return ++cp;
Packit 90a5c9
    }
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
typedef const char *h2_var_lookup(apr_pool_t *p, server_rec *s,
Packit 90a5c9
                                  conn_rec *c, request_rec *r, h2_ctx *ctx);
Packit 90a5c9
typedef struct h2_var_def {
Packit 90a5c9
    const char *name;
Packit 90a5c9
    h2_var_lookup *lookup;
Packit 90a5c9
    unsigned int  subprocess : 1;    /* should be set in r->subprocess_env */
Packit 90a5c9
} h2_var_def;
Packit 90a5c9
Packit 90a5c9
static h2_var_def H2_VARS[] = {
Packit 90a5c9
    { "HTTP2",               val_HTTP2,  1 },
Packit 90a5c9
    { "H2PUSH",              val_H2_PUSH, 1 },
Packit 90a5c9
    { "H2_PUSH",             val_H2_PUSH, 1 },
Packit 90a5c9
    { "H2_PUSHED",           val_H2_PUSHED, 1 },
Packit 90a5c9
    { "H2_PUSHED_ON",        val_H2_PUSHED_ON, 1 },
Packit 90a5c9
    { "H2_STREAM_ID",        val_H2_STREAM_ID, 1 },
Packit 90a5c9
    { "H2_STREAM_TAG",       val_H2_STREAM_TAG, 1 },
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
#ifndef H2_ALEN
Packit 90a5c9
#define H2_ALEN(a)          (sizeof(a)/sizeof((a)[0]))
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static int http2_is_h2(conn_rec *c)
Packit 90a5c9
{
Packit 90a5c9
    return h2_ctx_get(c->master? c->master : c, 0) != NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static char *http2_var_lookup(apr_pool_t *p, server_rec *s,
Packit 90a5c9
                              conn_rec *c, request_rec *r, char *name)
Packit 90a5c9
{
Packit 90a5c9
    int i;
Packit 90a5c9
    /* If the # of vars grow, we need to put definitions in a hash */
Packit 90a5c9
    for (i = 0; i < H2_ALEN(H2_VARS); ++i) {
Packit 90a5c9
        h2_var_def *vdef = &H2_VARS[i];
Packit 90a5c9
        if (!strcmp(vdef->name, name)) {
Packit 90a5c9
            h2_ctx *ctx = (r? h2_ctx_rget(r) : 
Packit 90a5c9
                           h2_ctx_get(c->master? c->master : c, 0));
Packit 90a5c9
            return (char *)vdef->lookup(p, s, c, r, ctx);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return (char*)"";
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int h2_h2_fixups(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    if (r->connection->master) {
Packit 90a5c9
        h2_ctx *ctx = h2_ctx_rget(r);
Packit 90a5c9
        int i;
Packit 90a5c9
        
Packit 90a5c9
        for (i = 0; ctx && i < H2_ALEN(H2_VARS); ++i) {
Packit 90a5c9
            h2_var_def *vdef = &H2_VARS[i];
Packit 90a5c9
            if (vdef->subprocess) {
Packit 90a5c9
                apr_table_setn(r->subprocess_env, vdef->name, 
Packit 90a5c9
                               vdef->lookup(r->pool, r->server, r->connection, 
Packit 90a5c9
                                            r, ctx));
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return DECLINED;
Packit 90a5c9
}