Skip to content

mod_http3/src/h3_hooks.c

Functions

Name
const char * h3_hook_http_scheme(const request_rec * r)
apr_port_t h3_hook_default_port(const request_rec * r)
int h3_hook_ssl_conn_is_ssl(conn_rec * c)
int h3_hook_fixups(request_rec * r)
int h3_hook_post_read_request(request_rec * r)
void h3_hook_pre_read_request(request_rec * r, conn_rec * c)
int h3_hook_access_checker(request_rec * r)
int h3_hook_http_create_request(request_rec * r)
int h3_status_handler(request_rec * r)

Functions Documentation

function h3_hook_http_scheme

const char * h3_hook_http_scheme(
    const request_rec * r
)

Parameters:

  • r The request.

Return: "https" for an H3 request, NULL to decline otherwise.

ap_hook_http_scheme. H3 connections are always TLS, but mod_ssl does not manage them and declines; without this hook self-referential URLs (redirects, REQUEST_SCHEME) would claim "http".

function h3_hook_default_port

apr_port_t h3_hook_default_port(
    const request_rec * r
)

Parameters:

  • r The request.

Return: 443 for an H3 request, 0 to decline otherwise.

ap_hook_default_port. Companion to the scheme hook: keeps the default port for H3 request URLs at 443 instead of 80.

function h3_hook_ssl_conn_is_ssl

int h3_hook_ssl_conn_is_ssl(
    conn_rec * c
)

Parameters:

  • c The connection.

Return: OK for an H3 connection, DECLINED otherwise.

ap_hook_ssl_conn_is_ssl. Marks H3 connections as TLS-protected for ap_ssl_conn_is_ssl() consumers (mod_rewrite %{HTTPS}, expression parser).

function h3_hook_fixups

int h3_hook_fixups(
    request_rec * r
)

Parameters:

  • r The request being fixed up.

Return: OK for an H3 request, DECLINED for a non-H3 request.

ap_hook_fixups. For H3 requests, runs the fixups chain and returns OK. For non-H3 requests, returns DECLINED so the standard fixups chain runs.

function h3_hook_post_read_request

int h3_hook_post_read_request(
    request_rec * r
)

Parameters:

  • r The request (unused).

Return: OK.

ap_hook_post_read_request. No-op for H3; reserved for future per-request initialisation. Always returns OK.

function h3_hook_pre_read_request

void h3_hook_pre_read_request(
    request_rec * r,
    conn_rec * c
)

Parameters:

  • r The request (unused).
  • c The connection (unused).

ap_hook_pre_read_request. No-op for H3 — the request is already fully parsed by nghttp3 before the dispatcher runs.

function h3_hook_access_checker

int h3_hook_access_checker(
    request_rec * r
)

Parameters:

  • r The request being checked.

Return: OK/413/400 for H3, DECLINED otherwise.

ap_hook_access_checker. DECLINED for HTTP/1.x, otherwise OK/413/400.

function h3_hook_http_create_request

int h3_hook_http_create_request(
    request_rec * r
)

Parameters:

  • r The newly created request.

Return: OK on install, DECLINED to skip.

ap_hook_http_create_request. Installs the H3 input and output filter chain (protocol + network) on a freshly created H3 request. Skips subrequests (r->main != NULL) and non-H3 requests.

function h3_status_handler

int h3_status_handler(
    request_rec * r
)

Parameters:

  • r The request being handled.

Return: OK when it serves "http3-status", DECLINED otherwise.

ap_hook_handler for the "http3-status" handler. Emits a JSON snapshot of this child's HTTP/3 metrics (live workers, connection/stream counts, and byte totals). Declines any other handler or a non-GET method.

Source code

