Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Apache/modules/http2/   (Apache Software Stiftung Version 2.4.65©)  Datei vom 24.5.2024 mit Größe 32 kB image not shown  

Quelle  h2_c2.c   Sprache: C

 
/* 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 <assert.h>
#include <stddef.h>

#include <apr_atomic.h>
#include <apr_strings.h>

#include <httpd.h>
#include <http_core.h>
#include <http_config.h>
#include <http_connection.h>
#include <http_protocol.h>
#include <http_request.h>
#include <http_log.h>
#include <http_vhost.h>
#include <util_filter.h>
#include <ap_mmn.h>
#include <ap_mpm.h>
#include <mpm_common.h>
#include <mod_core.h>
#include <scoreboard.h>

#include "h2_private.h"
#include "h2.h"
#include "h2_bucket_beam.h"
#include "h2_c1.h"
#include "h2_config.h"
#include "h2_conn_ctx.h"
#include "h2_c2_filter.h"
#include "h2_protocol.h"
#include "h2_mplx.h"
#include "h2_request.h"
#include "h2_headers.h"
#include "h2_session.h"
#include "h2_stream.h"
#include "h2_ws.h"
#include "h2_c2.h"
#include "h2_util.h"
#include "mod_http2.h"


static module *mpm_module;
static int mpm_supported = 1;
static apr_socket_t *dummy_socket;

#if AP_HAS_RESPONSE_BUCKETS

static ap_filter_rec_t *c2_net_in_filter_handle;
static ap_filter_rec_t *c2_net_out_filter_handle;
static ap_filter_rec_t *c2_request_in_filter_handle;
static ap_filter_rec_t *c2_notes_out_filter_handle;

#endif /* AP_HAS_RESPONSE_BUCKETS */

static void check_modules(int force)
{
    static int checked = 0;
    int i;

    if (force || !checked) {
        for (i = 0; ap_loaded_modules[i]; ++i) {
            module *m = ap_loaded_modules[i];

            if (!strcmp("event.c", m->name)) {
                mpm_module = m;
                break;
            }
            else if (!strcmp("motorz.c", m->name)) {
                mpm_module = m;
                break;
            }
            else if (!strcmp("mpm_netware.c", m->name)) {
                mpm_module = m;
                break;
            }
            else if (!strcmp("prefork.c", m->name)) {
                mpm_module = m;
                /* While http2 can work really well on prefork, it collides
                 * today's use case for prefork: running single-thread app engines
                 * like php. If we restrict h2_workers to 1 per process, php will
                 * work fine, but browser will be limited to 1 active request at a
                 * time. */

                mpm_supported = 0;
                break;
            }
            else if (!strcmp("simple_api.c", m->name)) {
                mpm_module = m;
                mpm_supported = 0;
                break;
            }
            else if (!strcmp("mpm_winnt.c", m->name)) {
                mpm_module = m;
                break;
            }
            else if (!strcmp("worker.c", m->name)) {
                mpm_module = m;
                break;
            }
        }
        checked = 1;
    }
}

const char *h2_conn_mpm_name(void)
{
    check_modules(0);
    return mpm_module? mpm_module->name : "unknown";
}

int h2_mpm_supported(void)
{
    check_modules(0);
    return mpm_supported;
}

apr_status_t h2_c2_child_init(apr_pool_t *pool, server_rec *s)
{
    check_modules(1);
    return apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
                             APR_PROTO_TCP, pool);
}

static void h2_c2_log_io(conn_rec *c2, apr_off_t bytes_sent)
{
    if (bytes_sent && h2_c_logio_add_bytes_out) {
        h2_c_logio_add_bytes_out(c2, bytes_sent);
    }
}

void h2_c2_destroy(conn_rec *c2)
{
    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c2);

    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c2,
                  "h2_c2(%s): destroy", c2->log_id);
    if(!c2->aborted && conn_ctx && conn_ctx->bytes_sent) {
      h2_c2_log_io(c2, conn_ctx->bytes_sent);
    }
    apr_pool_destroy(c2->pool);
}

