Blame modules/http2/h2_filter.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 <httpd.h>
Packit 90a5c9
#include <http_core.h>
Packit 90a5c9
#include <http_protocol.h>
Packit 90a5c9
#include <http_log.h>
Packit 90a5c9
#include <http_connection.h>
Packit 90a5c9
#include <scoreboard.h>
Packit 90a5c9
Packit 90a5c9
#include "h2_private.h"
Packit 90a5c9
#include "h2.h"
Packit 90a5c9
#include "h2_config.h"
Packit 90a5c9
#include "h2_conn_io.h"
Packit 90a5c9
#include "h2_ctx.h"
Packit 90a5c9
#include "h2_mplx.h"
Packit 90a5c9
#include "h2_push.h"
Packit 90a5c9
#include "h2_task.h"
Packit 90a5c9
#include "h2_stream.h"
Packit 90a5c9
#include "h2_request.h"
Packit 90a5c9
#include "h2_headers.h"
Packit 90a5c9
#include "h2_stream.h"
Packit 90a5c9
#include "h2_session.h"
Packit 90a5c9
#include "h2_util.h"
Packit 90a5c9
#include "h2_version.h"
Packit 90a5c9
Packit 90a5c9
#include "h2_filter.h"
Packit 90a5c9
Packit 90a5c9
#define UNSET       -1
Packit 90a5c9
#define H2MIN(x,y) ((x) < (y) ? (x) : (y))
Packit 90a5c9
Packit 90a5c9
static apr_status_t recv_RAW_DATA(conn_rec *c, h2_filter_cin *cin, 
Packit 90a5c9
                                  apr_bucket *b, apr_read_type_e block)
