Blame modules/http2/mod_proxy_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 <nghttp2/nghttp2.h>
Packit 90a5c9
Packit 90a5c9
#include <httpd.h>
Packit 90a5c9
#include <mod_proxy.h>
Packit 90a5c9
#include "mod_http2.h"
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
#include "mod_proxy_http2.h"
Packit 90a5c9
#include "h2_request.h"
Packit 90a5c9
#include "h2_proxy_util.h"
Packit 90a5c9
#include "h2_version.h"
Packit 90a5c9
#include "h2_proxy_session.h"
Packit 90a5c9
Packit 90a5c9
#define H2MIN(x,y) ((x) < (y) ? (x) : (y))
Packit 90a5c9
Packit 90a5c9
static void register_hook(apr_pool_t *p);
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(proxy_http2) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    NULL,              /* create per-directory config structure */
Packit 90a5c9
    NULL,              /* merge per-directory config structures */
Packit 90a5c9
    NULL,              /* create per-server config structure */
Packit 90a5c9
    NULL,              /* merge per-server config structures */
Packit 90a5c9
    NULL,              /* command apr_table_t */
Packit 90a5c9
    register_hook,     /* register 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
/* Optional functions from mod_http2 */
Packit 90a5c9
static int (*is_h2)(conn_rec *c);
Packit 90a5c9
static apr_status_t (*req_engine_push)(const char *name, request_rec *r, 
Packit 90a5c9
                                       http2_req_engine_init *einit);
Packit 90a5c9
static apr_status_t (*req_engine_pull)(h2_req_engine *engine, 
Packit 90a5c9
                                       apr_read_type_e block, 
Packit 90a5c9
                                       int capacity, 
Packit 90a5c9
                                       request_rec **pr);
Packit 90a5c9
static void (*req_engine_done)(h2_req_engine *engine, conn_rec *r_conn,
Packit 90a5c9
                               apr_status_t status);
Packit 90a5c9
                                       
Packit 90a5c9
typedef struct h2_proxy_ctx {
Packit 90a5c9
    conn_rec *owner;
Packit 90a5c9
    apr_pool_t *pool;
Packit 90a5c9
    request_rec *rbase;
Packit 90a5c9
    server_rec *server;
Packit 90a5c9
    const char *proxy_func;
Packit 90a5c9
    char server_portstr[32];
Packit 90a5c9
    proxy_conn_rec *p_conn;
Packit 90a5c9
    proxy_worker *worker;
Packit 90a5c9
    proxy_server_conf *conf;
Packit 90a5c9
    
Packit 90a5c9
    h2_req_engine *engine;
Packit 90a5c9
    const char *engine_id;
Packit 90a5c9
    const char *engine_type;
Packit 90a5c9
    apr_pool_t *engine_pool;    
Packit 90a5c9
    apr_size_t req_buffer_size;
Packit 90a5c9
    h2_proxy_fifo *requests;
Packit 90a5c9
    int capacity;
Packit 90a5c9
    
Packit 90a5c9
    unsigned standalone : 1;
Packit 90a5c9
    unsigned is_ssl : 1;
Packit 90a5c9
    unsigned flushall : 1;
Packit 90a5c9
    
Packit 90a5c9
    apr_status_t r_status;     /* status of our first request work */
Packit 90a5c9
    h2_proxy_session *session; /* current http2 session against backend */
Packit 90a5c9
} h2_proxy_ctx;
Packit 90a5c9
Packit 90a5c9
static int h2_proxy_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 *init_key = "mod_proxy_http2_init_counter";
Packit 90a5c9
    nghttp2_info *ngh2;
Packit 90a5c9
    apr_status_t status = APR_SUCCESS;
Packit 90a5c9
    (void)plog;(void)ptemp;
Packit 90a5c9
    
Packit 90a5c9
    apr_pool_userdata_get(&data, init_key, s->process->pool);
Packit 90a5c9
    if ( data == NULL ) {
Packit 90a5c9
        apr_pool_userdata_set((const void *)1, 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(03349)
Packit 90a5c9
                 "mod_proxy_http2 (v%s, nghttp2 %s), initializing...",
Packit 90a5c9
                 MOD_HTTP2_VERSION, ngh2? ngh2->version_str : "unknown");
Packit 90a5c9
    
Packit 90a5c9
    is_h2 = APR_RETRIEVE_OPTIONAL_FN(http2_is_h2);
Packit 90a5c9
    req_engine_push = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_push);