void h2_c2_abort(conn_rec *c2, conn_rec *from)
{
    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c2);

    AP_DEBUG_ASSERT(conn_ctx);
    AP_DEBUG_ASSERT(conn_ctx->stream_id);
    if(!c2->aborted && conn_ctx->bytes_sent) {
      h2_c2_log_io(c2, conn_ctx->bytes_sent);
    }

    if (conn_ctx->beam_in) {
        h2_beam_abort(conn_ctx->beam_in, from);
    }
    if (conn_ctx->beam_out) {
        h2_beam_abort(conn_ctx->beam_out, from);
    }
    c2->aborted = 1;
}

typedef struct {
    apr_bucket_brigade *bb;       /* c2: data in holding area */
    unsigned did_upgrade_eos:1;   /* for Upgrade, we added an extra EOS */
} h2_c2_fctx_in_t;

static apr_status_t h2_c2_filter_in(ap_filter_t* f,
                                           apr_bucket_brigade* bb,
                                           ap_input_mode_t mode,
                                           apr_read_type_e block,
                                           apr_off_t readbytes)
{
    h2_conn_ctx_t *conn_ctx;
    h2_c2_fctx_in_t *fctx = f->ctx;
    apr_status_t status = APR_SUCCESS;
    apr_bucket *b;
    apr_off_t bblen;
    apr_size_t rmax = (readbytes < APR_INT32_MAX)?
                       (apr_size_t)readbytes : APR_INT32_MAX;
    
    conn_ctx = h2_conn_ctx_get(f->c);
    AP_DEBUG_ASSERT(conn_ctx);

    if (mode == AP_MODE_INIT) {
        return ap_get_brigade(f->c->input_filters, bb, mode, block, readbytes);
    }
    
    if (f->c->aborted) {
        return APR_ECONNABORTED;
    }
    
    if (APLOGctrace3(f->c)) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, f->c,
                      "h2_c2_in(%s-%d): read, mode=%d, block=%d, readbytes=%ld",
                      conn_ctx->id, conn_ctx->stream_id, mode, block,
                      (long)readbytes);
    }

    if (!fctx) {
        fctx = apr_pcalloc(f->c->pool, sizeof(*fctx));
        f->ctx = fctx;
        fctx->bb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
        if (!conn_ctx->beam_in) {
            b = apr_bucket_eos_create(f->c->bucket_alloc);
            APR_BRIGADE_INSERT_TAIL(fctx->bb, b);
        }
    }

    /* If this is a HTTP Upgrade, it means the request we process
     * has not Content, although the stream is not necessarily closed.
     * On first read, we insert an EOS to signal processing that it
     * has the complete body. */

    if (conn_ctx->is_upgrade && !fctx->did_upgrade_eos) {
        b = apr_bucket_eos_create(f->c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(fctx->bb, b);
        fctx->did_upgrade_eos = 1;
    }

    while (APR_BRIGADE_EMPTY(fctx->bb)) {
        /* Get more input data for our request. */
        if (APLOGctrace2(f->c)) {
            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c,
                          "h2_c2_in(%s-%d): get more data from mplx, block=%d, "
                          "readbytes=%ld",
                          conn_ctx->id, conn_ctx->stream_id, block, (long)readbytes);
        }
        if (conn_ctx->beam_in) {
            if (conn_ctx->pipe_in[H2_PIPE_OUT]) {
receive:
                status = h2_beam_receive(conn_ctx->beam_in, f->c, fctx->bb, APR_NONBLOCK_READ,
                                         conn_ctx->mplx->stream_max_mem);
                if (APR_STATUS_IS_EAGAIN(status) && APR_BLOCK_READ == block) {
                    status = h2_util_wait_on_pipe(conn_ctx->pipe_in[H2_PIPE_OUT]);
                    if (APR_SUCCESS == status) {
                        goto receive;
                    }
                }
            }
            else {
                status = h2_beam_receive(conn_ctx->beam_in, f->c, fctx->bb, block,
                                         conn_ctx->mplx->stream_max_mem);
            }
        }
        else {
            status = APR_EOF;
        }
        
        if (APLOGctrace3(f->c)) {
            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, f->c,
                          "h2_c2_in(%s-%d): read returned",
                          conn_ctx->id, conn_ctx->stream_id);
        }
        if (APR_STATUS_IS_EAGAIN(status) 
            && (mode == AP_MODE_GETLINE || block == APR_BLOCK_READ)) {
            /* chunked input handling does not seem to like it if we
             * return with APR_EAGAIN from a GETLINE read... 
             * upload 100k test on test-ser.example.org hangs */

            status = APR_SUCCESS;
        }
        else if (APR_STATUS_IS_EOF(status)) {
            break;
        }
        else if (status != APR_SUCCESS) {
            conn_ctx->last_err = status;
            return status;
        }

        if (APLOGctrace3(f->c)) {
            h2_util_bb_log(f->c, conn_ctx->stream_id, APLOG_TRACE3,
                        "c2 input recv raw", fctx->bb);
        }
        if (h2_c_logio_add_bytes_in) {
            apr_brigade_length(bb, 0, &bblen);
            h2_c_logio_add_bytes_in(f->c, bblen);
        }
    }
    
    /* Nothing there, no more data to get. Return. */
    if (status == APR_EOF && APR_BRIGADE_EMPTY(fctx->bb)) {
        return status;
    }

    if (APLOGctrace3(f->c)) {
        h2_util_bb_log(f->c, conn_ctx->stream_id, APLOG_TRACE3,
                    "c2 input.bb", fctx->bb);
    }
           
    if (APR_BRIGADE_EMPTY(fctx->bb)) {
        if (APLOGctrace3(f->c)) {
            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, f->c,
                          "h2_c2_in(%s-%d): no data",
                          conn_ctx->id, conn_ctx->stream_id);
        }
        return (block == APR_NONBLOCK_READ)? APR_EAGAIN : APR_EOF;
    }
    
    if (mode == AP_MODE_EXHAUSTIVE) {
        /* return all we have */
        APR_BRIGADE_CONCAT(bb, fctx->bb);
    }
    else if (mode == AP_MODE_READBYTES) {
        status = h2_brigade_concat_length(bb, fctx->bb, rmax);
    }
    else if (mode == AP_MODE_SPECULATIVE) {
        status = h2_brigade_copy_length(bb, fctx->bb, rmax);
    }
    else if (mode == AP_MODE_GETLINE) {
        /* we are reading a single LF line, e.g. the HTTP headers. 
         * this has the nasty side effect to split the bucket, even
         * though it ends with CRLF and creates a 0 length bucket */

        status = apr_brigade_split_line(bb, fctx->bb, block,
                                        HUGE_STRING_LEN);
        if (APLOGctrace3(f->c)) {
            char buffer[1024];
            apr_size_t len = sizeof(buffer)-1;
            apr_brigade_flatten(bb, buffer, &len);
            buffer[len] = 0;
            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, f->c,
                          "h2_c2_in(%s-%d): getline: %s",
                          conn_ctx->id, conn_ctx->stream_id, buffer);
        }
    }
    else {
        /* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not
         * to support it. Seems to work. */

        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c,
                      APLOGNO(03472) 
                      "h2_c2_in(%s-%d), unsupported READ mode %d",
                      conn_ctx->id, conn_ctx->stream_id, mode);
        status = APR_ENOTIMPL;
    }
    
    if (APLOGctrace3(f->c)) {
        apr_brigade_length(bb, 0, &bblen);
        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, f->c,
                      "h2_c2_in(%s-%d): %ld data bytes",
                      conn_ctx->id, conn_ctx->stream_id, (long)bblen);
    }
    return status;
}

