aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/event/ngx_event_quic.c40
-rw-r--r--src/event/ngx_event_quic.h27
-rw-r--r--src/event/ngx_event_quic_transport.c104
-rw-r--r--src/event/ngx_event_quic_transport.h21
-rw-r--r--src/http/ngx_http_request.c5
-rw-r--r--src/http/v3/ngx_http_v3.h5
-rw-r--r--src/http/v3/ngx_http_v3_module.c182
7 files changed, 361 insertions, 23 deletions
diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c
index 629d83360..2f623ee30 100644
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -32,6 +32,8 @@ struct ngx_quic_connection_s {
ngx_str_t dcid;
ngx_str_t token;
+ ngx_quic_tp_t tp;
+
/* current packet numbers for each namespace */
ngx_uint_t initial_pn;
ngx_uint_t handshake_pn;
@@ -67,7 +69,7 @@ static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
- ngx_quic_header_t *pkt);
+ ngx_quic_tp_t *tp, ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
static void ngx_quic_handshake_handler(ngx_event_t *rev);
static void ngx_quic_close_connection(ngx_connection_t *c);
@@ -283,8 +285,8 @@ ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
void
-ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout,
- ngx_connection_handler_pt handler)
+ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
+ ngx_msec_t timeout, ngx_connection_handler_pt handler)
{
ngx_buf_t *b;
ngx_quic_header_t pkt;
@@ -302,7 +304,7 @@ ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout,
pkt.data = b->start;
pkt.len = b->last - b->start;
- if (ngx_quic_new_connection(c, ssl, &pkt) != NGX_OK) {
+ if (ngx_quic_new_connection(c, ssl, tp, &pkt) != NGX_OK) {
ngx_quic_close_connection(c);
return;
}
@@ -320,7 +322,7 @@ ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout,
static ngx_int_t
-ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
+ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
ngx_quic_header_t *pkt)
{
ngx_quic_connection_t *qc;
@@ -354,6 +356,7 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
c->quic = qc;
qc->ssl = ssl;
+ qc->tp = *tp;
qc->dcid.len = pkt->dcid.len;
qc->dcid.data = ngx_pnalloc(c->pool, pkt->dcid.len);
@@ -402,19 +405,11 @@ static ngx_int_t
ngx_quic_init_connection(ngx_connection_t *c)
{
int n, sslerr;
+ u_char *p;
+ ssize_t len;
ngx_ssl_conn_t *ssl_conn;
ngx_quic_connection_t *qc;
- static const uint8_t params[] =
- "\x00\x29" /* parameters length: 41 bytes */
- "\x00\x0e\x00\x01\x05" /* active connection id limit: 5 */
- "\x00\x04\x00\x04\x80\x98\x96\x80" /* initial max data = 10000000 */
- "\x00\x09\x00\x01\x03" /* initial max streams uni: 3 */
- "\x00\x08\x00\x01\x10" /* initial max streams bidi: 16 */
- "\x00\x05\x00\x02\x40\xff" /* initial max stream bidi local: 255 */
- "\x00\x06\x00\x02\x40\xff" /* initial max stream bidi remote: 255 */
- "\x00\x07\x00\x02\x40\xff"; /* initial max stream data uni: 255 */
-
qc = c->quic;
if (ngx_ssl_create_connection(qc->ssl, c, NGX_SSL_BUFFER) != NGX_OK) {
@@ -429,7 +424,20 @@ ngx_quic_init_connection(ngx_connection_t *c)
return NGX_ERROR;
}
- if (SSL_set_quic_transport_params(ssl_conn, params, sizeof(params) - 1) == 0) {
+ len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp);
+ /* always succeeds */
+
+ p = ngx_pnalloc(c->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ len = ngx_quic_create_transport_params(p, p + len, &qc->tp);
+ if (len < 0) {
+ return NGX_ERROR;
+ }
+
+ if (SSL_set_quic_transport_params(ssl_conn, p, len) == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"SSL_set_quic_transport_params() failed");
return NGX_ERROR;
diff --git a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h
index b3b86d99b..e30186d1b 100644
--- a/src/event/ngx_event_quic.h
+++ b/src/event/ngx_event_quic.h
@@ -15,6 +15,29 @@
//#define quic_version 0xff00001b /* draft-27 (FFN 76) */
+typedef struct {
+ /* configurable */
+ ngx_msec_t max_idle_timeout;
+ ngx_msec_t max_ack_delay;
+
+ ngx_uint_t max_packet_size;
+ ngx_uint_t initial_max_data;
+ ngx_uint_t initial_max_stream_data_bidi_local;
+ ngx_uint_t initial_max_stream_data_bidi_remote;
+ ngx_uint_t initial_max_stream_data_uni;
+ ngx_uint_t initial_max_streams_bidi;
+ ngx_uint_t initial_max_streams_uni;
+ ngx_uint_t ack_delay_exponent;
+ ngx_uint_t disable_active_migration;
+ ngx_uint_t active_connection_id_limit;
+
+ /* TODO */
+ ngx_uint_t original_connection_id;
+ u_char stateless_reset_token[16];
+ void *preferred_address;
+} ngx_quic_tp_t;
+
+
struct ngx_quic_stream_s {
uint64_t id;
ngx_uint_t unidirectional:1;
@@ -25,8 +48,8 @@ struct ngx_quic_stream_s {
void ngx_quic_init_ssl_methods(SSL_CTX* ctx);
-void ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout,
- ngx_connection_handler_pt handler);
+void ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
+ ngx_msec_t timeout, ngx_connection_handler_pt handler);
ngx_connection_t *ngx_quic_create_uni_stream(ngx_connection_t *c);
diff --git a/src/event/ngx_event_quic_transport.c b/src/event/ngx_event_quic_transport.c
index 9d7302021..0eee5052f 100644
--- a/src/event/ngx_event_quic_transport.c
+++ b/src/event/ngx_event_quic_transport.c
@@ -87,6 +87,7 @@ static char *ngx_quic_errors[] = {
"INVALID_TOKEN",
"",
"CRYPTO_BUFFER_EXCEEDED",
+ "",
"CRYPTO_ERROR",
};
@@ -639,11 +640,11 @@ ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end,
return NGX_ERROR;
}
- if (f->u.close.error_code > NGX_QUIC_ERR_LAST) {
+ if (f->u.close.error_code >= NGX_QUIC_ERR_LAST) {
ngx_log_error(NGX_LOG_ERR, pkt->log, 0,
"unkown error code: %ui, truncated",
f->u.close.error_code);
- f->u.close.error_code = NGX_QUIC_ERR_LAST;
+ f->u.close.error_code = NGX_QUIC_ERR_LAST - 1;
}
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
@@ -963,6 +964,105 @@ ngx_quic_create_max_streams(u_char *p, ngx_quic_max_streams_frame_t *ms)
}
+ssize_t
+ngx_quic_create_transport_params(u_char *pos, u_char *end, ngx_quic_tp_t *tp)
+{
+ u_char *p;
+ size_t len;
+
+#if (quic_version < 0xff00001b)
+
+/* older drafts with static transport parameters encoding */
+
+#define ngx_quic_tp_len(id, value) \
+ 4 + ngx_quic_varint_len(value)
+
+#define ngx_quic_tp_vint(id, value) \
+ do { \
+ p = ngx_quic_write_uint16(p, id); \
+ p = ngx_quic_write_uint16(p, ngx_quic_varint_len(value)); \
+ ngx_quic_build_int(&p, value); \
+ } while (0)
+
+#else
+
+/* recent drafts with variable integer transport parameters encoding */
+
+#define ngx_quic_tp_len(id, value) \
+ ngx_quic_varint_len(id) \
+ + ngx_quic_varint_len(value) \
+ + ngx_quic_varint_len(ngx_quic_varint_len(value))
+
+#define ngx_quic_tp_vint(id, value) \
+ do { \
+ ngx_quic_build_int(&p, id); \
+ ngx_quic_build_int(&p, ngx_quic_varint_len(value)); \
+ ngx_quic_build_int(&p, value); \
+ } while (0)
+
+#endif
+
+ p = pos;
+
+ len = ngx_quic_tp_len(NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT,
+ tp->active_connection_id_limit);
+
+ len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_DATA,tp->initial_max_data);
+
+ len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI,
+ tp->initial_max_streams_uni);
+
+ len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI,
+ tp->initial_max_streams_bidi);
+
+ len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ tp->initial_max_stream_data_bidi_local);
+
+ len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+ tp->initial_max_stream_data_bidi_remote);
+
+ len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI,
+ tp->initial_max_stream_data_uni);
+
+ if (pos == NULL) {
+#if (quic_version < 0xff00001b)
+ len += ngx_quic_varint_len(len);
+#endif
+ return len;
+ }
+
+#if (quic_version < 0xff00001b)
+ /* TLS extension length */
+ p = ngx_quic_write_uint16(p, len);
+#endif
+
+ ngx_quic_tp_vint(NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT,
+ tp->active_connection_id_limit);
+
+ ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_DATA,
+ tp->initial_max_data);
+
+ ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI,
+ tp->initial_max_streams_uni);
+
+ ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI,
+ tp->initial_max_streams_bidi);
+
+ ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ tp->initial_max_stream_data_bidi_local);
+
+ ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+ tp->initial_max_stream_data_bidi_remote);
+
+ ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI,
+ tp->initial_max_stream_data_uni);
+
+ ngx_quic_hexdump0(ngx_cycle->log, "transport parameters", pos, p - pos);
+
+ return p - pos;
+}
+
+
static size_t
ngx_quic_create_close(u_char *p, ngx_quic_close_frame_t *cl)
{
diff --git a/src/event/ngx_event_quic_transport.h b/src/event/ngx_event_quic_transport.h
index 8e6d81fe5..9fd6ac16b 100644
--- a/src/event/ngx_event_quic_transport.h
+++ b/src/event/ngx_event_quic_transport.h
@@ -65,10 +65,28 @@
#define NGX_QUIC_ERR_INVALID_TOKEN 0x0B
/* 0xC is not defined */
#define NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED 0x0D
+/* 0xE is not defined */
#define NGX_QUIC_ERR_CRYPTO_ERROR 0x10
#define NGX_QUIC_ERR_LAST NGX_QUIC_ERR_CRYPTO_ERROR
+/* Transport parameters */
+#define NGX_QUIC_TP_ORIGINAL_CONNECTION_ID 0x00
+#define NGX_QUIC_TP_MAX_IDLE_TIMEOUT 0x01
+#define NGX_QUIC_TP_STATELESS_RESET_TOKEN 0x02
+#define NGX_QUIC_TP_MAX_PACKET_SIZE 0x03
+#define NGX_QUIC_TP_INITIAL_MAX_DATA 0x04
+#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL 0x05
+#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE 0x06
+#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI 0x07
+#define NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI 0x08
+#define NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI 0x09
+#define NGX_QUIC_TP_ACK_DELAY_EXPONENT 0x0a
+#define NGX_QUIC_TP_MAX_ACK_DELAY 0x0b
+#define NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION 0x0c
+#define NGX_QUIC_TP_PREFERRED_ADDRESS 0x0d
+#define NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT 0x0e
+
typedef struct {
ngx_uint_t pn;
@@ -208,4 +226,7 @@ ssize_t ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end,
ngx_quic_frame_t *frame);
ssize_t ngx_quic_create_frame(u_char *p, u_char *end, ngx_quic_frame_t *f);
+ssize_t ngx_quic_create_transport_params(u_char *p, u_char *end,
+ ngx_quic_tp_t *tp);
+
#endif /* _NGX_EVENT_QUIC_WIRE_H_INCLUDED_ */
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 687de931c..6c9fdc543 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -341,11 +341,14 @@ ngx_http_init_connection(ngx_connection_t *c)
#if (NGX_HTTP_V3)
if (hc->quic) {
+ ngx_http_v3_srv_conf_t *v3cf;
ngx_http_ssl_srv_conf_t *sscf;
+ v3cf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
- ngx_quic_run(c, &sscf->ssl, c->listening->post_accept_timeout,
+ ngx_quic_run(c, &sscf->ssl, &v3cf->quic,
+ c->listening->post_accept_timeout,
ngx_http_quic_stream_handler);
return;
}
diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h
index fc1cadd79..2c63f544e 100644
--- a/src/http/v3/ngx_http_v3.h
+++ b/src/http/v3/ngx_http_v3.h
@@ -40,6 +40,11 @@
typedef struct {
+ ngx_quic_tp_t quic;
+} ngx_http_v3_srv_conf_t;
+
+
+typedef struct {
ngx_http_connection_t hc;
ngx_array_t *dynamic;
diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c
index af310bd44..6d3f34d41 100644
--- a/src/http/v3/ngx_http_v3_module.c
+++ b/src/http/v3/ngx_http_v3_module.c
@@ -11,10 +11,100 @@
static ngx_command_t ngx_http_v3_commands[] = {
+
+ { ngx_string("quic_max_idle_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.max_idle_timeout),
+ NULL },
+
+ { ngx_string("quic_max_ack_delay"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.max_ack_delay),
+ NULL },
+
+ { ngx_string("quic_max_packet_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.max_packet_size),
+ NULL },
+
+ { ngx_string("quic_initial_max_data"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_data),
+ NULL },
+
+ { ngx_string("quic_initial_max_stream_data_bidi_local"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_stream_data_bidi_local),
+ NULL },
+
+ { ngx_string("quic_initial_max_stream_data_bidi_remote"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_stream_data_bidi_remote),
+ NULL },
+
+ { ngx_string("quic_initial_max_stream_data_uni"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_stream_data_uni),
+ NULL },
+
+ { ngx_string("quic_initial_max_streams_bidi"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_streams_bidi),
+ NULL },
+
+ { ngx_string("quic_initial_max_streams_uni"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_streams_uni),
+ NULL },
+
+ { ngx_string("quic_ack_delay_exponent"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.ack_delay_exponent),
+ NULL },
+
+ { ngx_string("quic_active_migration"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.disable_active_migration),
+ NULL },
+
+ { ngx_string("quic_active_connection_id_limit"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_v3_srv_conf_t, quic.active_connection_id_limit),
+ NULL },
+
ngx_null_command
};
+static void *ngx_http_v3_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_http_v3_merge_srv_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+
+
static ngx_http_module_t ngx_http_v3_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
@@ -22,8 +112,8 @@ static ngx_http_module_t ngx_http_v3_module_ctx = {
NULL, /* create main configuration */
NULL, /* init main configuration */
- NULL, /* create server configuration */
- NULL, /* merge server configuration */
+ ngx_http_v3_create_srv_conf, /* create server configuration */
+ ngx_http_v3_merge_srv_conf, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
@@ -44,3 +134,91 @@ ngx_module_t ngx_http_v3_module = {
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
+
+
+static void *
+ngx_http_v3_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_http_v3_srv_conf_t *v3cf;
+
+ v3cf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_srv_conf_t));
+ if (v3cf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ * v3cf->quic.original_connection_id = 0;
+ * v3cf->quic.stateless_reset_token = { 0 }
+ * conf->quic.preferred_address = NULL
+ */
+
+ v3cf->quic.max_idle_timeout = NGX_CONF_UNSET_MSEC;
+ v3cf->quic.max_ack_delay = NGX_CONF_UNSET_MSEC;
+
+ v3cf->quic.max_packet_size = NGX_CONF_UNSET_UINT;
+ v3cf->quic.initial_max_data = NGX_CONF_UNSET_UINT;
+ v3cf->quic.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_UINT;
+ v3cf->quic.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_UINT;
+ v3cf->quic.initial_max_stream_data_uni = NGX_CONF_UNSET_UINT;
+ v3cf->quic.initial_max_streams_bidi = NGX_CONF_UNSET_UINT;
+ v3cf->quic.initial_max_streams_uni = NGX_CONF_UNSET_UINT;
+ v3cf->quic.ack_delay_exponent = NGX_CONF_UNSET_UINT;
+ v3cf->quic.disable_active_migration = NGX_CONF_UNSET_UINT;
+ v3cf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT;
+
+ return v3cf;
+}
+
+
+static char *
+ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_v3_srv_conf_t *prev = parent;
+ ngx_http_v3_srv_conf_t *conf = child;
+
+ ngx_conf_merge_msec_value(conf->quic.max_idle_timeout,
+ prev->quic.max_idle_timeout, 10000);
+
+ // > 2 ^ 14 is invalid
+ ngx_conf_merge_msec_value(conf->quic.max_ack_delay,
+ prev->quic.max_ack_delay, 25);
+
+ // < 1200 is invalid
+ ngx_conf_merge_uint_value(conf->quic.max_packet_size,
+ prev->quic.max_packet_size, 65527);
+
+ ngx_conf_merge_uint_value(conf->quic.initial_max_data,
+ prev->quic.initial_max_data, 10000000);
+
+ ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_bidi_local,
+ prev->quic.initial_max_stream_data_bidi_local,
+ 255);
+
+ ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_bidi_remote,
+ prev->quic.initial_max_stream_data_bidi_remote,
+ 255);
+
+ ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_uni,
+ prev->quic.initial_max_stream_data_uni, 255);
+
+ ngx_conf_merge_uint_value(conf->quic.initial_max_streams_bidi,
+ prev->quic.initial_max_streams_bidi, 16);
+
+ ngx_conf_merge_uint_value(conf->quic.initial_max_streams_uni,
+ prev->quic.initial_max_streams_uni, 16);
+
+ // > 20 is invalid
+ ngx_conf_merge_uint_value(conf->quic.ack_delay_exponent,
+ prev->quic.ack_delay_exponent, 3);
+
+ ngx_conf_merge_uint_value(conf->quic.disable_active_migration,
+ prev->quic.disable_active_migration, 1);
+
+ // < 2 is invalid
+ ngx_conf_merge_uint_value(conf->quic.active_connection_id_limit,
+ prev->quic.active_connection_id_limit, 2);
+
+ return NGX_CONF_OK;
+}
+