/*
 * Copyright (c) 2026 The mod_http3 Project Authors. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed 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 "h3_config.h"
#include <httpd.h>

#include <http_config.h>
#include <http_connection.h>
#include <http_core.h>
#include <http_log.h>
#include <http_protocol.h>
#include <http_request.h>
#include <http_ssl.h>
#include <http_vhost.h>

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

#include "h3.h"
#include "h3_check.h"
#include "h3_filter.h"
#include "h3_hooks.h"
#include "h3_io.h"
#include "h3_session.h"
#include "mod_http3.h"
#include "quic.h"

const char* h3_hook_http_scheme(const request_rec* r)
{
    return IS_H3_REQUEST(r) ? "https" : NULL;
}

apr_port_t h3_hook_default_port(const request_rec* r)
{
    return IS_H3_REQUEST(r) ? APR_URI_HTTPS_DEFAULT_PORT : 0;
}

int h3_hook_ssl_conn_is_ssl(conn_rec* c)
{
    return IS_H3_CONN(c) ? OK : DECLINED;
}

int h3_hook_fixups(request_rec* r)
{
    if (!ap_is_initial_req(r))
    {
        return DECLINED;
    }

    if (IS_H3_REQUEST(r))
    {
        /* mod_ssl does not manage this connection, so it sets no TLS environment. */
        apr_table_setn(r->subprocess_env, "HTTPS", "on");
    }

    h3_server_conf* conf = ap_get_module_config(r->server->module_config, &http3_module);

    if (!conf || !conf->h3_cert_path || !conf->h3_key_path || conf->h3_port == 0)
    {
        return DECLINED;
    }

    if (conf->h3_alt_svc == H3_FLAG_OFF)
    {
        return DECLINED;
    }

    ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, "h3_hook_fixups called");

    if (apr_table_get(r->headers_out, "Alt-Svc"))
    {
        return DECLINED;
    }

    apr_table_setn(r->headers_out, "Alt-Svc", apr_psprintf(r->pool, "h3=\":%d\"; ma=%u; persist=1", (int)conf->h3_port, (unsigned)conf->h3_alt_svc_max_age));
    return OK;
}

int h3_hook_post_read_request(request_rec* r)
{
    CHECK(r);
    if (!IS_H3_REQUEST(r))
    {
        return DECLINED;
    }
    r->protocol = "HTTP/3.0";
    r->proto_num = HTTP_VERSION(3, 0);
    return OK;
}

void h3_hook_pre_read_request(request_rec* /*r*/, conn_rec* /*c*/)
{
}

int h3_hook_access_checker(request_rec* r)
{
    if (!IS_H3_REQUEST(r))
    {
        return DECLINED;
    }
    ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, "h3_hook_access_checker called");
    /* Reject unprocessable bodies early. */
    h3_conn_ctx_t* ctx = ap_get_module_config(r->request_config, &http3_module);
    h3_stream* stream = ctx ? ctx->stream : NULL;
    if (stream && stream->request_body_overflow)
    {
        return HTTP_REQUEST_ENTITY_TOO_LARGE;
    }
    if (stream && stream->body_truncated)
    {
        return HTTP_BAD_REQUEST;
    }
    return OK;
}

int h3_hook_http_create_request(request_rec* r)
{
    CHECK(r);
    if (!IS_H3_REQUEST(r) || r->main != NULL)
    {
        return DECLINED;
    }
    ap_add_input_filter_handle(h3_proto_in_filter_handle, NULL, r, r->connection);
    ap_add_input_filter_handle(h3_net_in_filter_handle, NULL, NULL, r->connection);
    ap_add_output_filter_handle(h3_net_out_filter_handle, NULL, NULL, r->connection);
    r->output_filters = r->connection->output_filters;
    r->proto_output_filters = r->connection->output_filters;
    return OK;
}

int h3_status_handler(request_rec* r)
{
    if (strcmp(r->handler, "http3-status"))
    {
        return DECLINED;
    }

    if (r->method_number != M_GET)
    {
        return DECLINED;
    }

    ap_set_content_type(r, "application/json");

    if (!child_h3_io)
    {
        ap_rputs("{\"error\": \"HTTP/3 not enabled or initialized on this child process\"}\n", r);
        return OK;
    }

    apr_uint32_t live = apr_atomic_read32(&child_h3_io->active_session_count);
    apr_uint32_t conns = apr_atomic_read32(&child_h3_io->total_connections);
    apr_uint32_t streams = apr_atomic_read32(&child_h3_io->total_streams);
    apr_uint64_t bytes_in = apr_atomic_read64(&child_h3_io->total_bytes_read);
    apr_uint64_t bytes_out = apr_atomic_read64(&child_h3_io->total_bytes_written);

    ap_rprintf(r,
               "{\n"
               "  \"quic_backend\": \"%s\",\n"
               "  \"live_workers\": %u,\n"
               "  \"total_connections\": %u,\n"
               "  \"total_streams\": %u,\n"
               "  \"total_bytes_read\": %" APR_UINT64_T_FMT ",\n"
               "  \"total_bytes_written\": %" APR_UINT64_T_FMT "\n"
               "}\n",
               quic_engine_name(), live, conns, streams, bytes_in, bytes_out);

    return OK;
}