static apr_status_t beam_out(conn_rec *c2, h2_conn_ctx_t *conn_ctx, apr_bucket_brigade* bb)
{
    apr_off_t written = 0;
    apr_status_t rv;

    rv = h2_beam_send(conn_ctx->beam_out, c2, bb, APR_BLOCK_READ, &written);
    if (APR_STATUS_IS_EAGAIN(rv)) {
        rv = APR_SUCCESS;
    }
    return rv;
}

static apr_status_t h2_c2_filter_out(ap_filter_t* f, apr_bucket_brigade* bb)
{
    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(f->c);
    apr_status_t rv;

   if (bb == NULL) {
#if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1)
        f->c->data_in_output_filters = 0;
#endif
        return APR_SUCCESS;
    }

    ap_assert(conn_ctx);
#if AP_HAS_RESPONSE_BUCKETS
    if (!conn_ctx->has_final_response) {
        apr_bucket *e;

        for (e = APR_BRIGADE_FIRST(bb);
             e != APR_BRIGADE_SENTINEL(bb);
             e = APR_BUCKET_NEXT(e))
        {
            if (AP_BUCKET_IS_RESPONSE(e)) {
                ap_bucket_response *resp = e->data;
                if (resp->status >= 200) {
                    conn_ctx->has_final_response = 1;
                    break;
                }
            }
            if (APR_BUCKET_IS_EOS(e)) {
                break;
            }
        }
    }
#endif /* AP_HAS_RESPONSE_BUCKETS */
    rv = beam_out(f->c, conn_ctx, bb);

    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, f->c,
                  "h2_c2(%s-%d): output leave",
                  conn_ctx->id, conn_ctx->stream_id);
    if (APR_SUCCESS != rv) {
        h2_c2_abort(f->c, f->c);
    }
    return rv;
}