Packit 90a5c9
    req_engine_pull = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_pull);
Packit 90a5c9
    req_engine_done = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_done);
Packit 90a5c9
    
Packit 90a5c9
    /* we need all of them */
Packit 90a5c9
    if (!req_engine_push || !req_engine_pull || !req_engine_done) {
Packit 90a5c9
        req_engine_push = NULL;
Packit 90a5c9
        req_engine_pull = NULL;
Packit 90a5c9
        req_engine_done = NULL;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    return status;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/**
Packit 90a5c9
 * canonicalize the url into the request, if it is meant for us.
Packit 90a5c9
 * slightly modified copy from mod_http
Packit 90a5c9
 */
Packit 90a5c9
static int proxy_http2_canon(request_rec *r, char *url)
Packit 90a5c9
{
Packit 90a5c9
    char *host, *path, sport[7];
Packit 90a5c9
    char *search = NULL;
Packit 90a5c9
    const char *err;
Packit 90a5c9
    const char *scheme;
Packit 90a5c9
    const char *http_scheme;
Packit 90a5c9
    apr_port_t port, def_port;
Packit 90a5c9
Packit 90a5c9
    /* ap_port_of_scheme() */
Packit 90a5c9
    if (ap_cstr_casecmpn(url, "h2c:", 4) == 0) {
Packit 90a5c9
        url += 4;
Packit 90a5c9
        scheme = "h2c";
Packit 90a5c9
        http_scheme = "http";
Packit 90a5c9
    }
Packit 90a5c9
    else if (ap_cstr_casecmpn(url, "h2:", 3) == 0) {
Packit 90a5c9
        url += 3;
Packit 90a5c9
        scheme = "h2";
Packit 90a5c9
        http_scheme = "https";
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    port = def_port = ap_proxy_port_of_scheme(http_scheme);
Packit 90a5c9
Packit 90a5c9
    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
Packit 90a5c9
                  "HTTP2: canonicalising URL %s", url);
Packit 90a5c9
Packit 90a5c9
    /* do syntatic check.
Packit 90a5c9
     * We break the URL into host, port, path, search
Packit 90a5c9
     */
Packit 90a5c9
    err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
Packit 90a5c9
    if (err) {
Packit 90a5c9
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03350)
Packit 90a5c9
                      "error parsing URL %s: %s", url, err);
Packit 90a5c9
        return HTTP_BAD_REQUEST;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * now parse path/search args, according to rfc1738:
Packit 90a5c9
     * process the path.
Packit 90a5c9
     *
Packit 90a5c9
     * In a reverse proxy, our URL has been processed, so canonicalise
Packit 90a5c9
     * unless proxy-nocanon is set to say it's raw
Packit 90a5c9
     * In a forward proxy, we have and MUST NOT MANGLE the original.
Packit 90a5c9
     */
Packit 90a5c9
    switch (r->proxyreq) {
Packit 90a5c9
    default: /* wtf are we doing here? */
Packit 90a5c9
    case PROXYREQ_REVERSE:
Packit 90a5c9
        if (apr_table_get(r->notes, "proxy-nocanon")) {
Packit 90a5c9
            path = url;   /* this is the raw path */
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            path = ap_proxy_canonenc(r->pool, url, (int)strlen(url),
Packit 90a5c9
                                     enc_path, 0, r->proxyreq);
Packit 90a5c9
            search = r->args;
Packit 90a5c9
        }
Packit 90a5c9
        break;
Packit 90a5c9
    case PROXYREQ_PROXY:
Packit 90a5c9
        path = url;
Packit 90a5c9
        break;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (path == NULL) {
Packit 90a5c9
        return HTTP_BAD_REQUEST;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (port != def_port) {
Packit 90a5c9
        apr_snprintf(sport, sizeof(sport), ":%d", port);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        sport[0] = '\0';
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
Packit 90a5c9
        host = apr_pstrcat(r->pool, "[", host, "]", NULL);
Packit 90a5c9
    }
Packit 90a5c9
    r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
Packit 90a5c9
            "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void out_consumed(void *baton, conn_rec *c, apr_off_t bytes)
Packit 90a5c9
{
Packit 90a5c9
    h2_proxy_ctx *ctx = baton;
Packit 90a5c9
    
Packit 90a5c9
    if (ctx->session) {
Packit 90a5c9
        h2_proxy_session_update_window(ctx->session, c, bytes);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t proxy_engine_init(h2_req_engine *engine, 
Packit 90a5c9
                                        const char *id, 
Packit 90a5c9
                                        const char *type,
Packit 90a5c9
                                        apr_pool_t *pool, 
Packit 90a5c9
                                        apr_size_t req_buffer_size,
Packit 90a5c9
                                        request_rec *r,
Packit 90a5c9
                                        http2_output_consumed **pconsumed,
Packit 90a5c9
                                        void **pctx)
Packit 90a5c9
{
Packit 90a5c9
    h2_proxy_ctx *ctx = ap_get_module_config(r->connection->conn_config, 
Packit 90a5c9
                                             &proxy_http2_module);
Packit 90a5c9
    if (!ctx) {
Packit 90a5c9
        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(03368)
Packit 90a5c9
                      "h2_proxy_session, engine init, no ctx found");
Packit 90a5c9
        return APR_ENOTIMPL;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    ctx->pool = pool;
Packit 90a5c9
    ctx->engine = engine;
Packit 90a5c9
    ctx->engine_id = id;
Packit 90a5c9
    ctx->engine_type = type;
Packit 90a5c9
    ctx->engine_pool = pool;
Packit 90a5c9
    ctx->req_buffer_size = req_buffer_size;
Packit 90a5c9
    ctx->capacity = H2MIN(100, h2_proxy_fifo_capacity(ctx->requests));
Packit 90a5c9
    
Packit 90a5c9
    *pconsumed = out_consumed;
Packit 90a5c9
    *pctx = ctx;
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t add_request(h2_proxy_session *session, request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    h2_proxy_ctx *ctx = session->user_data;
Packit 90a5c9
    const char *url;
Packit 90a5c9
    apr_status_t status;
Packit 90a5c9
Packit 90a5c9
    url = apr_table_get(r->notes, H2_PROXY_REQ_URL_NOTE);
Packit 90a5c9
    apr_table_setn(r->notes, "proxy-source-port", apr_psprintf(r->pool, "%hu",
Packit 90a5c9
                   ctx->p_conn->connection->local_addr->port));
Packit 90a5c9
    status = h2_proxy_session_submit(session, url, r, ctx->standalone);
Packit 90a5c9
    if (status != APR_SUCCESS) {
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_ERR, status, r->connection, APLOGNO(03351)
Packit 90a5c9
                      "pass request body failed to %pI (%s) from %s (%s)",
Packit 90a5c9
                      ctx->p_conn->addr, ctx->p_conn->hostname ? 
Packit 90a5c9
                      ctx->p_conn->hostname: "", session->c->client_ip, 
Packit 90a5c9
                      session->c->remote_host ? session->c->remote_host: "");
Packit 90a5c9
    }
Packit 90a5c9
    return status;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void request_done(h2_proxy_ctx *ctx, request_rec *r,
Packit 90a5c9
                         apr_status_t status, int touched)
Packit 90a5c9
{   
Packit 90a5c9
    const char *task_id = apr_table_get(r->connection->notes, H2_TASK_ID_NOTE);
Packit 90a5c9
Packit 90a5c9
    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, r->connection, 
Packit 90a5c9
                  "h2_proxy_session(%s): request done %s, touched=%d",
Packit 90a5c9
                  ctx->engine_id, task_id, touched);
Packit 90a5c9
    if (status != APR_SUCCESS) {
Packit 90a5c9
        if (!touched) {
Packit 90a5c9
            /* untouched request, need rescheduling */
Packit 90a5c9
            status = h2_proxy_fifo_push(ctx->requests, r);
Packit 90a5c9
            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, 
Packit 90a5c9
                          APLOGNO(03369)
Packit 90a5c9
                          "h2_proxy_session(%s): rescheduled request %s",
Packit 90a5c9
                          ctx->engine_id, task_id);
Packit 90a5c9
            return;
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            const char *uri;
Packit 90a5c9
            uri = apr_uri_unparse(r->pool, &r->parsed_uri, 0);
Packit 90a5c9
            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, 
Packit 90a5c9
                          APLOGNO(03471) "h2_proxy_session(%s): request %s -> %s "
Packit 90a5c9
                          "not complete, cannot repeat", 
Packit 90a5c9
                          ctx->engine_id, task_id, uri);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (r == ctx->rbase) {
Packit 90a5c9
        ctx->r_status = ((status == APR_SUCCESS)? APR_SUCCESS
Packit 90a5c9
                         : HTTP_SERVICE_UNAVAILABLE);
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (req_engine_done && ctx->engine) {
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, 
Packit 90a5c9
                      APLOGNO(03370)
Packit 90a5c9
                      "h2_proxy_session(%s): finished request %s",
Packit 90a5c9
                      ctx->engine_id, task_id);
Packit 90a5c9
        req_engine_done(ctx->engine, r->connection, status);
Packit 90a5c9
    }
Packit 90a5c9
}    
Packit 90a5c9
Packit 90a5c9
static void session_req_done(h2_proxy_session *session, request_rec *r,
Packit 90a5c9
                             apr_status_t status, int touched)
Packit 90a5c9
{
Packit 90a5c9
    request_done(session->user_data, r, status, touched);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t next_request(h2_proxy_ctx *ctx, int before_leave)
Packit 90a5c9
{
Packit 90a5c9
    if (h2_proxy_fifo_count(ctx->requests) > 0) {
Packit 90a5c9
        return APR_SUCCESS;
Packit 90a5c9
    }
Packit 90a5c9
    else if (req_engine_pull && ctx->engine) {
Packit 90a5c9
        apr_status_t status;
Packit 90a5c9
        request_rec *r = NULL;
Packit 90a5c9
        
Packit 90a5c9
        status = req_engine_pull(ctx->engine, before_leave? 
Packit 90a5c9
                                 APR_BLOCK_READ: APR_NONBLOCK_READ, 
Packit 90a5c9
                                 ctx->capacity, &r);
Packit 90a5c9
        if (status == APR_SUCCESS && r) {
Packit 90a5c9
            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, ctx->owner, 
Packit 90a5c9
                          "h2_proxy_engine(%s): pulled request (%s) %s", 
Packit 90a5c9
                          ctx->engine_id, 
Packit 90a5c9
                          before_leave? "before leave" : "regular", 
Packit 90a5c9
                          r->the_request);
Packit 90a5c9
            h2_proxy_fifo_push(ctx->requests, r);
Packit 90a5c9
        }
Packit 90a5c9
        return APR_STATUS_IS_EAGAIN(status)? APR_SUCCESS : status;
Packit 90a5c9
    }
Packit 90a5c9
    return APR_EOF;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t proxy_engine_run(h2_proxy_ctx *ctx) {
Packit 90a5c9
    apr_status_t status = OK;
Packit 90a5c9
    int h2_front;
Packit 90a5c9
    request_rec *r;
Packit 90a5c9
    
Packit 90a5c9
    /* Step Four: Send the Request in a new HTTP/2 stream and
Packit 90a5c9
     * loop until we got the response or encounter errors.
Packit 90a5c9
     */
Packit 90a5c9
    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner, 
Packit 90a5c9
                  "eng(%s): setup session", ctx->engine_id);
Packit 90a5c9
    h2_front = is_h2? is_h2(ctx->owner) : 0;
Packit 90a5c9
    ctx->session = h2_proxy_session_setup(ctx->engine_id, ctx->p_conn, ctx->conf,
Packit 90a5c9
                                          h2_front, 30, 
Packit 90a5c9
                                          h2_proxy_log2((int)ctx->req_buffer_size), 
Packit 90a5c9
                                          session_req_done);
Packit 90a5c9
    if (!ctx->session) {
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, 
Packit 90a5c9
                      APLOGNO(03372) "session unavailable");
Packit 90a5c9
        return HTTP_SERVICE_UNAVAILABLE;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03373)
Packit 90a5c9
                  "eng(%s): run session %s", ctx->engine_id, ctx->session->id);
Packit 90a5c9
    ctx->session->user_data = ctx;
Packit 90a5c9
    
Packit 90a5c9
    while (!ctx->owner->aborted) {
Packit 90a5c9
        if (APR_SUCCESS == h2_proxy_fifo_try_pull(ctx->requests, (void**)&r)) {
Packit 90a5c9
            add_request(ctx->session, r);
Packit 90a5c9
        }
Packit 90a5c9
        
Packit 90a5c9
        status = h2_proxy_session_process(ctx->session);
Packit 90a5c9
        
Packit 90a5c9
        if (status == APR_SUCCESS) {
Packit 90a5c9
            apr_status_t s2;
Packit 90a5c9
            /* ongoing processing, call again */
Packit 90a5c9
            if (ctx->session->remote_max_concurrent > 0
Packit 90a5c9
                && ctx->session->remote_max_concurrent != ctx->capacity) {
Packit 90a5c9
                ctx->capacity = H2MIN((int)ctx->session->remote_max_concurrent, 
Packit 90a5c9
                                      h2_proxy_fifo_capacity(ctx->requests));
Packit 90a5c9
            }
Packit 90a5c9
            s2 = next_request(ctx, 0);
Packit 90a5c9
            if (s2 == APR_ECONNABORTED) {
Packit 90a5c9
                /* master connection gone */
Packit 90a5c9
                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, s2, ctx->owner, 
Packit 90a5c9
                              APLOGNO(03374) "eng(%s): pull request", 
Packit 90a5c9
                              ctx->engine_id);
Packit 90a5c9
                /* give notice that we're leaving and cancel all ongoing
Packit 90a5c9
                 * streams. */
Packit 90a5c9
                next_request(ctx, 1); 
Packit 90a5c9
                h2_proxy_session_cancel_all(ctx->session);
Packit 90a5c9
                h2_proxy_session_process(ctx->session);
Packit 90a5c9
                status = ctx->r_status = APR_SUCCESS;
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
            if ((h2_proxy_fifo_count(ctx->requests) == 0) 
Packit 90a5c9
                && h2_proxy_ihash_empty(ctx->session->streams)) {
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            /* end of processing, maybe error */
Packit 90a5c9
            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, 
Packit 90a5c9
                          APLOGNO(03375) "eng(%s): end of session %s", 
Packit 90a5c9
                          ctx->engine_id, ctx->session->id);
Packit 90a5c9
            /*
Packit 90a5c9
             * Any open stream of that session needs to
Packit 90a5c9
             * a) be reopened on the new session iff safe to do so
Packit 90a5c9
             * b) reported as done (failed) otherwise
Packit 90a5c9
             */
Packit 90a5c9
            h2_proxy_session_cleanup(ctx->session, session_req_done);
Packit 90a5c9
            break;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    ctx->session->user_data = NULL;
Packit 90a5c9
    ctx->session = NULL;
Packit 90a5c9
    
Packit 90a5c9
    return status;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t push_request_somewhere(h2_proxy_ctx *ctx, request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    conn_rec *c = ctx->owner;
Packit 90a5c9
    const char *engine_type, *hostname;
Packit 90a5c9
    
Packit 90a5c9
    hostname = (ctx->p_conn->ssl_hostname? 
Packit 90a5c9
                ctx->p_conn->ssl_hostname : ctx->p_conn->hostname);
Packit 90a5c9
    engine_type = apr_psprintf(ctx->pool, "proxy_http2 %s%s", hostname, 
Packit 90a5c9
                               ctx->server_portstr);
Packit 90a5c9
    
Packit 90a5c9
    if (c->master && req_engine_push && r && is_h2 && is_h2(c)) {
Packit 90a5c9
        /* If we are have req_engine capabilities, push the handling of this
Packit 90a5c9
         * request (e.g. slave connection) to a proxy_http2 engine which 
Packit 90a5c9
         * uses the same backend. We may be called to create an engine 
Packit 90a5c9
         * ourself. */
Packit 90a5c9
        if (req_engine_push(engine_type, r, proxy_engine_init) == APR_SUCCESS) {
Packit 90a5c9
            if (ctx->engine == NULL) {
Packit 90a5c9
                /* request has been assigned to an engine in another thread */
Packit 90a5c9
                return SUSPENDED;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (!ctx->engine) {
Packit 90a5c9
        /* No engine was available or has been initialized, handle this
Packit 90a5c9
         * request just by ourself. */
Packit 90a5c9
        ctx->engine_id = apr_psprintf(ctx->pool, "eng-proxy-%ld", c->id);
Packit 90a5c9
        ctx->engine_type = engine_type;
Packit 90a5c9
        ctx->engine_pool = ctx->pool;
Packit 90a5c9
        ctx->req_buffer_size = (32*1024);
Packit 90a5c9
        ctx->standalone = 1;
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, 
Packit 90a5c9
                      "h2_proxy_http2(%ld): setup standalone engine for type %s", 
Packit 90a5c9
                      c->id, engine_type);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, 
Packit 90a5c9
                      "H2: hosting engine %s", ctx->engine_id);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return h2_proxy_fifo_push(ctx->requests, r);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int proxy_http2_handler(request_rec *r, 
Packit 90a5c9
                               proxy_worker *worker,
Packit 90a5c9
                               proxy_server_conf *conf,
Packit 90a5c9
                               char *url, 
Packit 90a5c9
                               const char *proxyname,
Packit 90a5c9
                               apr_port_t proxyport)
Packit 90a5c9
{
Packit 90a5c9
    const char *proxy_func;
Packit 90a5c9
    char *locurl = url, *u;
Packit 90a5c9
    apr_size_t slen;
Packit 90a5c9
    int is_ssl = 0;
Packit 90a5c9
    apr_status_t status;
Packit 90a5c9
    h2_proxy_ctx *ctx;
Packit 90a5c9
    apr_uri_t uri;
Packit 90a5c9
    int reconnects = 0;
Packit 90a5c9
    
Packit 90a5c9
    /* find the scheme */
Packit 90a5c9
    if ((url[0] != 'h' && url[0] != 'H') || url[1] != '2') {
Packit 90a5c9
       return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    u = strchr(url, ':');
Packit 90a5c9
    if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0') {
Packit 90a5c9
       return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    slen = (u - url);
Packit 90a5c9
    switch(slen) {
Packit 90a5c9
        case 2:
Packit 90a5c9
            proxy_func = "H2";
Packit 90a5c9
            is_ssl = 1;
Packit 90a5c9
            break;
Packit 90a5c9
        case 3:
Packit 90a5c9
            if (url[2] != 'c' && url[2] != 'C') {
Packit 90a5c9
                return DECLINED;
Packit 90a5c9
            }
Packit 90a5c9
            proxy_func = "H2C";
Packit 90a5c9
            break;
Packit 90a5c9
        default:
Packit 90a5c9
            return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    ctx = apr_pcalloc(r->pool, sizeof(*ctx));
Packit 90a5c9
    ctx->owner      = r->connection;
Packit 90a5c9
    ctx->pool       = r->pool;
Packit 90a5c9
    ctx->rbase      = r;
Packit 90a5c9
    ctx->server     = r->server;
Packit 90a5c9
    ctx->proxy_func = proxy_func;
Packit 90a5c9
    ctx->is_ssl     = is_ssl;
Packit 90a5c9
    ctx->worker     = worker;
Packit 90a5c9
    ctx->conf       = conf;
Packit 90a5c9
    ctx->flushall   = apr_table_get(r->subprocess_env, "proxy-flushall")? 1 : 0;
Packit 90a5c9
    ctx->r_status   = HTTP_SERVICE_UNAVAILABLE;
Packit 90a5c9
    
Packit 90a5c9
    h2_proxy_fifo_set_create(&ctx->requests, ctx->pool, 100);
Packit 90a5c9
    
Packit 90a5c9
    ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, ctx);
Packit 90a5c9
Packit 90a5c9
    /* scheme says, this is for us. */
Packit 90a5c9
    apr_table_setn(ctx->rbase->notes, H2_PROXY_REQ_URL_NOTE, url);
Packit 90a5c9
    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->rbase, 
Packit 90a5c9
                  "H2: serving URL %s", url);
Packit 90a5c9
    
Packit 90a5c9
run_connect:    
Packit 90a5c9
    /* Get a proxy_conn_rec from the worker, might be a new one, might
Packit 90a5c9
     * be one still open from another request, or it might fail if the
Packit 90a5c9
     * worker is stopped or in error. */
Packit 90a5c9
    if ((status = ap_proxy_acquire_connection(ctx->proxy_func, &ctx->p_conn,
Packit 90a5c9
                                              ctx->worker, ctx->server)) != OK) {
Packit 90a5c9
        goto cleanup;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ctx->p_conn->is_ssl = ctx->is_ssl;
Packit 90a5c9
    if (ctx->is_ssl && ctx->p_conn->connection) {
Packit 90a5c9
        /* If there are some metadata on the connection (e.g. TLS alert),
Packit 90a5c9
         * let mod_ssl detect them, and create a new connection below.
Packit 90a5c9
         */ 
Packit 90a5c9
        apr_bucket_brigade *tmp_bb;
Packit 90a5c9
        tmp_bb = apr_brigade_create(ctx->rbase->pool, 
Packit 90a5c9
                                    ctx->rbase->connection->bucket_alloc);
Packit 90a5c9
        status = ap_get_brigade(ctx->p_conn->connection->input_filters, tmp_bb,
Packit 90a5c9
                                AP_MODE_SPECULATIVE, APR_NONBLOCK_READ, 1);
Packit 90a5c9
        if (status != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(status)) {
Packit 90a5c9
            ctx->p_conn->close = 1;
Packit 90a5c9
        }
Packit 90a5c9
        apr_brigade_cleanup(tmp_bb);
Packit 90a5c9
    }   
Packit 90a5c9
Packit 90a5c9
    /* Step One: Determine the URL to connect to (might be a proxy),
Packit 90a5c9
     * initialize the backend accordingly and determine the server 
Packit 90a5c9
     * port string we can expect in responses. */
Packit 90a5c9
    if ((status = ap_proxy_determine_connection(ctx->pool, ctx->rbase, conf, worker, 
Packit 90a5c9
                                                ctx->p_conn, &uri, &locurl, 
Packit 90a5c9
                                                proxyname, proxyport, 
Packit 90a5c9
                                                ctx->server_portstr,
Packit 90a5c9
                                                sizeof(ctx->server_portstr))) != OK) {
Packit 90a5c9
        goto cleanup;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    /* If we are not already hosting an engine, try to push the request 
Packit 90a5c9
     * to an already existing engine or host a new engine here. */
Packit 90a5c9
    if (r && !ctx->engine) {
Packit 90a5c9
        ctx->r_status = push_request_somewhere(ctx, r);
Packit 90a5c9
        r = NULL;
Packit 90a5c9
        if (ctx->r_status == SUSPENDED) {
Packit 90a5c9
            /* request was pushed to another thread, leave processing here */
Packit 90a5c9
            goto cleanup;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    /* Step Two: Make the Connection (or check that an already existing
Packit 90a5c9
     * socket is still usable). On success, we have a socket connected to
Packit 90a5c9
     * backend->hostname. */
Packit 90a5c9
    if (ap_proxy_connect_backend(ctx->proxy_func, ctx->p_conn, ctx->worker, 
Packit 90a5c9
                                 ctx->server)) {
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03352)
Packit 90a5c9
                      "H2: failed to make connection to backend: %s",
Packit 90a5c9
                      ctx->p_conn->hostname);
Packit 90a5c9
        goto reconnect;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    /* Step Three: Create conn_rec for the socket we have open now. */
Packit 90a5c9
    if (!ctx->p_conn->connection) {
Packit 90a5c9
        status = ap_proxy_connection_create_ex(ctx->proxy_func,
Packit 90a5c9
                                               ctx->p_conn, ctx->rbase);
Packit 90a5c9
        if (status != OK) {
Packit 90a5c9
            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, APLOGNO(03353)
Packit 90a5c9
                          "setup new connection: is_ssl=%d %s %s %s", 
Packit 90a5c9
                          ctx->p_conn->is_ssl, ctx->p_conn->ssl_hostname, 
Packit 90a5c9
                          locurl, ctx->p_conn->hostname);
Packit 90a5c9
            goto reconnect;
Packit 90a5c9
        }
Packit 90a5c9
        
Packit 90a5c9
        if (!ctx->p_conn->data) {
Packit 90a5c9
            /* New conection: set a note on the connection what CN is
Packit 90a5c9
             * requested and what protocol we want */
Packit 90a5c9
            if (ctx->p_conn->ssl_hostname) {
Packit 90a5c9
                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, ctx->owner, 
Packit 90a5c9
                              "set SNI to %s for (%s)", 
Packit 90a5c9
                              ctx->p_conn->ssl_hostname, 
Packit 90a5c9
                              ctx->p_conn->hostname);
Packit 90a5c9
                apr_table_setn(ctx->p_conn->connection->notes,
Packit 90a5c9
                               "proxy-request-hostname", ctx->p_conn->ssl_hostname);
Packit 90a5c9
            }
Packit 90a5c9
            if (ctx->is_ssl) {
Packit 90a5c9
                apr_table_setn(ctx->p_conn->connection->notes,
Packit 90a5c9
                               "proxy-request-alpn-protos", "h2");
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
run_session:
Packit 90a5c9
    status = proxy_engine_run(ctx);
Packit 90a5c9
    if (status == APR_SUCCESS) {
Packit 90a5c9
        /* session and connection still ok */
Packit 90a5c9
        if (next_request(ctx, 1) == APR_SUCCESS) {
Packit 90a5c9
            /* more requests, run again */
Packit 90a5c9
            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03376)
Packit 90a5c9
                          "run_session, again");
Packit 90a5c9
            goto run_session;
Packit 90a5c9
        }
Packit 90a5c9
        /* done */
Packit 90a5c9
        ctx->engine = NULL;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
reconnect:
Packit 90a5c9
    if (next_request(ctx, 1) == APR_SUCCESS) {
Packit 90a5c9
        /* Still more to do, tear down old conn and start over */
Packit 90a5c9
        if (ctx->p_conn) {
Packit 90a5c9
            ctx->p_conn->close = 1;
Packit 90a5c9
            /*only in trunk so far */
Packit 90a5c9
            /*proxy_run_detach_backend(r, ctx->p_conn);*/
Packit 90a5c9
            ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server);
Packit 90a5c9
            ctx->p_conn = NULL;
Packit 90a5c9
        }
Packit 90a5c9
        ++reconnects;
Packit 90a5c9
        if (reconnects < 5 && !ctx->owner->aborted) {
Packit 90a5c9
            goto run_connect;
Packit 90a5c9
        } 
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(10023)
Packit 90a5c9
                      "giving up after %d reconnects, %d requests todo",
Packit 90a5c9
                      reconnects, h2_proxy_fifo_count(ctx->requests));
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
cleanup:
Packit 90a5c9
    if (ctx->p_conn) {
Packit 90a5c9
        if (status != APR_SUCCESS) {
Packit 90a5c9
            /* close socket when errors happened or session shut down (EOF) */
Packit 90a5c9
            ctx->p_conn->close = 1;
Packit 90a5c9
        }
Packit 90a5c9
        /*only in trunk so far */
Packit 90a5c9
        /*proxy_run_detach_backend(ctx->rbase, ctx->p_conn);*/
Packit 90a5c9
        ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server);
Packit 90a5c9
        ctx->p_conn = NULL;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Any requests will still have need to fail */
Packit 90a5c9
    while (APR_SUCCESS == h2_proxy_fifo_try_pull(ctx->requests, (void**)&r)) {
Packit 90a5c9
        request_done(ctx, r, HTTP_SERVICE_UNAVAILABLE, 1);
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, NULL);
Packit 90a5c9
    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, 
Packit 90a5c9
                  APLOGNO(03377) "leaving handler");
Packit 90a5c9
    return ctx->r_status;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void register_hook(apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    ap_hook_post_config(h2_proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
Packit 90a5c9
    proxy_hook_scheme_handler(proxy_http2_handler, NULL, NULL, APR_HOOK_FIRST);
Packit 90a5c9
    proxy_hook_canon_handler(proxy_http2_canon, NULL, NULL, APR_HOOK_FIRST);
Packit 90a5c9
}
Packit 90a5c9