Packit 90a5c9
{
Packit 90a5c9
    h2_session *session = cin->session;
Packit 90a5c9
    apr_status_t status = APR_SUCCESS;
Packit 90a5c9
    apr_size_t len;
Packit 90a5c9
    const char *data;
Packit 90a5c9
    ssize_t n;
Packit 90a5c9
    
Packit 90a5c9
    status = apr_bucket_read(b, &data, &len, block);
Packit 90a5c9
    
Packit 90a5c9
    while (status == APR_SUCCESS && len > 0) {
Packit 90a5c9
        n = nghttp2_session_mem_recv(session->ngh2, (const uint8_t *)data, len);
Packit 90a5c9
        
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
Packit 90a5c9
                      H2_SSSN_MSG(session, "fed %ld bytes to nghttp2, %ld read"),
Packit 90a5c9
                      (long)len, (long)n);
Packit 90a5c9
        if (n < 0) {
Packit 90a5c9
            if (nghttp2_is_fatal((int)n)) {
Packit 90a5c9
                h2_session_event(session, H2_SESSION_EV_PROTO_ERROR, 
Packit 90a5c9
                                 (int)n, nghttp2_strerror((int)n));
Packit 90a5c9
                status = APR_EGENERAL;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            session->io.bytes_read += n;
Packit 90a5c9
            if (len <= n) {
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
            len -= n;
Packit 90a5c9
            data += n;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    return status;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t recv_RAW_brigade(conn_rec *c, h2_filter_cin *cin, 
Packit 90a5c9
                                     apr_bucket_brigade *bb, 
Packit 90a5c9
                                     apr_read_type_e block)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t status = APR_SUCCESS;
Packit 90a5c9
    apr_bucket* b;
Packit 90a5c9
    int consumed = 0;
Packit 90a5c9
    
Packit 90a5c9
    h2_util_bb_log(c, c->id, APLOG_TRACE2, "RAW_in", bb);
Packit 90a5c9
    while (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(bb)) {
Packit 90a5c9
        b = APR_BRIGADE_FIRST(bb);
Packit 90a5c9
Packit 90a5c9
        if (APR_BUCKET_IS_METADATA(b)) {
Packit 90a5c9
            /* nop */
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            status = recv_RAW_DATA(c, cin, b, block);
Packit 90a5c9
        }
Packit 90a5c9
        consumed = 1;
Packit 90a5c9
        apr_bucket_delete(b);
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (!consumed && status == APR_SUCCESS && block == APR_NONBLOCK_READ) {
Packit 90a5c9
        return APR_EAGAIN;
Packit 90a5c9
    }
Packit 90a5c9
    return status;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
h2_filter_cin *h2_filter_cin_create(h2_session *session)
Packit 90a5c9
{
Packit 90a5c9
    h2_filter_cin *cin;
Packit 90a5c9
    
Packit 90a5c9
    cin = apr_pcalloc(session->pool, sizeof(*cin));
Packit 90a5c9
    if (!cin) {
Packit 90a5c9
        return NULL;
Packit 90a5c9
    }
Packit 90a5c9
    cin->session = session;
Packit 90a5c9
    return cin;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
void h2_filter_cin_timeout_set(h2_filter_cin *cin, apr_interval_time_t timeout)
Packit 90a5c9
{
Packit 90a5c9
    cin->timeout = timeout;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
apr_status_t h2_filter_core_input(ap_filter_t* f,
Packit 90a5c9
                                  apr_bucket_brigade* brigade,
Packit 90a5c9
                                  ap_input_mode_t mode,
Packit 90a5c9
                                  apr_read_type_e block,
Packit 90a5c9
                                  apr_off_t readbytes) 
Packit 90a5c9
{
Packit 90a5c9
    h2_filter_cin *cin = f->ctx;
Packit 90a5c9
    apr_status_t status = APR_SUCCESS;
Packit 90a5c9
    apr_interval_time_t saved_timeout = UNSET;
Packit 90a5c9
    const int trace1 = APLOGctrace1(f->c);
Packit 90a5c9
    
Packit 90a5c9
    if (trace1) {
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
Packit 90a5c9
                      "h2_session(%ld): read, %s, mode=%d, readbytes=%ld", 
Packit 90a5c9
                      (long)f->c->id, (block == APR_BLOCK_READ)? 
Packit 90a5c9
                      "BLOCK_READ" : "NONBLOCK_READ", mode, (long)readbytes);
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (mode == AP_MODE_INIT || mode == AP_MODE_SPECULATIVE) {
Packit 90a5c9
        return ap_get_brigade(f->next, brigade, mode, block, readbytes);
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (mode != AP_MODE_READBYTES) {
Packit 90a5c9
        return (block == APR_BLOCK_READ)? APR_SUCCESS : APR_EAGAIN;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (!cin->bb) {
Packit 90a5c9
        cin->bb = apr_brigade_create(cin->session->pool, f->c->bucket_alloc);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!cin->socket) {
Packit 90a5c9
        cin->socket = ap_get_conn_socket(f->c);
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    if (APR_BRIGADE_EMPTY(cin->bb)) {
Packit 90a5c9
        /* We only do a blocking read when we have no streams to process. So,
Packit 90a5c9
         * in httpd scoreboard lingo, we are in a KEEPALIVE connection state.
Packit 90a5c9
         */
Packit 90a5c9
        if (block == APR_BLOCK_READ) {
Packit 90a5c9
            if (cin->timeout > 0) {
Packit 90a5c9
                apr_socket_timeout_get(cin->socket, &saved_timeout);
Packit 90a5c9
                apr_socket_timeout_set(cin->socket, cin->timeout);
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        status = ap_get_brigade(f->next, cin->bb, AP_MODE_READBYTES,
Packit 90a5c9
                                block, readbytes);
Packit 90a5c9
        if (saved_timeout != UNSET) {
Packit 90a5c9
            apr_socket_timeout_set(cin->socket, saved_timeout);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    switch (status) {
Packit 90a5c9
        case APR_SUCCESS:
Packit 90a5c9
            status = recv_RAW_brigade(f->c, cin, cin->bb, block);
Packit 90a5c9
            break;
Packit 90a5c9
        case APR_EOF:
Packit 90a5c9
        case APR_EAGAIN:
Packit 90a5c9
        case APR_TIMEUP:
Packit 90a5c9
            if (trace1) {
Packit 90a5c9
                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
Packit 90a5c9
                              "h2_session(%ld): read", f->c->id);
Packit 90a5c9
            }
Packit 90a5c9
            break;
Packit 90a5c9
        default:
Packit 90a5c9
            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, f->c, APLOGNO(03046)
Packit 90a5c9
                          "h2_session(%ld): error reading", f->c->id);
Packit 90a5c9
            break;
Packit 90a5c9
    }
Packit 90a5c9
    return status;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*******************************************************************************
Packit 90a5c9
 * http2 connection status handler + stream out source
Packit 90a5c9
 ******************************************************************************/
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    apr_bucket_refcount refcount;
Packit 90a5c9
    h2_bucket_event_cb *cb;
Packit 90a5c9
    void *ctx;
Packit 90a5c9
} h2_bucket_observer;
Packit 90a5c9
 
Packit 90a5c9
static apr_status_t bucket_read(apr_bucket *b, const char **str,
Packit 90a5c9
                                apr_size_t *len, apr_read_type_e block)
Packit 90a5c9
{
Packit 90a5c9
    (void)b;
Packit 90a5c9
    (void)block;
Packit 90a5c9
    *str = NULL;
Packit 90a5c9
    *len = 0;
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void bucket_destroy(void *data)
Packit 90a5c9
{
Packit 90a5c9
    h2_bucket_observer *h = data;
Packit 90a5c9
    if (apr_bucket_shared_destroy(h)) {
Packit 90a5c9
        if (h->cb) {
Packit 90a5c9
            h->cb(h->ctx, H2_BUCKET_EV_BEFORE_DESTROY, NULL);
Packit 90a5c9
        }
Packit 90a5c9
        apr_bucket_free(h);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
apr_bucket * h2_bucket_observer_make(apr_bucket *b, h2_bucket_event_cb *cb,
Packit 90a5c9
                                 void *ctx)
Packit 90a5c9
{
Packit 90a5c9
    h2_bucket_observer *br;
Packit 90a5c9
Packit 90a5c9
    br = apr_bucket_alloc(sizeof(*br), b->list);
Packit 90a5c9
    br->cb = cb;
Packit 90a5c9
    br->ctx = ctx;
Packit 90a5c9
Packit 90a5c9
    b = apr_bucket_shared_make(b, br, 0, 0);
Packit 90a5c9
    b->type = &h2_bucket_type_observer;
Packit 90a5c9
    return b;
Packit 90a5c9
} 
Packit 90a5c9
Packit 90a5c9
apr_bucket * h2_bucket_observer_create(apr_bucket_alloc_t *list, 
Packit 90a5c9
                                       h2_bucket_event_cb *cb, void *ctx)
Packit 90a5c9
{
Packit 90a5c9
    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
Packit 90a5c9
Packit 90a5c9
    APR_BUCKET_INIT(b);
Packit 90a5c9
    b->free = apr_bucket_free;
Packit 90a5c9
    b->list = list;
Packit 90a5c9
    b = h2_bucket_observer_make(b, cb, ctx);
Packit 90a5c9
    return b;
Packit 90a5c9
}
Packit 90a5c9
                                       
Packit 90a5c9
apr_status_t h2_bucket_observer_fire(apr_bucket *b, h2_bucket_event event)
Packit 90a5c9
{
Packit 90a5c9
    if (H2_BUCKET_IS_OBSERVER(b)) {
Packit 90a5c9
        h2_bucket_observer *l = (h2_bucket_observer *)b->data; 
Packit 90a5c9
        return l->cb(l->ctx, event, b);
Packit 90a5c9
    }
Packit 90a5c9
    return APR_EINVAL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
const apr_bucket_type_t h2_bucket_type_observer = {
Packit 90a5c9
    "H2OBS", 5, APR_BUCKET_METADATA,
Packit 90a5c9
    bucket_destroy,
Packit 90a5c9
    bucket_read,
Packit 90a5c9
    apr_bucket_setaside_noop,
Packit 90a5c9
    apr_bucket_split_notimpl,
Packit 90a5c9
    apr_bucket_shared_copy
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
apr_bucket *h2_bucket_observer_beam(struct h2_bucket_beam *beam,
Packit 90a5c9
                                    apr_bucket_brigade *dest,
Packit 90a5c9
                                    const apr_bucket *src)
Packit 90a5c9
{
Packit 90a5c9
    if (H2_BUCKET_IS_OBSERVER(src)) {
Packit 90a5c9
        h2_bucket_observer *l = (h2_bucket_observer *)src->data; 
Packit 90a5c9
        apr_bucket *b = h2_bucket_observer_create(dest->bucket_alloc, 
Packit 90a5c9
                                                  l->cb, l->ctx);
Packit 90a5c9
        APR_BRIGADE_INSERT_TAIL(dest, b);
Packit 90a5c9
        l->cb = NULL;
Packit 90a5c9
        l->ctx = NULL;
Packit 90a5c9
        h2_bucket_observer_fire(b, H2_BUCKET_EV_BEFORE_MASTER_SEND);
Packit 90a5c9
        return b;
Packit 90a5c9
    }
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t bbout(apr_bucket_brigade *bb, const char *fmt, ...)
Packit 90a5c9
                             __attribute__((format(printf,2,3)));
Packit 90a5c9
static apr_status_t bbout(apr_bucket_brigade *bb, const char *fmt, ...)
Packit 90a5c9
{
Packit 90a5c9
    va_list args;
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
Packit 90a5c9
    va_start(args, fmt);
Packit 90a5c9
    rv = apr_brigade_vprintf(bb, NULL, NULL, fmt, args);
Packit 90a5c9
    va_end(args);
Packit 90a5c9
Packit 90a5c9
    return rv;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void add_settings(apr_bucket_brigade *bb, h2_session *s, int last) 
Packit 90a5c9
{
Packit 90a5c9
    h2_mplx *m = s->mplx;
Packit 90a5c9
    
Packit 90a5c9
    bbout(bb, "  \"settings\": {\n");
Packit 90a5c9
    bbout(bb, "    \"SETTINGS_MAX_CONCURRENT_STREAMS\": %d,\n", m->max_streams); 
Packit 90a5c9
    bbout(bb, "    \"SETTINGS_MAX_FRAME_SIZE\": %d,\n", 16*1024); 
Packit 90a5c9
    bbout(bb, "    \"SETTINGS_INITIAL_WINDOW_SIZE\": %d,\n",
Packit 90a5c9
          h2_config_geti(s->config, H2_CONF_WIN_SIZE));
Packit 90a5c9
    bbout(bb, "    \"SETTINGS_ENABLE_PUSH\": %d\n", h2_session_push_enabled(s)); 
Packit 90a5c9
    bbout(bb, "  }%s\n", last? "" : ",");
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void add_peer_settings(apr_bucket_brigade *bb, h2_session *s, int last) 
Packit 90a5c9
{
Packit 90a5c9
    bbout(bb, "  \"peerSettings\": {\n");
Packit 90a5c9
    bbout(bb, "    \"SETTINGS_MAX_CONCURRENT_STREAMS\": %d,\n", 
Packit 90a5c9
        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS)); 
Packit 90a5c9
    bbout(bb, "    \"SETTINGS_MAX_FRAME_SIZE\": %d,\n", 
Packit 90a5c9
        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_MAX_FRAME_SIZE)); 
Packit 90a5c9
    bbout(bb, "    \"SETTINGS_INITIAL_WINDOW_SIZE\": %d,\n", 
Packit 90a5c9
        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE)); 
Packit 90a5c9
    bbout(bb, "    \"SETTINGS_ENABLE_PUSH\": %d,\n", 
Packit 90a5c9
        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_ENABLE_PUSH)); 
Packit 90a5c9
    bbout(bb, "    \"SETTINGS_HEADER_TABLE_SIZE\": %d,\n", 
Packit 90a5c9
        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_HEADER_TABLE_SIZE)); 
Packit 90a5c9
    bbout(bb, "    \"SETTINGS_MAX_HEADER_LIST_SIZE\": %d\n", 
Packit 90a5c9
        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE)); 
Packit 90a5c9
    bbout(bb, "  }%s\n", last? "" : ",");
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    apr_bucket_brigade *bb;
Packit 90a5c9
    h2_session *s;
Packit 90a5c9
    int idx;
Packit 90a5c9
} stream_ctx_t;
Packit 90a5c9
Packit 90a5c9
static int add_stream(h2_stream *stream, void *ctx)
Packit 90a5c9
{
Packit 90a5c9
    stream_ctx_t *x = ctx;
Packit 90a5c9
    int32_t flowIn, flowOut;
Packit 90a5c9
    
Packit 90a5c9
    flowIn = nghttp2_session_get_stream_effective_local_window_size(x->s->ngh2, stream->id); 
Packit 90a5c9
    flowOut = nghttp2_session_get_stream_remote_window_size(x->s->ngh2, stream->id);
Packit 90a5c9
    bbout(x->bb, "%s\n    \"%d\": {\n", (x->idx? "," : ""), stream->id);
Packit 90a5c9
    bbout(x->bb, "    \"state\": \"%s\",\n", h2_stream_state_str(stream));
Packit 90a5c9
    bbout(x->bb, "    \"created\": %f,\n", ((double)stream->created)/APR_USEC_PER_SEC);
Packit 90a5c9
    bbout(x->bb, "    \"flowIn\": %d,\n", flowIn);
Packit 90a5c9
    bbout(x->bb, "    \"flowOut\": %d,\n", flowOut);
Packit 90a5c9
    bbout(x->bb, "    \"dataIn\": %"APR_OFF_T_FMT",\n", stream->in_data_octets);  
Packit 90a5c9
    bbout(x->bb, "    \"dataOut\": %"APR_OFF_T_FMT"\n", stream->out_data_octets);  
Packit 90a5c9
    bbout(x->bb, "    }");
Packit 90a5c9
    
Packit 90a5c9
    ++x->idx;
Packit 90a5c9
    return 1;
Packit 90a5c9
} 
Packit 90a5c9
Packit 90a5c9
static void add_streams(apr_bucket_brigade *bb, h2_session *s, int last) 
Packit 90a5c9
{
Packit 90a5c9
    stream_ctx_t x;
Packit 90a5c9
    
Packit 90a5c9
    x.bb = bb;
Packit 90a5c9
    x.s = s;
Packit 90a5c9
    x.idx = 0;
Packit 90a5c9
    bbout(bb, "  \"streams\": {");
Packit 90a5c9
    h2_mplx_stream_do(s->mplx, add_stream, &x);
Packit 90a5c9
    bbout(bb, "\n  }%s\n", last? "" : ",");
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void add_push(apr_bucket_brigade *bb, h2_session *s, 
Packit 90a5c9
                     h2_stream *stream, int last) 
Packit 90a5c9
{
Packit 90a5c9
    h2_push_diary *diary;
Packit 90a5c9
    apr_status_t status;
Packit 90a5c9
    
Packit 90a5c9
    bbout(bb, "    \"push\": {\n");
Packit 90a5c9
    diary = s->push_diary;
Packit 90a5c9
    if (diary) {
Packit 90a5c9
        const char *data;
Packit 90a5c9
        const char *base64_digest;
Packit 90a5c9
        apr_size_t len;
Packit 90a5c9
        
Packit 90a5c9
        status = h2_push_diary_digest_get(diary, bb->p, 256, 
Packit 90a5c9
                                          stream->request->authority, 
Packit 90a5c9
                                          &data, &len;;
Packit 90a5c9
        if (status == APR_SUCCESS) {
Packit 90a5c9
            base64_digest = h2_util_base64url_encode(data, len, bb->p);
Packit 90a5c9
            bbout(bb, "      \"cacheDigest\": \"%s\",\n", base64_digest);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    bbout(bb, "      \"promises\": %d,\n", s->pushes_promised);
Packit 90a5c9
    bbout(bb, "      \"submits\": %d,\n", s->pushes_submitted);
Packit 90a5c9
    bbout(bb, "      \"resets\": %d\n", s->pushes_reset);
Packit 90a5c9
    bbout(bb, "    }%s\n", last? "" : ",");
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void add_in(apr_bucket_brigade *bb, h2_session *s, int last) 
Packit 90a5c9
{
Packit 90a5c9
    bbout(bb, "    \"in\": {\n");
Packit 90a5c9
    bbout(bb, "      \"requests\": %d,\n", s->remote.emitted_count);
Packit 90a5c9
    bbout(bb, "      \"resets\": %d, \n", s->streams_reset);
Packit 90a5c9
    bbout(bb, "      \"frames\": %ld,\n", (long)s->frames_received);
Packit 90a5c9
    bbout(bb, "      \"octets\": %"APR_UINT64_T_FMT"\n", s->io.bytes_read);
Packit 90a5c9
    bbout(bb, "    }%s\n", last? "" : ",");
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void add_out(apr_bucket_brigade *bb, h2_session *s, int last) 
Packit 90a5c9
{
Packit 90a5c9
    bbout(bb, "    \"out\": {\n");
Packit 90a5c9
    bbout(bb, "      \"responses\": %d,\n", s->responses_submitted);
Packit 90a5c9
    bbout(bb, "      \"frames\": %ld,\n", (long)s->frames_sent);
Packit 90a5c9
    bbout(bb, "      \"octets\": %"APR_UINT64_T_FMT"\n", s->io.bytes_written);
Packit 90a5c9
    bbout(bb, "    }%s\n", last? "" : ",");
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void add_stats(apr_bucket_brigade *bb, h2_session *s, 
Packit 90a5c9
                     h2_stream *stream, int last) 
Packit 90a5c9
{
Packit 90a5c9
    bbout(bb, "  \"stats\": {\n");
Packit 90a5c9
    add_in(bb, s, 0);
Packit 90a5c9
    add_out(bb, s, 0);
Packit 90a5c9
    add_push(bb, s, stream, 1);
Packit 90a5c9
    bbout(bb, "  }%s\n", last? "" : ",");
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t h2_status_insert(h2_task *task, apr_bucket *b)
Packit 90a5c9
{
Packit 90a5c9
    conn_rec *c = task->c->master;
Packit 90a5c9
    h2_ctx *h2ctx = h2_ctx_get(c, 0);
Packit 90a5c9
    h2_session *session;
Packit 90a5c9
    h2_stream *stream;
Packit 90a5c9
    apr_bucket_brigade *bb;
Packit 90a5c9
    apr_bucket *e;
Packit 90a5c9
    int32_t connFlowIn, connFlowOut;
Packit 90a5c9
    
Packit 90a5c9
    
Packit 90a5c9
    if (!h2ctx || (session = h2_ctx_session_get(h2ctx)) == NULL) {
Packit 90a5c9
        return APR_SUCCESS;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    stream = h2_session_stream_get(session, task->stream_id);
Packit 90a5c9
    if (!stream) {
Packit 90a5c9
        /* stream already done */
Packit 90a5c9
        return APR_SUCCESS;
Packit 90a5c9
    }
Packit 90a5c9
    
Packit 90a5c9
    bb = apr_brigade_create(stream->pool, c->bucket_alloc);
Packit 90a5c9
    
Packit 90a5c9
    connFlowIn = nghttp2_session_get_effective_local_window_size(session->ngh2); 
Packit 90a5c9
    connFlowOut = nghttp2_session_get_remote_window_size(session->ngh2);
Packit 90a5c9
     
Packit 90a5c9
    bbout(bb, "{\n");
Packit 90a5c9
    bbout(bb, "  \"version\": \"draft-01\",\n");
Packit 90a5c9
    add_settings(bb, session, 0);
Packit 90a5c9
    add_peer_settings(bb, session, 0);
Packit 90a5c9
    bbout(bb, "  \"connFlowIn\": %d,\n", connFlowIn);
Packit 90a5c9
    bbout(bb, "  \"connFlowOut\": %d,\n", connFlowOut);
Packit 90a5c9
    bbout(bb, "  \"sentGoAway\": %d,\n", session->local.shutdown);
Packit 90a5c9
Packit 90a5c9
    add_streams(bb, session, 0);
Packit 90a5c9
    
Packit 90a5c9
    add_stats(bb, session, stream, 1);
Packit 90a5c9
    bbout(bb, "}\n");
Packit 90a5c9
    
Packit 90a5c9
    while ((e = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) {
Packit 90a5c9
        APR_BUCKET_REMOVE(e);
Packit 90a5c9
        APR_BUCKET_INSERT_AFTER(b, e);
Packit 90a5c9
        b = e;
Packit 90a5c9
    }
Packit 90a5c9
    apr_brigade_destroy(bb);
Packit 90a5c9
    
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t status_event(void *ctx, h2_bucket_event event, 
Packit 90a5c9
                                 apr_bucket *b)
Packit 90a5c9
{
Packit 90a5c9
    h2_task *task = ctx;
Packit 90a5c9
    
Packit 90a5c9
    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, task->c->master, 
Packit 90a5c9
                  "status_event(%s): %d", task->id, event);
Packit 90a5c9
    switch (event) {
Packit 90a5c9
        case H2_BUCKET_EV_BEFORE_MASTER_SEND:
Packit 90a5c9
            h2_status_insert(task, b);
Packit 90a5c9
            break;
Packit 90a5c9
        default:
Packit 90a5c9
            break;
Packit 90a5c9
    }
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
int h2_filter_h2_status_handler(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    h2_ctx *ctx = h2_ctx_rget(r);
Packit 90a5c9
    conn_rec *c = r->connection;
Packit 90a5c9
    h2_task *task;
Packit 90a5c9
    apr_bucket_brigade *bb;
Packit 90a5c9
    apr_bucket *b;
Packit 90a5c9
    apr_status_t status;
Packit 90a5c9
    
Packit 90a5c9
    if (strcmp(r->handler, "http2-status")) {
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    if (r->method_number != M_GET && r->method_number != M_POST) {
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    task = ctx? h2_ctx_get_task(ctx) : NULL;
Packit 90a5c9
    if (task) {
Packit 90a5c9
Packit 90a5c9
        if ((status = ap_discard_request_body(r)) != OK) {
Packit 90a5c9
            return status;
Packit 90a5c9
        }
Packit 90a5c9
        
Packit 90a5c9
        /* We need to handle the actual output on the main thread, as
Packit 90a5c9
         * we need to access h2_session information. */
Packit 90a5c9
        r->status = 200;
Packit 90a5c9
        r->clength = -1;
Packit 90a5c9
        r->chunked = 1;
Packit 90a5c9
        apr_table_unset(r->headers_out, "Content-Length");
Packit 90a5c9
        /* Discourage content-encodings */
Packit 90a5c9
        apr_table_unset(r->headers_out, "Content-Encoding");
Packit 90a5c9
        apr_table_setn(r->subprocess_env, "no-brotli", "1");
Packit 90a5c9
        apr_table_setn(r->subprocess_env, "no-gzip", "1");
Packit 90a5c9
Packit 90a5c9
        ap_set_content_type(r, "application/json");
Packit 90a5c9
        apr_table_setn(r->notes, H2_FILTER_DEBUG_NOTE, "on");
Packit 90a5c9
Packit 90a5c9
        bb = apr_brigade_create(r->pool, c->bucket_alloc);
Packit 90a5c9
        b = h2_bucket_observer_create(c->bucket_alloc, status_event, task);
Packit 90a5c9
        APR_BRIGADE_INSERT_TAIL(bb, b);
Packit 90a5c9
        b = apr_bucket_eos_create(c->bucket_alloc);
Packit 90a5c9
        APR_BRIGADE_INSERT_TAIL(bb, b);
Packit 90a5c9
Packit 90a5c9
        ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
Packit 90a5c9
                      "status_handler(%s): checking for incoming trailers", 
Packit 90a5c9
                      task->id);
Packit 90a5c9
        if (r->trailers_in && !apr_is_empty_table(r->trailers_in)) {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
Packit 90a5c9
                          "status_handler(%s): seeing incoming trailers", 
Packit 90a5c9
                          task->id);
Packit 90a5c9
            apr_table_setn(r->trailers_out, "h2-trailers-in", 
Packit 90a5c9
                           apr_itoa(r->pool, 1));
Packit 90a5c9
        }
Packit 90a5c9
        
Packit 90a5c9
        status = ap_pass_brigade(r->output_filters, bb);
Packit 90a5c9
        if (status == APR_SUCCESS
Packit 90a5c9
            || r->status != HTTP_OK
Packit 90a5c9
            || c->aborted) {
Packit 90a5c9
            return OK;
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            /* no way to know what type of error occurred */
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_TRACE1, status, r,
Packit 90a5c9
                          "status_handler(%s): ap_pass_brigade failed", 
Packit 90a5c9
                          task->id);
Packit 90a5c9
            return AP_FILTER_ERROR;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return DECLINED;
Packit 90a5c9
}
Packit 90a5c9