static int addn_headers(void *udata, const char *name, const char *value)
{
    apr_table_t *dest = udata;
    apr_table_addn(dest, name, value);
    return 1;
}

static void check_early_hints(request_rec *r, const char *tag)
{
    apr_array_header_t *push_list = h2_config_push_list(r);
    apr_table_t *early_headers = h2_config_early_headers(r);

    if (!r->expecting_100 &&
        ((push_list && push_list->nelts > 0) ||
         (early_headers && !apr_is_empty_table(early_headers)))) {
        int have_hints = 0, i;

        if (push_list && push_list->nelts > 0) {
            ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
                          "%s, early announcing %d resources for push",
                          tag, push_list->nelts);
            for (i = 0; i < push_list->nelts; ++i) {
                h2_push_res *push = &APR_ARRAY_IDX(push_list, i, h2_push_res);
                apr_table_add(r->headers_out, "Link",
                               apr_psprintf(r->pool, "<%s>; rel=preload%s",
                                            push->uri_ref, push->critical? "; critical" : ""));
            }
            have_hints = 1;
        }
        if (early_headers && !apr_is_empty_table(early_headers)) {
            apr_table_do(addn_headers, r->headers_out, early_headers, NULL);
            have_hints = 1;
        }

        if (have_hints) {
          int old_status;
          const char *old_line;

          if (h2_config_rgeti(r, H2_CONF_PUSH) == 0 &&
              h2_config_sgeti(r->server, H2_CONF_PUSH) != 0) {
              apr_table_setn(r->connection->notes, H2_PUSH_MODE_NOTE, "0");
          }
          old_status = r->status;
          old_line = r->status_line;
          r->status = 103;
          r->status_line = "103 Early Hints";
          ap_send_interim_response(r, 1);
          r->status = old_status;
          r->status_line = old_line;
        }
    }
}

static int c2_hook_fixups(request_rec *r)
{
    conn_rec *c2 = r->connection;
    h2_conn_ctx_t *conn_ctx;

    if (!c2->master || !(conn_ctx = h2_conn_ctx_get(c2)) || !conn_ctx->stream_id) {
        return DECLINED;
    }

    check_early_hints(r, "late_fixup");

    return DECLINED;
}

static apr_status_t http2_get_pollfd_from_conn(conn_rec *c,
                                               struct apr_pollfd_t *pfd,
                                               apr_interval_time_t *ptimeout)
{
#if H2_USE_PIPES
    if (c->master) {
        h2_conn_ctx_t *ctx = h2_conn_ctx_get(c);
        if (ctx) {
            if (ctx->beam_in && ctx->pipe_in[H2_PIPE_OUT]) {
                pfd->desc_type = APR_POLL_FILE;
                pfd->desc.f = ctx->pipe_in[H2_PIPE_OUT];
                if (ptimeout)
                    *ptimeout = h2_beam_timeout_get(ctx->beam_in);
            }
            else {
                /* no input */
                pfd->desc_type = APR_NO_DESC;
                if (ptimeout)
                    *ptimeout = -1;
            }
            return APR_SUCCESS;
        }
    }
#else
    (void)c;
    (void)pfd;
    (void)ptimeout;
#endif /* H2_USE_PIPES */
    return APR_ENOTIMPL;
}

#if AP_HAS_RESPONSE_BUCKETS

