mod_http3/src/h3_config.c¶
Functions¶
| Name | |
|---|---|
| apr_port_t | get_server_port(const server_rec * s) |
| void * | h3_create_server_config(apr_pool_t * p, server_rec * s) |
| void * | h3_merge_server_config(apr_pool_t * p, void * base_conf, void * new_conf) |
| int | mpm_query(int code) Value of an MPM query, or -1 when the active MPM does not answer it. |
| const char * | set_string(cmd_parms * cmd, const char * arg, const char * field) |
| const char * | set_h3_cert_path(cmd_parms * cmd, void * , const char * arg) |
| const char * | set_h3_key_path(cmd_parms * cmd, void * , const char * arg) |
| const char * | engine_list(apr_pool_t * pool) |
| int | engine_known(const char * name) |
| const char * | set_h3_quic_engine(cmd_parms * cmd, void * , const char * arg) |
| const char * | set_h3_port(cmd_parms * cmd, void * , const char * arg) |
| const char * | set_h3_max_concurrent_streams(cmd_parms * cmd, void * , const char * arg) |
| const char * | set_h3_max_connections(cmd_parms * cmd, void * , const char * arg) |
| const char * | set_h3_stream_buffer_size(cmd_parms * cmd, void * , const char * arg) |
| const char * | set_h3_max_request_body_size(cmd_parms * cmd, void * , const char * arg) |
| const char * | set_h3_max_response_body_size(cmd_parms * cmd, void * , const char * arg) |
| const char * | set_h3_handshake_timeout(cmd_parms * cmd, void * , const char * arg) |
| const char * | set_h3_idle_timeout(cmd_parms * cmd, void * , const char * arg) |
| const char * | set_h3_alt_svc(cmd_parms * cmd, void * , int flag) |
| const char * | set_h3_address_validation(cmd_parms * cmd, void * , int flag) |
| const char * | set_h3_alt_svc_max_age(cmd_parms * cmd, void * , const char * arg) |
| int | h3_post_config(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, server_rec * s) |
| void * | h3_create_dir_config(apr_pool_t * p, char * dir) |
| void * | h3_merge_dir_config(apr_pool_t * p, void * base, void * add) |
Attributes¶
| Name | |
|---|---|
| const command_rec | cmd_1 |
| const command_rec | cmd_2 |
| const command_rec | cmd_3 |
| const command_rec | cmd_4 |
| const command_rec | cmd_5 |
| const command_rec | cmd_6 |
| const command_rec | cmd_7 |
| const command_rec | cmd_8 |
| const command_rec | cmd_9 |
| const command_rec | cmd_10 |
| const command_rec | cmd_11 |
| const command_rec | cmd_12 |
| const command_rec | cmd_13 |
| const command_rec | cmd_14 |
| const command_rec | cmd_end |
| const command_rec[] | h3_cmds |
Functions Documentation¶
function get_server_port¶
Parameters:
- s The server_rec.
Return: The TCP port, or 0 if undetermined.
Look up the TCP Listen port of a vhost by scanning its Port and Listen directive state.
function h3_create_server_config¶
Parameters:
- p Pool used for the allocation.
- s The vhost the config belongs to (unused).
Return: The new h3_server_conf.
ap_create_server_config callback: allocate a fresh zero-initialised h3_server_conf from p. All fields start as NULL/0.
function h3_merge_server_config¶
Parameters:
- p Pool for the merged config.
- base_conf Parent h3_server_conf.
- new_conf Child h3_server_conf.
Return: The merged h3_server_conf.
ap_merge_server_config callback: produce a child vhost config that inherits each unset field from the parent. cert/key/h3_port use the new value if non-NULL/non-zero, else the base.
function mpm_query¶
Value of an MPM query, or -1 when the active MPM does not answer it.
function set_string¶
function set_h3_cert_path¶
function set_h3_key_path¶
function engine_list¶
function engine_known¶
function set_h3_quic_engine¶
function set_h3_port¶
function set_h3_max_concurrent_streams¶
function set_h3_max_connections¶
function set_h3_stream_buffer_size¶
function set_h3_max_request_body_size¶
function set_h3_max_response_body_size¶
function set_h3_handshake_timeout¶
function set_h3_idle_timeout¶
function set_h3_alt_svc¶
function set_h3_address_validation¶
function set_h3_alt_svc_max_age¶
function h3_post_config¶
Parameters:
- p Config pool (unused).
- plog Log pool (unused).
- ptemp Temp pool (unused).
- s The first server_rec in the configuration.
Return: OK, or HTTP_INTERNAL_SERVER_ERROR if no vhost has both H3CertificatePath and H3CertificateKeyPath set.
ap_post_config hook: resolve cert/key/h3_port for the listening vhost and log the resolved values. No-op in AP_SQ_MS_CREATE_PRE_CONFIG (pre-config phase). Returns OK if a fully-configured vhost is found, HTTP_INTERNAL_SERVER_ERROR otherwise.
function h3_create_dir_config¶
Parameters:
- p Pool for the allocation.
- dir The directory path (unused).
Return: A 1-byte zero-initialised block.
ap_create_dir_config callback. mod_http3 has no per-directory state; returns a 1-byte allocation so httpd takes ownership of a non-NULL pointer.
function h3_merge_dir_config¶
Parameters:
- p Pool for any allocation (unused).
- base Base per-dir config.
- add Per-dir config being merged in (unused).
Return: The base per-dir config.
ap_merge_dir_config callback. Identity merge: per-directory config is a no-op for mod_http3, so the base is returned unchanged.
Attributes Documentation¶
variable cmd_1¶
const command_rec cmd_1 = AP_INIT_TAKE1("H3CertificatePath", set_h3_cert_path, NULL, RSRC_CONF, "Path to the SSL certificate file for HTTP/3");
variable cmd_2¶
const command_rec cmd_2 = AP_INIT_TAKE1("H3CertificateKeyPath", set_h3_key_path, NULL, RSRC_CONF, "Path to the SSL certificate key file for HTTP/3");
variable cmd_3¶
const command_rec cmd_3 = AP_INIT_TAKE1("H3Port", set_h3_port, NULL, RSRC_CONF, "UDP port to listen on for QUIC/HTTP-3 (default: same as main server)");
variable cmd_4¶
const command_rec cmd_4 = AP_INIT_TAKE1("H3MaxConcurrentStreams", set_h3_max_concurrent_streams, NULL, RSRC_CONF, "Maximum number of concurrent HTTP/3 streams per connection (default: 100)");
variable cmd_5¶
const command_rec cmd_5 = AP_INIT_TAKE1("H3MaxConnections", set_h3_max_connections, NULL, RSRC_CONF, "Maximum concurrent QUIC/HTTP/3 connections per child process (default: 256)");
variable cmd_6¶
const command_rec cmd_6 = AP_INIT_TAKE1("H3StreamBufferSize", set_h3_stream_buffer_size, NULL, RSRC_CONF, "Per-stream request and streaming-response buffer size in bytes (default: 65536)");
variable cmd_7¶
const command_rec cmd_7 = AP_INIT_TAKE1("H3MaxRequestBodySize", set_h3_max_request_body_size, NULL, RSRC_CONF, "Maximum HTTP/3 request body size in bytes, fully buffered in memory (default: 10485760)");
variable cmd_8¶
const command_rec cmd_8 = AP_INIT_FLAG("H3AltSvc", set_h3_alt_svc, NULL, RSRC_CONF, "Whether to advertise HTTP/3 support via an Alt-Svc response header, required for browser discovery (default: on)");
variable cmd_9¶
const command_rec cmd_9 = AP_INIT_TAKE1("H3AltSvcMaxAge", set_h3_alt_svc_max_age, NULL, RSRC_CONF, "Seconds a client may cache the Alt-Svc HTTP/3 advertisement for (default: 86400)");
variable cmd_10¶
const command_rec cmd_10 = AP_INIT_TAKE1("H3HandshakeTimeout", set_h3_handshake_timeout, NULL, RSRC_CONF, "Timeout in seconds for QUIC handshakes to complete (default: 10)");
variable cmd_11¶
const command_rec cmd_11 = AP_INIT_TAKE1("H3IdleTimeout", set_h3_idle_timeout, NULL, RSRC_CONF, "Idle timeout in seconds for QUIC connections (default: 300)");
variable cmd_12¶
const command_rec cmd_12 = AP_INIT_TAKE1("H3MaxResponseBodySize", set_h3_max_response_body_size, NULL, RSRC_CONF, "Maximum HTTP/3 response body size in bytes; an explicit limit enables bounded whole-response buffering (default: unlimited streaming)");
variable cmd_13¶
const command_rec cmd_13 = AP_INIT_FLAG("H3AddressValidation", set_h3_address_validation, NULL, RSRC_CONF, "Whether to validate client addresses with a QUIC Retry packet before accepting a connection (default: on)");
variable cmd_14¶
const command_rec cmd_14 = AP_INIT_TAKE1("H3QuicEngine", set_h3_quic_engine, NULL, RSRC_CONF, "QUIC engine to run, among those compiled in (default: openssl)");
variable cmd_end¶
variable h3_cmds¶
const command_rec[] h3_cmds = {cmd_1, cmd_2, cmd_3, cmd_4, cmd_5, cmd_6, cmd_7, cmd_8, cmd_9, cmd_10, cmd_11, cmd_12, cmd_13, cmd_14, cmd_end};
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 <httpd.h>
#include <ap_mpm.h>
#include <http_config.h>
#include <http_core.h>
#include <http_log.h>
#include <http_main.h>
#include <apr_cstr.h>
#include <apr_pools.h>
#include <apr_strings.h>
#include <stdint.h>
#include <unistd.h>
#include "h3.h"
#include "h3_check.h"
#include "h3_config.h"
#include "mod_http3.h"
#include "quic.h"
apr_port_t get_server_port(const server_rec* s)
{
for (const server_addr_rec* sar = s->addrs; sar != NULL; sar = sar->next)
{
if (sar->host_port != 0)
{
return sar->host_port;
}
}
return 0;
}
void* h3_create_server_config(apr_pool_t* p, server_rec* /*s*/)
{
return apr_pcalloc(p, sizeof(h3_server_conf));
}
void* h3_merge_server_config(apr_pool_t* p, void* base_conf, void* new_conf)
{
h3_server_conf* merged = apr_pcalloc(p, sizeof(h3_server_conf));
h3_server_conf* base = (h3_server_conf*)base_conf;
h3_server_conf* new = (h3_server_conf*)new_conf;
merged->h3_cert_path = new->h3_cert_path ? new->h3_cert_path : base->h3_cert_path;
merged->h3_key_path = new->h3_key_path ? new->h3_key_path : base->h3_key_path;
merged->h3_quic_engine = new->h3_quic_engine ? new->h3_quic_engine : base->h3_quic_engine;
merged->h3_port = new->h3_port ? new->h3_port : base->h3_port;
merged->h3_max_concurrent_streams = new->h3_max_concurrent_streams ? new->h3_max_concurrent_streams : base->h3_max_concurrent_streams;
merged->h3_max_connections = new->h3_max_connections ? new->h3_max_connections : base->h3_max_connections;
merged->h3_stream_buffer_size = new->h3_stream_buffer_size ? new->h3_stream_buffer_size : base->h3_stream_buffer_size;
merged->h3_max_request_body_size = new->h3_max_request_body_size ? new->h3_max_request_body_size : base->h3_max_request_body_size;
merged->h3_max_response_body_size = new->h3_max_response_body_size ? new->h3_max_response_body_size : base->h3_max_response_body_size;
merged->h3_alt_svc = new->h3_alt_svc != H3_FLAG_UNSET ? new->h3_alt_svc : base->h3_alt_svc;
merged->h3_address_validation = new->h3_address_validation != H3_FLAG_UNSET ? new->h3_address_validation : base->h3_address_validation;
merged->h3_alt_svc_max_age = new->h3_alt_svc_max_age ? new->h3_alt_svc_max_age : base->h3_alt_svc_max_age;
merged->h3_handshake_timeout = new->h3_handshake_timeout ? new->h3_handshake_timeout : base->h3_handshake_timeout;
merged->h3_idle_timeout = new->h3_idle_timeout ? new->h3_idle_timeout : base->h3_idle_timeout;
return merged;
}
static int mpm_query(int code)
{
int value = 0;
return ap_mpm_query(code, &value) == APR_SUCCESS ? value : -1;
}
static const char* set_string(cmd_parms* cmd, const char* arg, const char* field)
{
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
if (!conf)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server, "mod_http3: server config missing in directive");
return "mod_http3: internal error: no server config";
}
*(const char**)((char*)conf + (apr_size_t)field) = apr_pstrdup(cmd->pool, arg);
return NULL;
}
static const char* set_h3_cert_path(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
return set_string(cmd, arg, (const char*)offsetof(h3_server_conf, h3_cert_path));
}
static const char* set_h3_key_path(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
return set_string(cmd, arg, (const char*)offsetof(h3_server_conf, h3_key_path));
}
static const char* engine_list(apr_pool_t* pool)
{
const char* list = quic_engine_name_at(0);
for (size_t i = 1; i < quic_engine_count(); i++)
{
list = apr_pstrcat(pool, list, ", ", quic_engine_name_at(i), NULL);
}
return list;
}
/* Checked here rather than only at post_config, which httpd -t never reaches. */
static int engine_known(const char* name)
{
for (size_t i = 0; i < quic_engine_count(); i++)
{
if (apr_cstr_casecmp(name, quic_engine_name_at(i)) == 0)
{
return 1;
}
}
return 0;
}
static const char* set_h3_quic_engine(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
if (arg && *arg && !engine_known(arg))
{
return apr_psprintf(cmd->pool, "H3QuicEngine %s: this build has no such engine (compiled: %s)", arg, engine_list(cmd->pool));
}
return set_string(cmd, arg, (const char*)offsetof(h3_server_conf, h3_quic_engine));
}
static const char* set_h3_port(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
if (!arg || !*arg)
{
return "H3Port: empty port number";
}
apr_int64_t port = 0;
apr_status_t rv = apr_cstr_atoi64(&port, arg);
if (rv == APR_EINVAL)
{
return apr_psprintf(cmd->pool, "H3Port: '%s' is not a number", arg);
}
if (rv == APR_ERANGE)
{
return apr_psprintf(cmd->pool, "H3Port: '%s' is out of representable range", arg);
}
if (port < 1 || port > 65535)
{
return apr_psprintf(cmd->pool, "H3Port: '%s' is out of allowed range (1-65535)", arg);
}
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_port = (apr_port_t)port;
return NULL;
}
static const char* set_h3_max_concurrent_streams(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
if (!arg || !*arg)
{
return "H3MaxConcurrentStreams: empty value";
}
apr_uint64_t val = 0;
apr_status_t rv = apr_cstr_atoui64(&val, arg);
if (rv == APR_EINVAL)
{
return apr_psprintf(cmd->pool, "H3MaxConcurrentStreams: '%s' is not a number", arg);
}
if (rv == APR_ERANGE)
{
return apr_psprintf(cmd->pool, "H3MaxConcurrentStreams: '%s' is out of representable range", arg);
}
if (val == 0 || val > UINT16_MAX)
{
return apr_psprintf(cmd->pool, "H3MaxConcurrentStreams: '%s' is out of allowed range (1-%u)", arg, (unsigned)UINT16_MAX);
}
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_max_concurrent_streams = (apr_uint32_t)val;
return NULL;
}
static const char* set_h3_max_connections(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
if (!arg || !*arg)
{
return "H3MaxConnections: empty value";
}
apr_uint64_t val = 0;
apr_status_t rv = apr_cstr_atoui64(&val, arg);
if (rv == APR_EINVAL)
{
return apr_psprintf(cmd->pool, "H3MaxConnections: '%s' is not a number", arg);
}
if (rv == APR_ERANGE)
{
return apr_psprintf(cmd->pool, "H3MaxConnections: '%s' is out of representable range", arg);
}
if (val == 0 || val > H3_MAX_CONNECTIONS_MAX)
{
return apr_psprintf(cmd->pool, "H3MaxConnections: '%s' is out of allowed range (1-%u)", arg, (unsigned)H3_MAX_CONNECTIONS_MAX);
}
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_max_connections = (apr_uint32_t)val;
return NULL;
}
static const char* set_h3_stream_buffer_size(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
if (!arg || !*arg)
{
return "H3StreamBufferSize: empty value";
}
apr_uint64_t val = 0;
apr_status_t rv = apr_cstr_atoui64(&val, arg);
if (rv == APR_EINVAL)
{
return apr_psprintf(cmd->pool, "H3StreamBufferSize: '%s' is not a number", arg);
}
if (rv == APR_ERANGE)
{
return apr_psprintf(cmd->pool, "H3StreamBufferSize: '%s' is out of representable range", arg);
}
if (val == 0 || val > H3_STREAM_BUFFER_SIZE_MAX)
{
return apr_psprintf(cmd->pool, "H3StreamBufferSize: '%s' is out of allowed range (1-%lu)", arg, (unsigned long)H3_STREAM_BUFFER_SIZE_MAX);
}
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_stream_buffer_size = (apr_size_t)val;
return NULL;
}
static const char* set_h3_max_request_body_size(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
if (!arg || !*arg)
{
return "H3MaxRequestBodySize: empty value";
}
apr_uint64_t val = 0;
apr_status_t rv = apr_cstr_atoui64(&val, arg);
if (rv == APR_EINVAL)
{
return apr_psprintf(cmd->pool, "H3MaxRequestBodySize: '%s' is not a number", arg);
}
if (rv == APR_ERANGE)
{
return apr_psprintf(cmd->pool, "H3MaxRequestBodySize: '%s' is out of representable range", arg);
}
if (val == 0 || val > H3_MAX_REQUEST_BODY_SIZE_MAX)
{
return apr_psprintf(cmd->pool, "H3MaxRequestBodySize: '%s' is out of allowed range (1-%lu)", arg, (unsigned long)H3_MAX_REQUEST_BODY_SIZE_MAX);
}
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_max_request_body_size = (apr_size_t)val;
return NULL;
}
static const char* set_h3_max_response_body_size(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
if (!arg || !*arg)
{
return "H3MaxResponseBodySize: empty value";
}
apr_uint64_t val = 0;
apr_status_t rv = apr_cstr_atoui64(&val, arg);
if (rv == APR_EINVAL)
{
return apr_psprintf(cmd->pool, "H3MaxResponseBodySize: '%s' is not a number", arg);
}
if (rv == APR_ERANGE)
{
return apr_psprintf(cmd->pool, "H3MaxResponseBodySize: '%s' is out of representable range", arg);
}
if (val == 0 || val > H3_MAX_RESPONSE_BODY_SIZE_MAX)
{
return apr_psprintf(cmd->pool, "H3MaxResponseBodySize: '%s' is out of allowed range (1-%lu)", arg, (unsigned long)H3_MAX_RESPONSE_BODY_SIZE_MAX);
}
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_max_response_body_size = (apr_size_t)val;
return NULL;
}
static const char* set_h3_handshake_timeout(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
if (!arg || !*arg)
{
return "H3HandshakeTimeout: empty value";
}
apr_uint64_t val = 0;
apr_status_t rv = apr_cstr_atoui64(&val, arg);
if (rv == APR_EINVAL)
{
return apr_psprintf(cmd->pool, "H3HandshakeTimeout: '%s' is not a number", arg);
}
if (rv == APR_ERANGE)
{
return apr_psprintf(cmd->pool, "H3HandshakeTimeout: '%s' is out of representable range", arg);
}
if (val == 0 || val > H3_HANDSHAKE_TIMEOUT_MAX)
{
return apr_psprintf(cmd->pool, "H3HandshakeTimeout: '%s' is out of allowed range (1-%u)", arg, (unsigned)H3_HANDSHAKE_TIMEOUT_MAX);
}
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_handshake_timeout = (apr_uint32_t)val;
return NULL;
}
static const char* set_h3_idle_timeout(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
if (!arg || !*arg)
{
return "H3IdleTimeout: empty value";
}
apr_uint64_t val = 0;
apr_status_t rv = apr_cstr_atoui64(&val, arg);
if (rv == APR_EINVAL)
{
return apr_psprintf(cmd->pool, "H3IdleTimeout: '%s' is not a number", arg);
}
if (rv == APR_ERANGE)
{
return apr_psprintf(cmd->pool, "H3IdleTimeout: '%s' is out of representable range", arg);
}
if (val == 0 || val > H3_IDLE_TIMEOUT_MAX)
{
return apr_psprintf(cmd->pool, "H3IdleTimeout: '%s' is out of allowed range (1-%u)", arg, (unsigned)H3_IDLE_TIMEOUT_MAX);
}
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_idle_timeout = (apr_uint32_t)val;
return NULL;
}
static const char* set_h3_alt_svc(cmd_parms* cmd, void* /*dummy*/, int flag)
{
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_alt_svc = flag ? H3_FLAG_ON : H3_FLAG_OFF;
return NULL;
}
static const char* set_h3_address_validation(cmd_parms* cmd, void* /*dummy*/, int flag)
{
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_address_validation = flag ? H3_FLAG_ON : H3_FLAG_OFF;
return NULL;
}
static const char* set_h3_alt_svc_max_age(cmd_parms* cmd, void* /*dummy*/, const char* arg)
{
if (!arg || !*arg)
{
return "H3AltSvcMaxAge: empty value";
}
apr_uint64_t val = 0;
apr_status_t rv = apr_cstr_atoui64(&val, arg);
if (rv == APR_EINVAL)
{
return apr_psprintf(cmd->pool, "H3AltSvcMaxAge: '%s' is not a number", arg);
}
if (rv == APR_ERANGE)
{
return apr_psprintf(cmd->pool, "H3AltSvcMaxAge: '%s' is out of representable range", arg);
}
if (val == 0 || val > H3_ALT_SVC_MAX_AGE_MAX)
{
return apr_psprintf(cmd->pool, "H3AltSvcMaxAge: '%s' is out of allowed range (1-%lu)", arg, (unsigned long)H3_ALT_SVC_MAX_AGE_MAX);
}
h3_server_conf* conf = ap_get_module_config(cmd->server->module_config, &http3_module);
CHECK(conf);
conf->h3_alt_svc_max_age = (apr_uint32_t)val;
return NULL;
}
int h3_post_config(apr_pool_t* /*p*/, apr_pool_t* /*plog*/, apr_pool_t* ptemp, server_rec* s)
{
CHECK(ptemp);
CHECK(s);
h3_server_conf* conf = NULL;
if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG)
{
return OK;
}
for (server_rec* vs = s; vs; vs = vs->next)
{
h3_server_conf* vc = ap_get_module_config(vs->module_config, &http3_module);
if (vc->h3_cert_path && vc->h3_key_path)
{
vc->host_port = get_server_port(vs);
if (vc->h3_port == 0)
{
vc->h3_port = vc->host_port;
}
if (vc->h3_max_concurrent_streams == 0)
{
vc->h3_max_concurrent_streams = H3_MAX_CONCURRENT_STREAMS_DEFAULT;
}
if (vc->h3_max_connections == 0)
{
vc->h3_max_connections = H3_MAX_CONNECTIONS_DEFAULT;
}
if (vc->h3_stream_buffer_size == 0)
{
vc->h3_stream_buffer_size = H3_STREAM_BUFFER_SIZE_DEFAULT;
}
if (vc->h3_max_request_body_size == 0)
{
vc->h3_max_request_body_size = H3_MAX_REQUEST_BODY_SIZE_DEFAULT;
}
if (vc->h3_max_response_body_size == 0)
{
vc->h3_max_response_body_size = H3_MAX_RESPONSE_BODY_SIZE_DEFAULT;
}
if (vc->h3_alt_svc == H3_FLAG_UNSET)
{
vc->h3_alt_svc = H3_FLAG_ON;
}
if (vc->h3_address_validation == H3_FLAG_UNSET)
{
vc->h3_address_validation = H3_FLAG_ON;
}
if (vc->h3_alt_svc_max_age == 0)
{
vc->h3_alt_svc_max_age = H3_ALT_SVC_MAX_AGE_DEFAULT;
}
if (vc->h3_handshake_timeout == 0)
{
vc->h3_handshake_timeout = H3_HANDSHAKE_TIMEOUT_DEFAULT;
}
if (vc->h3_idle_timeout == 0)
{
vc->h3_idle_timeout = H3_IDLE_TIMEOUT_DEFAULT;
}
conf = vc;
break;
}
}
CHECK(conf && conf->h3_cert_path && conf->h3_key_path, return HTTP_INTERNAL_SERVER_ERROR;);
if (conf->h3_quic_engine && !quic_select(conf->h3_quic_engine))
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_http3: H3QuicEngine %s: this build has no such engine (compiled: %s)", conf->h3_quic_engine, engine_list(ptemp));
return HTTP_INTERNAL_SERVER_ERROR;
}
/* Validate cert and key files are readable */
apr_file_t* f = NULL;
if (apr_file_open(&f, conf->h3_cert_path, APR_READ, APR_OS_DEFAULT, ptemp) != APR_SUCCESS)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_http3: H3CertificatePath not readable: %s", conf->h3_cert_path);
return HTTP_INTERNAL_SERVER_ERROR;
}
apr_file_close(f);
f = NULL;
if (apr_file_open(&f, conf->h3_key_path, APR_READ, APR_OS_DEFAULT, ptemp) != APR_SUCCESS)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_http3: H3CertificateKeyPath not readable: %s", conf->h3_key_path);
return HTTP_INTERNAL_SERVER_ERROR;
}
apr_file_close(f);
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "h3_post_config: pid=%d cert=%s key=%s h3_port=%d mpm=%s threaded=%d forked=%d max_threads=%d", getpid(), conf->h3_cert_path, conf->h3_key_path, (int)conf->h3_port, ap_show_mpm(), mpm_query(AP_MPMQ_IS_THREADED), mpm_query(AP_MPMQ_IS_FORKED),
mpm_query(AP_MPMQ_MAX_THREADS));
return OK;
}
void* h3_create_dir_config(apr_pool_t* p, char* /*dir*/)
{
CHECK(p);
return apr_pcalloc(p, 1);
}
void* h3_merge_dir_config(apr_pool_t* /*p*/, void* base, void* /*add*/)
{
CHECK(base);
return base;
}
const command_rec cmd_1 = AP_INIT_TAKE1("H3CertificatePath", set_h3_cert_path, NULL, RSRC_CONF, "Path to the SSL certificate file for HTTP/3");
const command_rec cmd_2 = AP_INIT_TAKE1("H3CertificateKeyPath", set_h3_key_path, NULL, RSRC_CONF, "Path to the SSL certificate key file for HTTP/3");
const command_rec cmd_3 = AP_INIT_TAKE1("H3Port", set_h3_port, NULL, RSRC_CONF, "UDP port to listen on for QUIC/HTTP-3 (default: same as main server)");
const command_rec cmd_4 = AP_INIT_TAKE1("H3MaxConcurrentStreams", set_h3_max_concurrent_streams, NULL, RSRC_CONF, "Maximum number of concurrent HTTP/3 streams per connection (default: 100)");
const command_rec cmd_5 = AP_INIT_TAKE1("H3MaxConnections", set_h3_max_connections, NULL, RSRC_CONF, "Maximum concurrent QUIC/HTTP/3 connections per child process (default: 256)");
const command_rec cmd_6 = AP_INIT_TAKE1("H3StreamBufferSize", set_h3_stream_buffer_size, NULL, RSRC_CONF, "Per-stream request and streaming-response buffer size in bytes (default: 65536)");
const command_rec cmd_7 = AP_INIT_TAKE1("H3MaxRequestBodySize", set_h3_max_request_body_size, NULL, RSRC_CONF, "Maximum HTTP/3 request body size in bytes, fully buffered in memory (default: 10485760)");
const command_rec cmd_8 = AP_INIT_FLAG("H3AltSvc", set_h3_alt_svc, NULL, RSRC_CONF, "Whether to advertise HTTP/3 support via an Alt-Svc response header, required for browser discovery (default: on)");
const command_rec cmd_9 = AP_INIT_TAKE1("H3AltSvcMaxAge", set_h3_alt_svc_max_age, NULL, RSRC_CONF, "Seconds a client may cache the Alt-Svc HTTP/3 advertisement for (default: 86400)");
const command_rec cmd_10 = AP_INIT_TAKE1("H3HandshakeTimeout", set_h3_handshake_timeout, NULL, RSRC_CONF, "Timeout in seconds for QUIC handshakes to complete (default: 10)");
const command_rec cmd_11 = AP_INIT_TAKE1("H3IdleTimeout", set_h3_idle_timeout, NULL, RSRC_CONF, "Idle timeout in seconds for QUIC connections (default: 300)");
const command_rec cmd_12 = AP_INIT_TAKE1("H3MaxResponseBodySize", set_h3_max_response_body_size, NULL, RSRC_CONF, "Maximum HTTP/3 response body size in bytes; an explicit limit enables bounded whole-response buffering (default: unlimited streaming)");
const command_rec cmd_13 = AP_INIT_FLAG("H3AddressValidation", set_h3_address_validation, NULL, RSRC_CONF, "Whether to validate client addresses with a QUIC Retry packet before accepting a connection (default: on)");
const command_rec cmd_14 = AP_INIT_TAKE1("H3QuicEngine", set_h3_quic_engine, NULL, RSRC_CONF, "QUIC engine to run, among those compiled in (default: openssl)");
const command_rec cmd_end = AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL);
const command_rec h3_cmds[] = {cmd_1, cmd_2, cmd_3, cmd_4, cmd_5, cmd_6, cmd_7, cmd_8, cmd_9, cmd_10, cmd_11, cmd_12, cmd_13, cmd_14, cmd_end};