static void c2_pre_read_request(request_rec *r, conn_rec *c2)
{
    h2_conn_ctx_t *conn_ctx;

    if (!c2->master || !(conn_ctx = h2_conn_ctx_get(c2)) || !conn_ctx->stream_id) {
        return;
    }
    ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
                  "h2_c2(%s-%d): adding request filters",
                  conn_ctx->id, conn_ctx->stream_id);
    ap_add_input_filter_handle(c2_request_in_filter_handle, NULL, r, r->connection);
    ap_add_output_filter_handle(c2_notes_out_filter_handle, NULL, r, r->connection);
}

static int c2_post_read_request(request_rec *r)
{
    h2_conn_ctx_t *conn_ctx;
    conn_rec *c2 = r->connection;
    apr_time_t timeout;

    if (!c2->master || !(conn_ctx = h2_conn_ctx_get(c2)) || !conn_ctx->stream_id) {
        return DECLINED;
    }
    /* Now that the request_rec is fully initialized, set relevant params */
    conn_ctx->server = r->server;
    timeout = h2_config_geti64(r, r->server, H2_CONF_STREAM_TIMEOUT);
    if (timeout <= 0) {
        timeout = r->server->timeout;
    }
    h2_conn_ctx_set_timeout(conn_ctx, timeout);
    /* We only handle this one request on the connection and tell everyone
     * that there is no need to keep it "clean" if something fails. Also,
     * this prevents mod_reqtimeout from doing funny business with monitoring
     * keepalive timeouts.
     */

    r->connection->keepalive = AP_CONN_CLOSE;

    if (conn_ctx->beam_in && !apr_table_get(r->headers_in, "Content-Length")) {
        r->body_indeterminate = 1;
    }

    if (h2_config_sgeti(conn_ctx->server, H2_CONF_COPY_FILES)) {
        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
                      "h2_mplx(%s-%d): copy_files in output",
                      conn_ctx->id, conn_ctx->stream_id);
        h2_beam_set_copy_files(conn_ctx->beam_out, 1);
    }

    /* Add the raw bytes of the request (e.g. header frame lengths to
     * the logio for this request. */

    if (conn_ctx->request->raw_bytes && h2_c_logio_add_bytes_in) {
        h2_c_logio_add_bytes_in(c2, conn_ctx->request->raw_bytes);
    }
    return OK;
}

static int c2_hook_pre_connection(conn_rec *c2, void *csd)
{
    h2_conn_ctx_t *conn_ctx;

    if (!c2->master || !(conn_ctx = h2_conn_ctx_get(c2)) || !conn_ctx->stream_id) {
        return DECLINED;
    }

    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c2,
                  "h2_c2(%s-%d), adding filters",
                  conn_ctx->id, conn_ctx->stream_id);
    ap_add_input_filter_handle(c2_net_in_filter_handle, NULL, NULL, c2);
    ap_add_output_filter_handle(c2_net_out_filter_handle, NULL, NULL, c2);
    if (c2->keepalives == 0) {
        /* Simulate that we had already a request on this connection. Some
         * hooks trigger special behaviour when keepalives is 0.
         * (Not necessarily in pre_connection, but later. Set it here, so it
         * is in place.) */

        c2->keepalives = 1;
        /* We signal that this connection will be closed after the request.
         * Which is true in that sense that we throw away all traffic data
         * on this c2 connection after each requests. Although we might
         * reuse internal structures like memory pools.
         * The wanted effect of this is that httpd does not try to clean up
         * any dangling data on this connection when a request is done. Which
         * is unnecessary on a h2 stream.
         */

        c2->keepalive = AP_CONN_CLOSE;
    }
    return OK;
}

void h2_c2_register_hooks(void)
{
    /* When the connection processing actually starts, we might
     * take over, if the connection is for a h2 stream.
     */

    ap_hook_pre_connection(c2_hook_pre_connection,
                           NULL, NULL, APR_HOOK_MIDDLE);

    /* We need to manipulate the standard HTTP/1.1 protocol filters and
     * install our own. This needs to be done very early. */

    ap_hook_pre_read_request(c2_pre_read_request, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_post_read_request(c2_post_read_request, NULL, NULL,
                              APR_HOOK_REALLY_FIRST);
    ap_hook_fixups(c2_hook_fixups, NULL, NULL, APR_HOOK_LAST);
#if H2_USE_POLLFD_FROM_CONN
    ap_hook_get_pollfd_from_conn(http2_get_pollfd_from_conn, NULL, NULL,
                                 APR_HOOK_MIDDLE);
#endif
    APR_REGISTER_OPTIONAL_FN(http2_get_pollfd_from_conn);

    c2_net_in_filter_handle =
        ap_register_input_filter("H2_C2_NET_IN", h2_c2_filter_in,
                                 NULL, AP_FTYPE_NETWORK);
    c2_net_out_filter_handle =
        ap_register_output_filter("H2_C2_NET_OUT", h2_c2_filter_out,
                                  NULL, AP_FTYPE_NETWORK);
    c2_request_in_filter_handle =
        ap_register_input_filter("H2_C2_REQUEST_IN", h2_c2_filter_request_in,
                                 NULL, AP_FTYPE_PROTOCOL);
    c2_notes_out_filter_handle =
        ap_register_output_filter("H2_C2_NOTES_OUT", h2_c2_filter_notes_out,
                                  NULL, AP_FTYPE_PROTOCOL);
}

#else /* AP_HAS_RESPONSE_BUCKETS */

static apr_status_t c2_run_pre_connection(conn_rec *c2, apr_socket_t *csd)
{
    if (c2->keepalives == 0) {
        /* Simulate that we had already a request on this connection. Some
         * hooks trigger special behaviour when keepalives is 0.
         * (Not necessarily in pre_connection, but later. Set it here, so it
         * is in place.) */

        c2->keepalives = 1;
        /* We signal that this connection will be closed after the request.
         * Which is true in that sense that we throw away all traffic data
         * on this c2 connection after each requests. Although we might
         * reuse internal structures like memory pools.
         * The wanted effect of this is that httpd does not try to clean up
         * any dangling data on this connection when a request is done. Which
         * is unnecessary on a h2 stream.
         */

        c2->keepalive = AP_CONN_CLOSE;
        return ap_run_pre_connection(c2, csd);
    }
    ap_assert(c2->output_filters);
    return APR_SUCCESS;
}

apr_status_t h2_c2_process(conn_rec *c2, apr_thread_t *thread, int worker_id)
{
    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c2);

    ap_assert(conn_ctx);
    ap_assert(conn_ctx->mplx);

    /* See the discussion at <https://github.com/icing/mod_h2/issues/195>
     *
     * Each conn_rec->id is supposed to be unique at a point in time. Since
     * some modules (and maybe external code) uses this id as an identifier
     * for the request_rec they handle, it needs to be unique for secondary
     * connections also.
     *
     * The MPM module assigns the connection ids and mod_unique_id is using
     * that one to generate identifier for requests. While the implementation
     * works for HTTP/1.x, the parallel execution of several requests per
     * connection will generate duplicate identifiers on load.
     *
     * The original implementation for secondary connection identifiers used
     * to shift the master connection id up and assign the stream id to the
     * lower bits. This was cramped on 32 bit systems, but on 64bit there was
     * enough space.
     *
     * As issue 195 showed, mod_unique_id only uses the lower 32 bit of the
     * connection id, even on 64bit systems. Therefore collisions in request ids.
     *
     * The way master connection ids are generated, there is some space "at the
     * top" of the lower 32 bits on allmost all systems. If you have a setup
     * with 64k threads per child and 255 child processes, you live on the edge.
     *
     * The new implementation shifts 8 bits and XORs in the worker
     * id. This will experience collisions with > 256 h2 workers and heavy
     * load still. There seems to be no way to solve this in all possible
     * configurations by mod_h2 alone.
     */

    c2->id = (c2->master->id << 8)^worker_id;

    if (!conn_ctx->pre_conn_done) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c2,
                      "h2_c2(%s-%d), adding filters",
                      conn_ctx->id, conn_ctx->stream_id);
        ap_add_input_filter("H2_C2_NET_IN", NULL, NULL, c2);
        ap_add_output_filter("H2_C2_NET_CATCH_H1", NULL, NULL, c2);
        ap_add_output_filter("H2_C2_NET_OUT", NULL, NULL, c2);

        c2_run_pre_connection(c2, ap_get_conn_socket(c2));
        conn_ctx->pre_conn_done = 1;
    }

    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c2,
                  "h2_c2(%s-%d): process connection",
                  conn_ctx->id, conn_ctx->stream_id);

    c2->current_thread = thread;
    ap_run_process_connection(c2);

    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c2,
                  "h2_c2(%s-%d): processing done",
                  conn_ctx->id, conn_ctx->stream_id);

    return APR_SUCCESS;
}

static apr_status_t c2_process(h2_conn_ctx_t *conn_ctx, conn_rec *c)
{
    const h2_request *req = conn_ctx->request;
    conn_state_t *cs = c->cs;
    request_rec *r = NULL;
    const char *tenc;
    apr_time_t timeout;
    apr_status_t rv = APR_SUCCESS;

    if (req->protocol && !strcmp("websocket", req->protocol)) {
        req = h2_ws_rewrite_request(req, c, conn_ctx->beam_in == NULL);
        if (!req) {
            rv = APR_EGENERAL;
            goto cleanup;
        }
    }

    r = h2_create_request_rec(req, c, conn_ctx->beam_in == NULL);

    if (!r) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                      "h2_c2(%s-%d): create request_rec failed, r=NULL",
                      conn_ctx->id, conn_ctx->stream_id);
        goto cleanup;
    }
    if (r->status != HTTP_OK) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                      "h2_c2(%s-%d): create request_rec failed, r->status=%d",
                      conn_ctx->id, conn_ctx->stream_id, r->status);
        goto cleanup;
    }

    tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
    conn_ctx->input_chunked = tenc && ap_is_chunked(r->pool, tenc);

    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                  "h2_c2(%s-%d): created request_rec for %s",
                  conn_ctx->id, conn_ctx->stream_id, r->the_request);
    conn_ctx->server = r->server;
    timeout = h2_config_geti64(r, r->server, H2_CONF_STREAM_TIMEOUT);
    if (timeout <= 0) {
        timeout = r->server->timeout;
    }
    h2_conn_ctx_set_timeout(conn_ctx, timeout);

    if (h2_config_sgeti(conn_ctx->server, H2_CONF_COPY_FILES)) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                      "h2_mplx(%s-%d): copy_files in output",
                      conn_ctx->id, conn_ctx->stream_id);
        h2_beam_set_copy_files(conn_ctx->beam_out, 1);
    }

    ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);
    if (cs) {
        cs->state = CONN_STATE_HANDLER;
    }
    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                  "h2_c2(%s-%d): start process_request",
                  conn_ctx->id, conn_ctx->stream_id);

    /* Add the raw bytes of the request (e.g. header frame lengths to
     * the logio for this request. */

    if (req->raw_bytes && h2_c_logio_add_bytes_in) {
        h2_c_logio_add_bytes_in(c, req->raw_bytes);
    }

    ap_process_request(r);
    /* After the call to ap_process_request, the
     * request pool may have been deleted. */

    r = NULL;
    if (conn_ctx->beam_out) {
        h2_beam_close(conn_ctx->beam_out, c);
    }

    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                  "h2_c2(%s-%d): process_request done",
                  conn_ctx->id, conn_ctx->stream_id);
    if (cs)
        cs->state = CONN_STATE_WRITE_COMPLETION;

cleanup:
    return rv;
}

conn_rec *h2_c2_create(conn_rec *c1, apr_pool_t *parent,
                       apr_bucket_alloc_t *buckt_alloc)
{
    apr_pool_t *pool;
    conn_rec *c2;
    void *cfg;

    ap_assert(c1);
    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c1,
                  "h2_c2: create for c1(%ld)", c1->id);

    /* We create a pool with its own allocator to be used for
     * processing a request. This is the only way to have the processing
     * independent of its parent pool in the sense that it can work in
     * another thread.
     */

    apr_pool_create(&pool, parent);
    apr_pool_tag(pool, "h2_c2_conn");

    c2 = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
    memcpy(c2, c1, sizeof(conn_rec));

    c2->master                 = c1;
    c2->pool                   = pool;
    c2->conn_config            = ap_create_conn_config(pool);
    c2->notes                  = apr_table_make(pool, 5);
    c2->input_filters          = NULL;
    c2->output_filters         = NULL;
    c2->keepalives             = 0;
#if AP_MODULE_MAGIC_AT_LEAST(20180903, 1)
    c2->filter_conn_ctx        = NULL;
#endif
    c2->bucket_alloc           = apr_bucket_alloc_create(pool);
#if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1)
    c2->data_in_input_filters  = 0;
    c2->data_in_output_filters = 0;
#endif
    /* prevent mpm_event from making wrong assumptions about this connection,
     * like e.g. using its socket for an async read check. */

    c2->clogging_input_filters = 1;
    c2->log                    = NULL;
    c2->aborted                = 0;
    /* We cannot install the master connection socket on the secondary, as
     * modules mess with timeouts/blocking of the socket, with
     * unwanted side effects to the master connection processing.
     * Fortunately, since we never use the secondary socket, we can just install
     * a single, process-wide dummy and everyone is happy.
     */

    ap_set_module_config(c2->conn_config, &core_module, dummy_socket);
    /* TODO: these should be unique to this thread */
    c2->sbh = NULL; /*c1->sbh;*/
    /* TODO: not all mpm modules have learned about secondary connections yet.
     * copy their config from master to secondary.
     */

    if (mpm_module) {
        cfg = ap_get_module_config(c1->conn_config, mpm_module);
        ap_set_module_config(c2->conn_config, mpm_module, cfg);
    }

    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c2,
                  "h2_c2(%s): created", c2->log_id);
    return c2;
}

static int h2_c2_hook_post_read_request(request_rec *r)
{
    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(r->connection);

    if (conn_ctx && conn_ctx->stream_id && ap_is_initial_req(r)) {

        ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
                      "h2_c2(%s-%d): adding request filters",
                      conn_ctx->id, conn_ctx->stream_id);

        /* setup the correct filters to process the request for h2 */
        ap_add_input_filter("H2_C2_REQUEST_IN", NULL, r, r->connection);

        /* replace the core http filter that formats response headers
         * in HTTP/1 with our own that collects status and headers */

        ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");

        ap_add_output_filter("H2_C2_RESPONSE_OUT", NULL, r, r->connection);
        ap_add_output_filter("H2_C2_TRAILERS_OUT", NULL, r, r->connection);
    }
    return DECLINED;
}

static int h2_c2_hook_process(conn_rec* c)
{
    h2_conn_ctx_t *ctx;

    if (!c->master) {
        return DECLINED;
    }

    ctx = h2_conn_ctx_get(c);
    if (ctx->stream_id) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                      "h2_h2, processing request directly");
        c2_process(ctx, c);
        return DONE;
    }
    else {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                      "secondary_conn(%ld): no h2 stream assing?", c->id);
    }
    return DECLINED;
}

void h2_c2_register_hooks(void)
{
    /* When the connection processing actually starts, we might
     * take over, if the connection is for a h2 stream.
     */

    ap_hook_process_connection(h2_c2_hook_process,
                               NULL, NULL, APR_HOOK_FIRST);
    /* We need to manipulate the standard HTTP/1.1 protocol filters and
     * install our own. This needs to be done very early. */

    ap_hook_post_read_request(h2_c2_hook_post_read_request, NULL, NULL, APR_HOOK_REALLY_FIRST);
    ap_hook_fixups(c2_hook_fixups, NULL, NULL, APR_HOOK_LAST);
#if H2_USE_POLLFD_FROM_CONN
    ap_hook_get_pollfd_from_conn(http2_get_pollfd_from_conn, NULL, NULL,
                                 APR_HOOK_MIDDLE);
#endif
    APR_REGISTER_OPTIONAL_FN(http2_get_pollfd_from_conn);

    ap_register_input_filter("H2_C2_NET_IN", h2_c2_filter_in,
                             NULL, AP_FTYPE_NETWORK);
    ap_register_output_filter("H2_C2_NET_OUT", h2_c2_filter_out,
                              NULL, AP_FTYPE_NETWORK);
    ap_register_output_filter("H2_C2_NET_CATCH_H1", h2_c2_filter_catch_h1_out,
                              NULL, AP_FTYPE_NETWORK);

    ap_register_input_filter("H2_C2_REQUEST_IN", h2_c2_filter_request_in,
                             NULL, AP_FTYPE_PROTOCOL);
    ap_register_output_filter("H2_C2_RESPONSE_OUT", h2_c2_filter_response_out,
                              NULL, AP_FTYPE_PROTOCOL);
    ap_register_output_filter("H2_C2_TRAILERS_OUT", h2_c2_filter_trailers_out,
                              NULL, AP_FTYPE_PROTOCOL);
}

#endif /* else AP_HAS_RESPONSE_BUCKETS */

99%


¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.