From e92cb24f40b865e3cc5b9f0993e328e4f0642e0f Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:51 +0300 Subject: HTTP UDP layer, QUIC support autotest. --- src/http/ngx_http.c | 7 ++++++- src/http/ngx_http_core_module.c | 6 ++++++ src/http/ngx_http_core_module.h | 2 ++ src/http/ngx_http_request.c | 10 ++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 79ef9c644..3e82dc60d 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1163,7 +1163,10 @@ ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, port = cmcf->ports->elts; for (i = 0; i < cmcf->ports->nelts; i++) { - if (p != port[i].port || sa->sa_family != port[i].family) { + if (p != port[i].port + || lsopt->type != port[i].type + || sa->sa_family != port[i].family) + { continue; } @@ -1180,6 +1183,7 @@ ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, } port->family = sa->sa_family; + port->type = lsopt->type; port->port = p; port->addrs.elts = NULL; @@ -1735,6 +1739,7 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) } #endif + ls->type = addr->opt.type; ls->backlog = addr->opt.backlog; ls->rcvbuf = addr->opt.rcvbuf; ls->sndbuf = addr->opt.sndbuf; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 4867bed2b..b0c8aabfc 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -3800,6 +3800,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t)); lsopt.backlog = NGX_LISTEN_BACKLOG; + lsopt.type = SOCK_STREAM; lsopt.rcvbuf = -1; lsopt.sndbuf = -1; #if (NGX_HAVE_SETFIB) @@ -3821,6 +3822,11 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) continue; } + if (ngx_strcmp(value[n].data, "quic") == 0) { + lsopt.type = SOCK_DGRAM; + continue; + } + if (ngx_strcmp(value[n].data, "bind") == 0) { lsopt.set = 1; lsopt.bind = 1; diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index f5434cc51..9f26b5794 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -86,6 +86,7 @@ typedef struct { int backlog; int rcvbuf; int sndbuf; + int type; #if (NGX_HAVE_SETFIB) int setfib; #endif @@ -266,6 +267,7 @@ typedef struct { typedef struct { ngx_int_t family; + ngx_int_t type; in_port_t port; ngx_array_t addrs; /* array of ngx_http_conf_addr_t */ } ngx_http_conf_port_t; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index bb69e71d0..f137590fd 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -324,6 +324,10 @@ ngx_http_init_connection(ngx_connection_t *c) rev->handler = ngx_http_wait_request_handler; c->write->handler = ngx_http_empty_handler; + if (c->shared) { + rev->ready = 1; + } + #if (NGX_HTTP_V2) if (hc->addr_conf->http2) { rev->handler = ngx_http_v2_init; @@ -386,6 +390,10 @@ ngx_http_wait_request_handler(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler"); + if (c->shared) { + goto request; + } + if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); ngx_http_close_connection(c); @@ -486,6 +494,8 @@ ngx_http_wait_request_handler(ngx_event_t *rev) } } +request: + c->log->action = "reading client request line"; ngx_reusable_connection(c, 0); -- cgit From 26ac1c73f0fe90c77cbad84a6b4ef5712e35ba52 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:51 +0300 Subject: Initial QUIC support in http. --- src/http/modules/ngx_http_ssl_module.c | 27 +- src/http/modules/ngx_http_ssl_module.h | 1 + src/http/ngx_http.c | 26 ++ src/http/ngx_http_core_module.c | 18 +- src/http/ngx_http_core_module.h | 2 + src/http/ngx_http_request.c | 495 +++++++++++++++++++++++++++++++++ src/http/ngx_http_request.h | 1 + 7 files changed, 564 insertions(+), 6 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 495e628d3..693e45a1c 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -249,6 +249,13 @@ static ngx_command_t ngx_http_ssl_commands[] = { offsetof(ngx_http_ssl_srv_conf_t, early_data), NULL }, + { ngx_string("ssl_quic"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, quic), + NULL }, + ngx_null_command }; @@ -568,6 +575,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) sscf->enable = NGX_CONF_UNSET; sscf->prefer_server_ciphers = NGX_CONF_UNSET; sscf->early_data = NGX_CONF_UNSET; + sscf->quic = NGX_CONF_UNSET; sscf->buffer_size = NGX_CONF_UNSET_SIZE; sscf->verify = NGX_CONF_UNSET_UINT; sscf->verify_depth = NGX_CONF_UNSET_UINT; @@ -612,6 +620,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->early_data, prev->early_data, 0); + ngx_conf_merge_value(conf->quic, prev->quic, 0); + ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); @@ -696,6 +706,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } } +printf("ngx_ssl_create\n"); if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -857,6 +868,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } + if (ngx_ssl_quic(cf, &conf->ssl, conf->quic) != NGX_OK) { + return NGX_CONF_ERROR; + } + return NGX_CONF_OK; } @@ -1141,13 +1156,15 @@ ngx_http_ssl_init(ngx_conf_t *cf) addr = port[p].addrs.elts; for (a = 0; a < port[p].addrs.nelts; a++) { +printf("ssl %d http3 %d\n", addr[a].opt.ssl, addr[a].opt.http3); - if (!addr[a].opt.ssl) { + if (!addr[a].opt.ssl && !addr[a].opt.http3) { continue; } cscf = addr[a].default_server; sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; +printf("sscf->protocols %lx\n", sscf->protocols); if (sscf->certificates == NULL) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, @@ -1156,6 +1173,14 @@ ngx_http_ssl_init(ngx_conf_t *cf) cscf->file_name, cscf->line); return NGX_ERROR; } + + if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "\"ssl_protocols\" did not enable TLSv1.3 for " + "the \"listen ... http3\" directive in %s:%ui", + cscf->file_name, cscf->line); + return NGX_ERROR; + } } } diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h index 26fdccfe4..310d7c737 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -21,6 +21,7 @@ typedef struct { ngx_flag_t prefer_server_ciphers; ngx_flag_t early_data; + ngx_flag_t quic; ngx_uint_t protocols; diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 3e82dc60d..10f88fabb 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1203,6 +1203,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #if (NGX_HTTP_V2) ngx_uint_t http2; #endif +#if (NGX_HTTP_SSL) + ngx_uint_t http3; +#endif /* * we cannot compare whole sockaddr struct's as kernel @@ -1238,6 +1241,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #if (NGX_HTTP_V2) http2 = lsopt->http2 || addr[i].opt.http2; #endif +#if (NGX_HTTP_SSL) + http3 = lsopt->http3 || addr[i].opt.http3; +#endif if (lsopt->set) { @@ -1274,6 +1280,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #if (NGX_HTTP_V2) addr[i].opt.http2 = http2; #endif +#if (NGX_HTTP_SSL) + addr[i].opt.http3 = http3; +#endif return NGX_OK; } @@ -1315,6 +1324,17 @@ ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, &lsopt->addr_text); } +#endif + +#if (NGX_HTTP_SSL && !defined NGX_OPENSSL_QUIC) + + if (lsopt->http3) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "nginx was built with OpenSSL that lacks QUIC " + "support, HTTP/3 is not enabled for %V", + &lsopt->addr_text); + } + #endif addr = ngx_array_push(&port->addrs); @@ -1806,6 +1826,9 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, #endif #if (NGX_HTTP_V2) addrs[i].conf.http2 = addr[i].opt.http2; +#endif +#if (NGX_HTTP_SSL) + addrs[i].conf.http3 = addr[i].opt.http3; #endif addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; @@ -1871,6 +1894,9 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, #endif #if (NGX_HTTP_V2) addrs6[i].conf.http2 = addr[i].opt.http2; +#endif +#if (NGX_HTTP_SSL) + addrs6[i].conf.http3 = addr[i].opt.http3; #endif addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index b0c8aabfc..5c210bebd 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -3822,11 +3822,6 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) continue; } - if (ngx_strcmp(value[n].data, "quic") == 0) { - lsopt.type = SOCK_DGRAM; - continue; - } - if (ngx_strcmp(value[n].data, "bind") == 0) { lsopt.set = 1; lsopt.bind = 1; @@ -4004,6 +3999,19 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #endif } + if (ngx_strcmp(value[n].data, "http3") == 0) { +#if (NGX_HTTP_SSL) + lsopt.http3 = 1; + lsopt.type = SOCK_DGRAM; + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"http3\" parameter requires " + "ngx_http_ssl_module"); + return NGX_CONF_ERROR; +#endif + } + if (ngx_strcmp(value[n].data, "spdy") == 0) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "invalid parameter \"spdy\": " diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index 9f26b5794..25327b2f4 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -75,6 +75,7 @@ typedef struct { unsigned wildcard:1; unsigned ssl:1; unsigned http2:1; + unsigned http3:1; #if (NGX_HAVE_INET6) unsigned ipv6only:1; #endif @@ -238,6 +239,7 @@ struct ngx_http_addr_conf_s { unsigned ssl:1; unsigned http2:1; + unsigned http3:1; unsigned proxy_protocol:1; }; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index f137590fd..54a0da497 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -62,6 +62,8 @@ static u_char *ngx_http_log_error_handler(ngx_http_request_t *r, #if (NGX_HTTP_SSL) static void ngx_http_ssl_handshake(ngx_event_t *rev); static void ngx_http_ssl_handshake_handler(ngx_connection_t *c); + +static void ngx_http_quic_handshake(ngx_event_t *rev); #endif @@ -328,6 +330,14 @@ ngx_http_init_connection(ngx_connection_t *c) rev->ready = 1; } +#if (NGX_HTTP_SSL) + if (hc->addr_conf->http3) { + hc->quic = 1; + c->log->action = "QUIC handshaking"; + rev->handler = ngx_http_quic_handshake; + } +#endif + #if (NGX_HTTP_V2) if (hc->addr_conf->http2) { rev->handler = ngx_http_v2_init; @@ -647,6 +657,491 @@ ngx_http_alloc_request(ngx_connection_t *c) #if (NGX_HTTP_SSL) +static uint64_t +ngx_quic_parse_int(u_char **pos) +{ + u_char *p; + uint64_t value; + ngx_uint_t len; + + p = *pos; + len = 1 << ((*p & 0xc0) >> 6); + value = *p++ & 0x3f; + + while (--len) { + value = (value << 8) + *p++; + } + + *pos = p; + return value; +} + + +static uint64_t +ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask) +{ + u_char *p; + uint64_t value; + + p = *pos; + value = *p++ ^ *mask++; + + while (--len) { + value = (value << 8) + (*p++ ^ *mask++); + } + + *pos = p; + return value; +} + + +static void +ngx_http_quic_handshake(ngx_event_t *rev) +{ + int n, sslerr; +#if (NGX_DEBUG) + u_char buf[512]; + size_t m; +#endif + ngx_buf_t *b; + ngx_connection_t *c; + ngx_http_connection_t *hc; + ngx_quic_connection_t *qc; + ngx_http_ssl_srv_conf_t *sscf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake"); + + c = rev->data; + hc = c->data; + b = c->buffer; + + qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); + if (qc == NULL) { + ngx_http_close_connection(c); + return; + } + + c->quic = qc; + + printf("buffer %p %p:%p:%p:%p \n", b, b->start, b->pos, b->last, b->end); + + if ((b->pos[0] & 0xf0) != 0xc0) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid initial packet"); + ngx_http_close_connection(c); + return; + } + + if (ngx_buf_size(b) < 1200) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "too small UDP datagram"); + ngx_http_close_connection(c); + return; + } + + ngx_int_t flags = *b->pos++; + uint32_t version = ngx_http_v2_parse_uint32(b->pos); + b->pos += 4; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic flags:%xi version:%xD", flags, version); + + if (version != 0xff000017) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); + ngx_http_close_connection(c); + return; + } + + qc->dcid.len = *b->pos++; + qc->dcid.data = ngx_pnalloc(c->pool, qc->dcid.len); + if (qc->dcid.data == NULL) { + ngx_http_close_connection(c); + return; + } + + ngx_memcpy(qc->dcid.data, b->pos, qc->dcid.len); + b->pos += qc->dcid.len; + + qc->scid.len = *b->pos++; + qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len); + if (qc->scid.data == NULL) { + ngx_http_close_connection(c); + return; + } + + ngx_memcpy(qc->scid.data, b->pos, qc->scid.len); + b->pos += qc->scid.len; + + qc->token.len = ngx_quic_parse_int(&b->pos); + qc->token.data = ngx_pnalloc(c->pool, qc->token.len); + if (qc->token.data == NULL) { + ngx_http_close_connection(c); + return; + } + + ngx_memcpy(qc->token.data, b->pos, qc->token.len); + b->pos += qc->token.len; + + uint64_t plen = ngx_quic_parse_int(&b->pos); + /* draft-ietf-quic-tls-23#section-5.4.2: + * the Packet Number field is assumed to be 4 bytes long + * draft-ietf-quic-tls-23#section-5.4.3: + * AES-Based header protection samples 16 bytes + */ + u_char *sample = b->pos + 4; + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, qc->dcid.data, qc->dcid.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic DCID: %*s, len: %uz", m, buf, qc->dcid.len); + + m = ngx_hex_dump(buf, qc->scid.data, qc->scid.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic SCID: %*s, len: %uz", m, buf, qc->scid.len); + + m = ngx_hex_dump(buf, qc->token.data, qc->token.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic token: %*s, len: %uz", m, buf, qc->token.len); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic packet length: %d", plen); + + m = ngx_hex_dump(buf, sample, 16) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic sample: %*s", m, buf); + } +#endif + +// initial secret + + size_t is_len; + uint8_t is[SHA256_DIGEST_LENGTH]; + const EVP_MD *digest; + static const uint8_t salt[20] = + "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" + "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"; + + digest = EVP_sha256(); + HKDF_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, salt, + sizeof(salt)); + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, (uint8_t *) salt, sizeof(salt)) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic salt: %*s, len: %uz", m, buf, sizeof(salt)); + + m = ngx_hex_dump(buf, is, is_len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic initial secret: %*s, len: %uz", m, buf, is_len); + } +#endif + + size_t hkdfl_len; + uint8_t hkdfl[20]; + uint8_t *p; + + /* draft-ietf-quic-tls-23#section-5.2 */ + + qc->client_in.len = SHA256_DIGEST_LENGTH; + qc->client_in.data = ngx_pnalloc(c->pool, qc->client_in.len); + if (qc->client_in.data == NULL) { + ngx_http_close_connection(c); + return; + } + + hkdfl_len = 2 + 1 + sizeof("tls13 client in") - 1 + 1; + hkdfl[0] = 0; + hkdfl[1] = qc->client_in.len; + hkdfl[2] = sizeof("tls13 client in") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 client in", + sizeof("tls13 client in") - 1); + *p = '\0'; + + if (HKDF_expand(qc->client_in.data, qc->client_in.len, + digest, is, is_len, hkdfl, hkdfl_len) + == 0) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "HKDF_expand(client_in) failed"); + ngx_http_close_connection(c); + return; + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic EVP key:%d tag:%d nonce:%d", + EVP_AEAD_key_length(EVP_aead_aes_128_gcm()), + EVP_AEAD_max_tag_len(EVP_aead_aes_128_gcm()), + EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm())); + + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ + + qc->client_in_key.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); + qc->client_in_key.data = ngx_pnalloc(c->pool, qc->client_in_key.len); + if (qc->client_in_key.data == NULL) { + ngx_http_close_connection(c); + return; + } + + hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1; + hkdfl[1] = qc->client_in_key.len; + hkdfl[2] = sizeof("tls13 quic key") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic key", + sizeof("tls13 quic key") - 1); + *p = '\0'; + + if (HKDF_expand(qc->client_in_key.data, qc->client_in_key.len, + digest, qc->client_in.data, qc->client_in.len, + hkdfl, hkdfl_len) + == 0) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "HKDF_expand(client_in_key) failed"); + ngx_http_close_connection(c); + return; + } + + qc->client_in_iv.len = EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm()); + qc->client_in_iv.data = ngx_pnalloc(c->pool, qc->client_in_iv.len); + if (qc->client_in_iv.data == NULL) { + ngx_http_close_connection(c); + return; + } + + hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1; + hkdfl[1] = qc->client_in_iv.len; + hkdfl[2] = sizeof("tls13 quic iv") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); + *p = '\0'; + + if (HKDF_expand(qc->client_in_iv.data, qc->client_in_iv.len, digest, + qc->client_in.data, qc->client_in.len, hkdfl, hkdfl_len) + == 0) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "HKDF_expand(client_in_iv) failed"); + ngx_http_close_connection(c); + return; + } + + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ + + qc->client_in_hp.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); + qc->client_in_hp.data = ngx_pnalloc(c->pool, qc->client_in_hp.len); + if (qc->client_in_hp.data == NULL) { + ngx_http_close_connection(c); + return; + } + + hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1; + hkdfl[1] = qc->client_in_hp.len; + hkdfl[2] = sizeof("tls13 quic hp") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); + *p = '\0'; + + if (HKDF_expand(qc->client_in_hp.data, qc->client_in_hp.len, digest, + qc->client_in.data, qc->client_in.len, hkdfl, hkdfl_len) + == 0) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "HKDF_expand(client_in_hp) failed"); + ngx_http_close_connection(c); + return; + } + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, qc->client_in.data, qc->client_in.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic client initial secret: %*s, len: %uz", + m, buf, qc->client_in.len); + + m = ngx_hex_dump(buf, qc->client_in_key.data, qc->client_in_key.len) + - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic key: %*s, len: %uz", + m, buf, qc->client_in_key.len); + + m = ngx_hex_dump(buf, qc->client_in_iv.data, qc->client_in_iv.len) + - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic iv: %*s, len: %uz", m, buf, qc->client_in_iv.len); + + m = ngx_hex_dump(buf, qc->client_in_hp.data, qc->client_in_hp.len) + - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic hp: %*s, len: %uz", m, buf, qc->client_in_hp.len); + } +#endif + +// header protection + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + uint8_t mask[16]; + int outlen; + + if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, + qc->client_in_hp.data, NULL) + != 1) + { + EVP_CIPHER_CTX_free(ctx); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_EncryptInit_ex() failed"); + ngx_http_close_connection(c); + return; + } + + if (!EVP_EncryptUpdate(ctx, mask, &outlen, sample, 16)) { + EVP_CIPHER_CTX_free(ctx); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_EncryptUpdate() failed"); + ngx_http_close_connection(c); + return; + } + + EVP_CIPHER_CTX_free(ctx); + + u_char clearflags = flags ^ (mask[0] & 0x0f); + ngx_int_t pnl = (clearflags & 0x03) + 1; + uint64_t pn = ngx_quic_parse_pn(&b->pos, pnl, &mask[1]); + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, sample, 16) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic sample: %*s", m, buf); + + m = ngx_hex_dump(buf, mask, 5) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic mask: %*s", m, buf); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic packet number: %uL, len: %xi", pn, pnl); + } +#endif + +// packet protection + + ngx_str_t ciphertext; + ciphertext.data = b->pos; + ciphertext.len = plen - pnl; + + ngx_str_t ad; + ad.len = b->pos - b->start; + ad.data = ngx_pnalloc(c->pool, ad.len); + if (ad.data == NULL) { + ngx_http_close_connection(c); + return; + } + + ngx_memcpy(ad.data, b->start, ad.len); + ad.data[0] = clearflags; + ad.data[ad.len - pnl] = (u_char)pn; + + uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_in_iv); + nonce[11] ^= pn; + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, nonce, 12) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic nonce: %*s, len: %uz", m, buf, 12); + + m = ngx_hex_dump(buf, ad.data, ad.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic ad: %*s, len: %uz", m, buf, ad.len); + } +#endif + + EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(EVP_aead_aes_128_gcm(), + qc->client_in_key.data, + qc->client_in_key.len, + EVP_AEAD_DEFAULT_TAG_LENGTH); + uint8_t cleartext[1600]; + size_t cleartext_len = sizeof(cleartext); + + if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext), + nonce, qc->client_in_iv.len, ciphertext.data, + ciphertext.len, ad.data, ad.len) + != 1) + { + EVP_AEAD_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_AEAD_CTX_open() failed"); + ngx_http_close_connection(c); + return; + } + + EVP_AEAD_CTX_free(aead); + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, cleartext, ngx_min(cleartext_len, 256)) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic packet: %*s%s, len: %uz", + m, buf, m < 512 ? "" : "...", cleartext_len); + } +#endif + + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); + + if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) + != NGX_OK) + { + ngx_http_close_connection(c); + return; + } + + n = SSL_do_handshake(c->ssl->connection); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); + + if (n == -1) { + sslerr = SSL_get_error(c->ssl->connection, n); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", + sslerr); + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL_quic_read_level: %d, SSL_quic_write_level: %d", + (int) SSL_quic_read_level(c->ssl->connection), + (int) SSL_quic_write_level(c->ssl->connection)); + + if (!SSL_provide_quic_data(c->ssl->connection, + SSL_quic_read_level(c->ssl->connection), + &cleartext[4], cleartext_len - 4)) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "SSL_provide_quic_data() failed"); + ngx_http_close_connection(c); + return; + } + + n = SSL_do_handshake(c->ssl->connection); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); + + if (n == -1) { + sslerr = SSL_get_error(c->ssl->connection, n); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", + sslerr); + + if (sslerr == SSL_ERROR_SSL) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed"); + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL_quic_read_level: %d, SSL_quic_write_level: %d", + (int) SSL_quic_read_level(c->ssl->connection), + (int) SSL_quic_write_level(c->ssl->connection)); + + ngx_http_close_connection(c); + return; +} + + static void ngx_http_ssl_handshake(ngx_event_t *rev) { diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 70c2d424d..8cc5d6432 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -323,6 +323,7 @@ typedef struct { ngx_chain_t *free; unsigned ssl:1; + unsigned quic:1; unsigned proxy_protocol:1; } ngx_http_connection_t; -- cgit From f03fe916636c25bfe6ac9a63b48f28f4bfaa72b2 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:51 +0300 Subject: Server Initial Keys. --- src/http/ngx_http_request.c | 141 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 137 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 54a0da497..bc4f2a13b 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -898,7 +898,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) "HKDF_expand(client_in_key) failed"); ngx_http_close_connection(c); return; - } + } qc->client_in_iv.len = EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm()); qc->client_in_iv.data = ngx_pnalloc(c->pool, qc->client_in_iv.len); @@ -958,18 +958,151 @@ ngx_http_quic_handshake(ngx_event_t *rev) m = ngx_hex_dump(buf, qc->client_in_key.data, qc->client_in_key.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic key: %*s, len: %uz", + "quic client key: %*s, len: %uz", m, buf, qc->client_in_key.len); m = ngx_hex_dump(buf, qc->client_in_iv.data, qc->client_in_iv.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic iv: %*s, len: %uz", m, buf, qc->client_in_iv.len); + "quic client iv: %*s, len: %uz", + m, buf, qc->client_in_iv.len); m = ngx_hex_dump(buf, qc->client_in_hp.data, qc->client_in_hp.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic hp: %*s, len: %uz", m, buf, qc->client_in_hp.len); + "quic client hp: %*s, len: %uz", + m, buf, qc->client_in_hp.len); + } +#endif + +// server initial + + /* draft-ietf-quic-tls-23#section-5.2 */ + + qc->server_in.len = SHA256_DIGEST_LENGTH; + qc->server_in.data = ngx_pnalloc(c->pool, qc->server_in.len); + if (qc->server_in.data == NULL) { + ngx_http_close_connection(c); + return; + } + + hkdfl_len = 2 + 1 + sizeof("tls13 server in") - 1 + 1; + hkdfl[0] = 0; + hkdfl[1] = qc->server_in.len; + hkdfl[2] = sizeof("tls13 server in") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 server in", + sizeof("tls13 server in") - 1); + *p = '\0'; + + if (HKDF_expand(qc->server_in.data, qc->server_in.len, + digest, is, is_len, hkdfl, hkdfl_len) + == 0) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "HKDF_expand(server_in) failed"); + ngx_http_close_connection(c); + return; + } + + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ + + qc->server_in_key.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); + qc->server_in_key.data = ngx_pnalloc(c->pool, qc->server_in_key.len); + if (qc->server_in_key.data == NULL) { + ngx_http_close_connection(c); + return; + } + + hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1; + hkdfl[1] = qc->server_in_key.len; + hkdfl[2] = sizeof("tls13 quic key") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic key", + sizeof("tls13 quic key") - 1); + *p = '\0'; + + if (HKDF_expand(qc->server_in_key.data, qc->server_in_key.len, + digest, qc->server_in.data, qc->server_in.len, + hkdfl, hkdfl_len) + == 0) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "HKDF_expand(server_in_key) failed"); + ngx_http_close_connection(c); + return; + } + + qc->server_in_iv.len = EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm()); + qc->server_in_iv.data = ngx_pnalloc(c->pool, qc->server_in_iv.len); + if (qc->server_in_iv.data == NULL) { + ngx_http_close_connection(c); + return; + } + + hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1; + hkdfl[1] = qc->server_in_iv.len; + hkdfl[2] = sizeof("tls13 quic iv") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); + *p = '\0'; + + if (HKDF_expand(qc->server_in_iv.data, qc->server_in_iv.len, digest, + qc->server_in.data, qc->server_in.len, hkdfl, hkdfl_len) + == 0) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "HKDF_expand(server_in_iv) failed"); + ngx_http_close_connection(c); + return; + } + + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ + + qc->server_in_hp.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); + qc->server_in_hp.data = ngx_pnalloc(c->pool, qc->server_in_hp.len); + if (qc->server_in_hp.data == NULL) { + ngx_http_close_connection(c); + return; + } + + hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1; + hkdfl[1] = qc->server_in_hp.len; + hkdfl[2] = sizeof("tls13 quic hp") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); + *p = '\0'; + + if (HKDF_expand(qc->server_in_hp.data, qc->server_in_hp.len, digest, + qc->server_in.data, qc->server_in.len, hkdfl, hkdfl_len) + == 0) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "HKDF_expand(server_in_hp) failed"); + ngx_http_close_connection(c); + return; + } + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, qc->server_in.data, qc->server_in.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic server initial secret: %*s, len: %uz", + m, buf, qc->server_in.len); + + m = ngx_hex_dump(buf, qc->server_in_key.data, qc->server_in_key.len) + - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic server key: %*s, len: %uz", + m, buf, qc->server_in_key.len); + + m = ngx_hex_dump(buf, qc->server_in_iv.data, qc->server_in_iv.len) + - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic server iv: %*s, len: %uz", + m, buf, qc->server_in_iv.len); + + m = ngx_hex_dump(buf, qc->server_in_hp.data, qc->server_in_hp.len) + - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic server hp: %*s, len: %uz", + m, buf, qc->server_in_hp.len); } #endif -- cgit From b77c2d00b57233a87350dedbe44897fcb0b7adf1 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:51 +0300 Subject: QUIC set_encryption_secrets callback. --- src/http/ngx_http_request.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index bc4f2a13b..85b6835ee 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -783,8 +783,8 @@ ngx_http_quic_handshake(ngx_event_t *rev) uint64_t plen = ngx_quic_parse_int(&b->pos); /* draft-ietf-quic-tls-23#section-5.4.2: * the Packet Number field is assumed to be 4 bytes long - * draft-ietf-quic-tls-23#section-5.4.3: - * AES-Based header protection samples 16 bytes + * draft-ietf-quic-tls-23#section-5.4.[34]: + * AES-Based and ChaCha20-Based header protections sample 16 bytes */ u_char *sample = b->pos + 4; -- cgit From 812a0b69a072119de75d57ebdb222f540952c423 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:51 +0300 Subject: QUIC add_handshake_data callback, varint routines. --- src/http/ngx_http_request.c | 40 +--------------------------------------- 1 file changed, 1 insertion(+), 39 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 85b6835ee..d128190c5 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -657,44 +657,6 @@ ngx_http_alloc_request(ngx_connection_t *c) #if (NGX_HTTP_SSL) -static uint64_t -ngx_quic_parse_int(u_char **pos) -{ - u_char *p; - uint64_t value; - ngx_uint_t len; - - p = *pos; - len = 1 << ((*p & 0xc0) >> 6); - value = *p++ & 0x3f; - - while (--len) { - value = (value << 8) + *p++; - } - - *pos = p; - return value; -} - - -static uint64_t -ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask) -{ - u_char *p; - uint64_t value; - - p = *pos; - value = *p++ ^ *mask++; - - while (--len) { - value = (value << 8) + (*p++ ^ *mask++); - } - - *pos = p; - return value; -} - - static void ngx_http_quic_handshake(ngx_event_t *rev) { @@ -1210,7 +1172,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { m = ngx_hex_dump(buf, cleartext, ngx_min(cleartext_len, 256)) - buf; ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet: %*s%s, len: %uz", + "quic packet payload: %*s%s, len: %uz", m, buf, m < 512 ? "" : "...", cleartext_len); } #endif -- cgit From ac640641a6ee09affe12a77cd7a944c1ac3148f0 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:51 +0300 Subject: OpenSSL compatibility. --- src/http/ngx_http_request.c | 251 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 199 insertions(+), 52 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index d128190c5..17ad78986 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -775,16 +775,35 @@ ngx_http_quic_handshake(ngx_event_t *rev) // initial secret - size_t is_len; - uint8_t is[SHA256_DIGEST_LENGTH]; - const EVP_MD *digest; + size_t is_len; + uint8_t is[SHA256_DIGEST_LENGTH]; + const EVP_MD *digest; +#ifdef OPENSSL_IS_BORINGSSL + const EVP_AEAD *cipher; +#else + const EVP_CIPHER *cipher; +#endif static const uint8_t salt[20] = "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"; digest = EVP_sha256(); - HKDF_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, salt, - sizeof(salt)); + + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ + +#ifdef OPENSSL_IS_BORINGSSL + cipher = EVP_aead_aes_128_gcm(); +#else + cipher = EVP_aes_128_gcm(); +#endif + + if (ngx_hkdf_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, + salt, sizeof(salt)) + != NGX_OK) + { + ngx_http_close_connection(c); + return; + } #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { @@ -812,6 +831,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) } hkdfl_len = 2 + 1 + sizeof("tls13 client in") - 1 + 1; + bzero(hkdfl, sizeof(hkdfl)); hkdfl[0] = 0; hkdfl[1] = qc->client_in.len; hkdfl[2] = sizeof("tls13 client in") - 1; @@ -819,25 +839,39 @@ ngx_http_quic_handshake(ngx_event_t *rev) sizeof("tls13 client in") - 1); *p = '\0'; - if (HKDF_expand(qc->client_in.data, qc->client_in.len, - digest, is, is_len, hkdfl, hkdfl_len) - == 0) +#if 0 + ngx_memcpy(hkdfl, "\x00\x20\x0f\x74\x6c\x73\x31\x33\x20\x63\x6c\x69\x65\x6e\x74\x20\x69\x6e\x00\x00", 20); + + m = ngx_hex_dump(buf, hkdfl, sizeof(hkdfl)) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic initial secret hkdf: %*s, len: %uz", + m, buf, sizeof(hkdfl)); +#endif + + if (ngx_hkdf_expand(qc->client_in.data, qc->client_in.len, + digest, is, is_len, hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(client_in) failed"); + "ngx_hkdf_expand(client_in) failed"); ngx_http_close_connection(c); return; } +#ifdef OPENSSL_IS_BORINGSSL ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic EVP key:%d tag:%d nonce:%d", - EVP_AEAD_key_length(EVP_aead_aes_128_gcm()), - EVP_AEAD_max_tag_len(EVP_aead_aes_128_gcm()), - EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm())); + EVP_AEAD_key_length(cipher), + EVP_AEAD_max_tag_len(cipher), + EVP_AEAD_nonce_length(cipher)); +#endif - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ - qc->client_in_key.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->client_in_key.len = EVP_AEAD_key_length(cipher); +#else + qc->client_in_key.len = EVP_CIPHER_key_length(cipher); +#endif qc->client_in_key.data = ngx_pnalloc(c->pool, qc->client_in_key.len); if (qc->client_in_key.data == NULL) { ngx_http_close_connection(c); @@ -851,18 +885,22 @@ ngx_http_quic_handshake(ngx_event_t *rev) sizeof("tls13 quic key") - 1); *p = '\0'; - if (HKDF_expand(qc->client_in_key.data, qc->client_in_key.len, - digest, qc->client_in.data, qc->client_in.len, - hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->client_in_key.data, qc->client_in_key.len, + digest, qc->client_in.data, qc->client_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(client_in_key) failed"); + "ngx_hkdf_expand(client_in_key) failed"); ngx_http_close_connection(c); return; } - qc->client_in_iv.len = EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->client_in_iv.len = EVP_AEAD_nonce_length(cipher); +#else + qc->client_in_iv.len = EVP_CIPHER_iv_length(cipher); +#endif qc->client_in_iv.data = ngx_pnalloc(c->pool, qc->client_in_iv.len); if (qc->client_in_iv.data == NULL) { ngx_http_close_connection(c); @@ -875,19 +913,24 @@ ngx_http_quic_handshake(ngx_event_t *rev) p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); *p = '\0'; - if (HKDF_expand(qc->client_in_iv.data, qc->client_in_iv.len, digest, - qc->client_in.data, qc->client_in.len, hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->client_in_iv.data, qc->client_in_iv.len, + digest, qc->client_in.data, qc->client_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(client_in_iv) failed"); + "ngx_hkdf_expand(client_in_iv) failed"); ngx_http_close_connection(c); return; } /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ - qc->client_in_hp.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->client_in_hp.len = EVP_AEAD_key_length(cipher); +#else + qc->client_in_hp.len = EVP_CIPHER_key_length(cipher); +#endif qc->client_in_hp.data = ngx_pnalloc(c->pool, qc->client_in_hp.len); if (qc->client_in_hp.data == NULL) { ngx_http_close_connection(c); @@ -900,12 +943,13 @@ ngx_http_quic_handshake(ngx_event_t *rev) p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); *p = '\0'; - if (HKDF_expand(qc->client_in_hp.data, qc->client_in_hp.len, digest, - qc->client_in.data, qc->client_in.len, hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->client_in_hp.data, qc->client_in_hp.len, + digest, qc->client_in.data, qc->client_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(client_in_hp) failed"); + "ngx_hkdf_expand(client_in_hp) failed"); ngx_http_close_connection(c); return; } @@ -956,19 +1000,23 @@ ngx_http_quic_handshake(ngx_event_t *rev) sizeof("tls13 server in") - 1); *p = '\0'; - if (HKDF_expand(qc->server_in.data, qc->server_in.len, - digest, is, is_len, hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->server_in.data, qc->server_in.len, + digest, is, is_len, hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(server_in) failed"); + "ngx_hkdf_expand(server_in) failed"); ngx_http_close_connection(c); return; } /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ - qc->server_in_key.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->server_in_key.len = EVP_AEAD_key_length(cipher); +#else + qc->server_in_key.len = EVP_CIPHER_key_length(cipher); +#endif qc->server_in_key.data = ngx_pnalloc(c->pool, qc->server_in_key.len); if (qc->server_in_key.data == NULL) { ngx_http_close_connection(c); @@ -982,18 +1030,22 @@ ngx_http_quic_handshake(ngx_event_t *rev) sizeof("tls13 quic key") - 1); *p = '\0'; - if (HKDF_expand(qc->server_in_key.data, qc->server_in_key.len, - digest, qc->server_in.data, qc->server_in.len, - hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->server_in_key.data, qc->server_in_key.len, + digest, qc->server_in.data, qc->server_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(server_in_key) failed"); + "ngx_hkdf_expand(server_in_key) failed"); ngx_http_close_connection(c); return; } - qc->server_in_iv.len = EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->server_in_iv.len = EVP_AEAD_nonce_length(cipher); +#else + qc->server_in_iv.len = EVP_CIPHER_iv_length(cipher); +#endif qc->server_in_iv.data = ngx_pnalloc(c->pool, qc->server_in_iv.len); if (qc->server_in_iv.data == NULL) { ngx_http_close_connection(c); @@ -1006,19 +1058,24 @@ ngx_http_quic_handshake(ngx_event_t *rev) p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); *p = '\0'; - if (HKDF_expand(qc->server_in_iv.data, qc->server_in_iv.len, digest, - qc->server_in.data, qc->server_in.len, hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->server_in_iv.data, qc->server_in_iv.len, + digest, qc->server_in.data, qc->server_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(server_in_iv) failed"); + "ngx_hkdf_expand(server_in_iv) failed"); ngx_http_close_connection(c); return; } /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ - qc->server_in_hp.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->server_in_hp.len = EVP_AEAD_key_length(cipher); +#else + qc->server_in_hp.len = EVP_CIPHER_key_length(cipher); +#endif qc->server_in_hp.data = ngx_pnalloc(c->pool, qc->server_in_hp.len); if (qc->server_in_hp.data == NULL) { ngx_http_close_connection(c); @@ -1031,12 +1088,13 @@ ngx_http_quic_handshake(ngx_event_t *rev) p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); *p = '\0'; - if (HKDF_expand(qc->server_in_hp.data, qc->server_in_hp.len, digest, - qc->server_in.data, qc->server_in.len, hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->server_in_hp.data, qc->server_in_hp.len, + digest, qc->server_in.data, qc->server_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(server_in_hp) failed"); + "ngx_hkdf_expand(server_in_hp) failed"); ngx_http_close_connection(c); return; } @@ -1147,12 +1205,20 @@ ngx_http_quic_handshake(ngx_event_t *rev) } #endif - EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(EVP_aead_aes_128_gcm(), + uint8_t cleartext[1600]; + size_t cleartext_len; + +#ifdef OPENSSL_IS_BORINGSSL + EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(cipher, qc->client_in_key.data, qc->client_in_key.len, EVP_AEAD_DEFAULT_TAG_LENGTH); - uint8_t cleartext[1600]; - size_t cleartext_len = sizeof(cleartext); + if (aead == NULL) { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_AEAD_CTX_new() failed"); + ngx_http_close_connection(c); + return; + } if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext), nonce, qc->client_in_iv.len, ciphertext.data, @@ -1167,6 +1233,87 @@ ngx_http_quic_handshake(ngx_event_t *rev) } EVP_AEAD_CTX_free(aead); +#else + int len; + u_char *tag; + EVP_CIPHER_CTX *aead; + + aead = EVP_CIPHER_CTX_new(); + if (aead == NULL) { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_CIPHER_CTX_new() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptInit_ex(aead, cipher, NULL, NULL, NULL) != 1) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, qc->client_in_iv.len, + NULL) + == 0) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptInit_ex(aead, NULL, NULL, qc->client_in_key.data, nonce) + != 1) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptUpdate(aead, NULL, &len, ad.data, ad.len) != 1) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptUpdate(aead, cleartext, &len, ciphertext.data, + ciphertext.len - EVP_GCM_TLS_TAG_LEN) + != 1) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); + ngx_http_close_connection(c); + return; + } + + cleartext_len = len; + tag = ciphertext.data + ciphertext.len - EVP_GCM_TLS_TAG_LEN; + + if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, + tag) + == 0) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_TAG) failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptFinal_ex(aead, cleartext + len, &len) <= 0) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptFinal_ex failed"); + ngx_http_close_connection(c); + return; + } + + cleartext_len += len; + + EVP_CIPHER_CTX_free(aead); +#endif #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { -- cgit From 0ddf4a2e67beb96a8de26e3a00082ebc98987902 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:52 +0300 Subject: PN-aware AEAD nonce, feeding proper CRYPTO length. --- src/http/ngx_http_request.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 17ad78986..fd30bf3d9 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1324,6 +1324,26 @@ ngx_http_quic_handshake(ngx_event_t *rev) } #endif + if (cleartext[0] != 0x06) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, + "unexpected frame in initial packet"); + ngx_http_close_connection(c); + return; + } + + if (cleartext[1] != 0x00) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, + "unexpected CRYPTO offset in initial packet"); + ngx_http_close_connection(c); + return; + } + + uint8_t *crypto = &cleartext[2]; + uint64_t crypto_len = ngx_quic_parse_int(&crypto); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic initial packet CRYPTO length: %uL pp:%p:%p", crypto_len, cleartext, crypto); + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) @@ -1351,7 +1371,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) if (!SSL_provide_quic_data(c->ssl->connection, SSL_quic_read_level(c->ssl->connection), - &cleartext[4], cleartext_len - 4)) + crypto, crypto_len)) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "SSL_provide_quic_data() failed"); -- cgit From 56a80c228a7f4211e507ce48edf023f907821f1e Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:52 +0300 Subject: Fixed indentation. --- src/http/ngx_http_request.c | 395 ++++++++++++++++++++++---------------------- 1 file changed, 196 insertions(+), 199 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index fd30bf3d9..389d7488f 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -817,46 +817,46 @@ ngx_http_quic_handshake(ngx_event_t *rev) } #endif - size_t hkdfl_len; - uint8_t hkdfl[20]; - uint8_t *p; + size_t hkdfl_len; + uint8_t hkdfl[20]; + uint8_t *p; - /* draft-ietf-quic-tls-23#section-5.2 */ + /* draft-ietf-quic-tls-23#section-5.2 */ - qc->client_in.len = SHA256_DIGEST_LENGTH; - qc->client_in.data = ngx_pnalloc(c->pool, qc->client_in.len); - if (qc->client_in.data == NULL) { - ngx_http_close_connection(c); - return; - } + qc->client_in.len = SHA256_DIGEST_LENGTH; + qc->client_in.data = ngx_pnalloc(c->pool, qc->client_in.len); + if (qc->client_in.data == NULL) { + ngx_http_close_connection(c); + return; + } - hkdfl_len = 2 + 1 + sizeof("tls13 client in") - 1 + 1; - bzero(hkdfl, sizeof(hkdfl)); - hkdfl[0] = 0; - hkdfl[1] = qc->client_in.len; - hkdfl[2] = sizeof("tls13 client in") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 client in", - sizeof("tls13 client in") - 1); - *p = '\0'; + hkdfl_len = 2 + 1 + sizeof("tls13 client in") - 1 + 1; + bzero(hkdfl, sizeof(hkdfl)); + hkdfl[0] = 0; + hkdfl[1] = qc->client_in.len; + hkdfl[2] = sizeof("tls13 client in") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 client in", sizeof("tls13 client in") - 1); + *p = '\0'; #if 0 - ngx_memcpy(hkdfl, "\x00\x20\x0f\x74\x6c\x73\x31\x33\x20\x63\x6c\x69\x65\x6e\x74\x20\x69\x6e\x00\x00", 20); + ngx_memcpy(hkdfl, "\x00\x20\x0f\x74\x6c\x73\x31\x33\x20\x63" + "\x6c\x69\x65\x6e\x74\x20\x69\x6e\x00\x00", 20); - m = ngx_hex_dump(buf, hkdfl, sizeof(hkdfl)) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic initial secret hkdf: %*s, len: %uz", - m, buf, sizeof(hkdfl)); + m = ngx_hex_dump(buf, hkdfl, sizeof(hkdfl)) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic initial secret hkdf: %*s, len: %uz", + m, buf, sizeof(hkdfl)); #endif - if (ngx_hkdf_expand(qc->client_in.data, qc->client_in.len, - digest, is, is_len, hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in) failed"); - ngx_http_close_connection(c); - return; - } + if (ngx_hkdf_expand(qc->client_in.data, qc->client_in.len, + digest, is, is_len, hkdfl, hkdfl_len) + != NGX_OK) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "ngx_hkdf_expand(client_in) failed"); + ngx_http_close_connection(c); + return; + } #ifdef OPENSSL_IS_BORINGSSL ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, @@ -868,91 +868,90 @@ ngx_http_quic_handshake(ngx_event_t *rev) #ifdef OPENSSL_IS_BORINGSSL - qc->client_in_key.len = EVP_AEAD_key_length(cipher); + qc->client_in_key.len = EVP_AEAD_key_length(cipher); #else - qc->client_in_key.len = EVP_CIPHER_key_length(cipher); + qc->client_in_key.len = EVP_CIPHER_key_length(cipher); #endif - qc->client_in_key.data = ngx_pnalloc(c->pool, qc->client_in_key.len); - if (qc->client_in_key.data == NULL) { - ngx_http_close_connection(c); - return; - } + qc->client_in_key.data = ngx_pnalloc(c->pool, qc->client_in_key.len); + if (qc->client_in_key.data == NULL) { + ngx_http_close_connection(c); + return; + } - hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1; - hkdfl[1] = qc->client_in_key.len; - hkdfl[2] = sizeof("tls13 quic key") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic key", - sizeof("tls13 quic key") - 1); - *p = '\0'; + hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1; + hkdfl[1] = qc->client_in_key.len; + hkdfl[2] = sizeof("tls13 quic key") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic key", sizeof("tls13 quic key") - 1); + *p = '\0'; - if (ngx_hkdf_expand(qc->client_in_key.data, qc->client_in_key.len, - digest, qc->client_in.data, qc->client_in.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in_key) failed"); - ngx_http_close_connection(c); - return; - } + if (ngx_hkdf_expand(qc->client_in_key.data, qc->client_in_key.len, + digest, qc->client_in.data, qc->client_in.len, + hkdfl, hkdfl_len) + != NGX_OK) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "ngx_hkdf_expand(client_in_key) failed"); + ngx_http_close_connection(c); + return; + } #ifdef OPENSSL_IS_BORINGSSL - qc->client_in_iv.len = EVP_AEAD_nonce_length(cipher); + qc->client_in_iv.len = EVP_AEAD_nonce_length(cipher); #else - qc->client_in_iv.len = EVP_CIPHER_iv_length(cipher); + qc->client_in_iv.len = EVP_CIPHER_iv_length(cipher); #endif - qc->client_in_iv.data = ngx_pnalloc(c->pool, qc->client_in_iv.len); - if (qc->client_in_iv.data == NULL) { - ngx_http_close_connection(c); - return; - } + qc->client_in_iv.data = ngx_pnalloc(c->pool, qc->client_in_iv.len); + if (qc->client_in_iv.data == NULL) { + ngx_http_close_connection(c); + return; + } - hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1; - hkdfl[1] = qc->client_in_iv.len; - hkdfl[2] = sizeof("tls13 quic iv") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); - *p = '\0'; + hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1; + hkdfl[1] = qc->client_in_iv.len; + hkdfl[2] = sizeof("tls13 quic iv") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); + *p = '\0'; - if (ngx_hkdf_expand(qc->client_in_iv.data, qc->client_in_iv.len, - digest, qc->client_in.data, qc->client_in.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in_iv) failed"); - ngx_http_close_connection(c); - return; - } + if (ngx_hkdf_expand(qc->client_in_iv.data, qc->client_in_iv.len, + digest, qc->client_in.data, qc->client_in.len, + hkdfl, hkdfl_len) + != NGX_OK) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "ngx_hkdf_expand(client_in_iv) failed"); + ngx_http_close_connection(c); + return; + } - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ #ifdef OPENSSL_IS_BORINGSSL - qc->client_in_hp.len = EVP_AEAD_key_length(cipher); + qc->client_in_hp.len = EVP_AEAD_key_length(cipher); #else - qc->client_in_hp.len = EVP_CIPHER_key_length(cipher); + qc->client_in_hp.len = EVP_CIPHER_key_length(cipher); #endif - qc->client_in_hp.data = ngx_pnalloc(c->pool, qc->client_in_hp.len); - if (qc->client_in_hp.data == NULL) { - ngx_http_close_connection(c); - return; - } + qc->client_in_hp.data = ngx_pnalloc(c->pool, qc->client_in_hp.len); + if (qc->client_in_hp.data == NULL) { + ngx_http_close_connection(c); + return; + } - hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1; - hkdfl[1] = qc->client_in_hp.len; - hkdfl[2] = sizeof("tls13 quic hp") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); - *p = '\0'; + hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1; + hkdfl[1] = qc->client_in_hp.len; + hkdfl[2] = sizeof("tls13 quic hp") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); + *p = '\0'; - if (ngx_hkdf_expand(qc->client_in_hp.data, qc->client_in_hp.len, - digest, qc->client_in.data, qc->client_in.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in_hp) failed"); - ngx_http_close_connection(c); - return; - } + if (ngx_hkdf_expand(qc->client_in_hp.data, qc->client_in_hp.len, + digest, qc->client_in.data, qc->client_in.len, + hkdfl, hkdfl_len) + != NGX_OK) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "ngx_hkdf_expand(client_in_hp) failed"); + ngx_http_close_connection(c); + return; + } #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { @@ -983,121 +982,119 @@ ngx_http_quic_handshake(ngx_event_t *rev) // server initial - /* draft-ietf-quic-tls-23#section-5.2 */ + /* draft-ietf-quic-tls-23#section-5.2 */ - qc->server_in.len = SHA256_DIGEST_LENGTH; - qc->server_in.data = ngx_pnalloc(c->pool, qc->server_in.len); - if (qc->server_in.data == NULL) { - ngx_http_close_connection(c); - return; - } + qc->server_in.len = SHA256_DIGEST_LENGTH; + qc->server_in.data = ngx_pnalloc(c->pool, qc->server_in.len); + if (qc->server_in.data == NULL) { + ngx_http_close_connection(c); + return; + } - hkdfl_len = 2 + 1 + sizeof("tls13 server in") - 1 + 1; - hkdfl[0] = 0; - hkdfl[1] = qc->server_in.len; - hkdfl[2] = sizeof("tls13 server in") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 server in", - sizeof("tls13 server in") - 1); - *p = '\0'; + hkdfl_len = 2 + 1 + sizeof("tls13 server in") - 1 + 1; + hkdfl[0] = 0; + hkdfl[1] = qc->server_in.len; + hkdfl[2] = sizeof("tls13 server in") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 server in", sizeof("tls13 server in") - 1); + *p = '\0'; - if (ngx_hkdf_expand(qc->server_in.data, qc->server_in.len, - digest, is, is_len, hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in) failed"); - ngx_http_close_connection(c); - return; - } + if (ngx_hkdf_expand(qc->server_in.data, qc->server_in.len, + digest, is, is_len, hkdfl, hkdfl_len) + != NGX_OK) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "ngx_hkdf_expand(server_in) failed"); + ngx_http_close_connection(c); + return; + } - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ #ifdef OPENSSL_IS_BORINGSSL - qc->server_in_key.len = EVP_AEAD_key_length(cipher); + qc->server_in_key.len = EVP_AEAD_key_length(cipher); #else - qc->server_in_key.len = EVP_CIPHER_key_length(cipher); + qc->server_in_key.len = EVP_CIPHER_key_length(cipher); #endif - qc->server_in_key.data = ngx_pnalloc(c->pool, qc->server_in_key.len); - if (qc->server_in_key.data == NULL) { - ngx_http_close_connection(c); - return; - } + qc->server_in_key.data = ngx_pnalloc(c->pool, qc->server_in_key.len); + if (qc->server_in_key.data == NULL) { + ngx_http_close_connection(c); + return; + } - hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1; - hkdfl[1] = qc->server_in_key.len; - hkdfl[2] = sizeof("tls13 quic key") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic key", - sizeof("tls13 quic key") - 1); - *p = '\0'; + hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1; + hkdfl[1] = qc->server_in_key.len; + hkdfl[2] = sizeof("tls13 quic key") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic key", sizeof("tls13 quic key") - 1); + *p = '\0'; - if (ngx_hkdf_expand(qc->server_in_key.data, qc->server_in_key.len, - digest, qc->server_in.data, qc->server_in.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in_key) failed"); - ngx_http_close_connection(c); - return; - } + if (ngx_hkdf_expand(qc->server_in_key.data, qc->server_in_key.len, + digest, qc->server_in.data, qc->server_in.len, + hkdfl, hkdfl_len) + != NGX_OK) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "ngx_hkdf_expand(server_in_key) failed"); + ngx_http_close_connection(c); + return; + } #ifdef OPENSSL_IS_BORINGSSL - qc->server_in_iv.len = EVP_AEAD_nonce_length(cipher); + qc->server_in_iv.len = EVP_AEAD_nonce_length(cipher); #else - qc->server_in_iv.len = EVP_CIPHER_iv_length(cipher); + qc->server_in_iv.len = EVP_CIPHER_iv_length(cipher); #endif - qc->server_in_iv.data = ngx_pnalloc(c->pool, qc->server_in_iv.len); - if (qc->server_in_iv.data == NULL) { - ngx_http_close_connection(c); - return; - } + qc->server_in_iv.data = ngx_pnalloc(c->pool, qc->server_in_iv.len); + if (qc->server_in_iv.data == NULL) { + ngx_http_close_connection(c); + return; + } - hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1; - hkdfl[1] = qc->server_in_iv.len; - hkdfl[2] = sizeof("tls13 quic iv") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); - *p = '\0'; + hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1; + hkdfl[1] = qc->server_in_iv.len; + hkdfl[2] = sizeof("tls13 quic iv") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); + *p = '\0'; - if (ngx_hkdf_expand(qc->server_in_iv.data, qc->server_in_iv.len, - digest, qc->server_in.data, qc->server_in.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in_iv) failed"); - ngx_http_close_connection(c); - return; - } + if (ngx_hkdf_expand(qc->server_in_iv.data, qc->server_in_iv.len, + digest, qc->server_in.data, qc->server_in.len, + hkdfl, hkdfl_len) + != NGX_OK) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "ngx_hkdf_expand(server_in_iv) failed"); + ngx_http_close_connection(c); + return; + } - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ #ifdef OPENSSL_IS_BORINGSSL - qc->server_in_hp.len = EVP_AEAD_key_length(cipher); + qc->server_in_hp.len = EVP_AEAD_key_length(cipher); #else - qc->server_in_hp.len = EVP_CIPHER_key_length(cipher); + qc->server_in_hp.len = EVP_CIPHER_key_length(cipher); #endif - qc->server_in_hp.data = ngx_pnalloc(c->pool, qc->server_in_hp.len); - if (qc->server_in_hp.data == NULL) { - ngx_http_close_connection(c); - return; - } + qc->server_in_hp.data = ngx_pnalloc(c->pool, qc->server_in_hp.len); + if (qc->server_in_hp.data == NULL) { + ngx_http_close_connection(c); + return; + } - hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1; - hkdfl[1] = qc->server_in_hp.len; - hkdfl[2] = sizeof("tls13 quic hp") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); - *p = '\0'; + hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1; + hkdfl[1] = qc->server_in_hp.len; + hkdfl[2] = sizeof("tls13 quic hp") - 1; + p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); + *p = '\0'; - if (ngx_hkdf_expand(qc->server_in_hp.data, qc->server_in_hp.len, - digest, qc->server_in.data, qc->server_in.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in_hp) failed"); - ngx_http_close_connection(c); - return; - } + if (ngx_hkdf_expand(qc->server_in_hp.data, qc->server_in_hp.len, + digest, qc->server_in.data, qc->server_in.len, + hkdfl, hkdfl_len) + != NGX_OK) + { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "ngx_hkdf_expand(server_in_hp) failed"); + ngx_http_close_connection(c); + return; + } #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { @@ -1214,8 +1211,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) qc->client_in_key.len, EVP_AEAD_DEFAULT_TAG_LENGTH); if (aead == NULL) { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_AEAD_CTX_new() failed"); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_AEAD_CTX_new() failed"); ngx_http_close_connection(c); return; } @@ -1342,7 +1338,8 @@ ngx_http_quic_handshake(ngx_event_t *rev) uint64_t crypto_len = ngx_quic_parse_int(&crypto); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic initial packet CRYPTO length: %uL pp:%p:%p", crypto_len, cleartext, crypto); + "quic initial packet CRYPTO length: %uL pp:%p:%p", + crypto_len, cleartext, crypto); sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); @@ -1373,10 +1370,10 @@ ngx_http_quic_handshake(ngx_event_t *rev) SSL_quic_read_level(c->ssl->connection), crypto, crypto_len)) { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "SSL_provide_quic_data() failed"); - ngx_http_close_connection(c); - return; + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "SSL_provide_quic_data() failed"); + ngx_http_close_connection(c); + return; } n = SSL_do_handshake(c->ssl->connection); -- cgit From aba1768d94a834dac08c8252576ca5279d4108ef Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:52 +0300 Subject: QUIC handshake handler, draft 24 bump. --- src/http/ngx_http_request.c | 356 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 353 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 389d7488f..b8bca5547 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -64,6 +64,7 @@ static void ngx_http_ssl_handshake(ngx_event_t *rev); static void ngx_http_ssl_handshake_handler(ngx_connection_t *c); static void ngx_http_quic_handshake(ngx_event_t *rev); +static void ngx_http_quic_handshake_handler(ngx_event_t *rev); #endif @@ -706,7 +707,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic flags:%xi version:%xD", flags, version); - if (version != 0xff000017) { + if (version != 0xff000018) { ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); ngx_http_close_connection(c); return; @@ -742,7 +743,14 @@ ngx_http_quic_handshake(ngx_event_t *rev) ngx_memcpy(qc->token.data, b->pos, qc->token.len); b->pos += qc->token.len; - uint64_t plen = ngx_quic_parse_int(&b->pos); + ngx_int_t plen = ngx_quic_parse_int(&b->pos); + + if (plen > b->last - b->pos) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "truncated initial packet"); + ngx_http_close_connection(c); + return; + } + /* draft-ietf-quic-tls-23#section-5.4.2: * the Packet Number field is assumed to be 4 bytes long * draft-ietf-quic-tls-23#section-5.4.[34]: @@ -1396,11 +1404,353 @@ ngx_http_quic_handshake(ngx_event_t *rev) (int) SSL_quic_read_level(c->ssl->connection), (int) SSL_quic_write_level(c->ssl->connection)); - ngx_http_close_connection(c); + if (!rev->timer_set) { + ngx_add_timer(rev, c->listening->post_accept_timeout); + } + + rev->handler = ngx_http_quic_handshake_handler; return; } +static void +ngx_http_quic_handshake_handler(ngx_event_t *rev) +{ + size_t m; + ssize_t n; + ngx_connection_t *c; + ngx_quic_connection_t *qc; + u_char buf[4096], b[512], *p; + + c = rev->data; + qc = c->quic; + p = b; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_http_close_connection(c); + return; + } + + if (c->close) { + ngx_http_close_connection(c); + return; + } + + n = c->recv(c, b, sizeof(b)); + + if (n == NGX_AGAIN) { + return; + } + + if (n == NGX_ERROR) { + c->read->eof = 1; + ngx_http_close_connection(c); + return; + } + + m = ngx_hex_dump(buf, b, n) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic handshake handler: %*s, len: %uz", m, buf, n); + + /* XXX bug-for-bug compat - assuming initial ack in handshake pkt */ + + if ((p[0] & 0xf0) != 0xe0) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid packet type"); + ngx_http_close_connection(c); + return; + } + + ngx_int_t flags = *p++; + uint32_t version = ngx_http_v2_parse_uint32(p); + p += 4; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic flags:%xi version:%xD", flags, version); + + if (version != 0xff000018) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); + ngx_http_close_connection(c); + return; + } + + if (*p++ != qc->dcid.len) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic dcidl"); + ngx_http_close_connection(c); + return; + } + + if (ngx_memcmp(p, qc->dcid.data, qc->dcid.len) != 0) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic dcid"); + ngx_http_close_connection(c); + return; + } + + p += qc->dcid.len; + + if (*p++ != qc->scid.len) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic scidl"); + ngx_http_close_connection(c); + return; + } + + if (ngx_memcmp(p, qc->scid.data, qc->scid.len) != 0) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic scid"); + ngx_http_close_connection(c); + return; + } + + p += qc->scid.len; + + ngx_int_t plen = ngx_quic_parse_int(&p); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic packet length: %d", plen); + + if (plen > b + n - p) { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, "truncated handshake packet"); + ngx_http_close_connection(c); + return; + } + + u_char *sample = p + 4; + + m = ngx_hex_dump(buf, sample, 16) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic sample: %*s", m, buf); + +// header protection + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + uint8_t mask[16]; + int outlen; + + if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, + qc->client_hs_hp.data, NULL) + != 1) + { + EVP_CIPHER_CTX_free(ctx); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_EncryptInit_ex() failed"); + ngx_http_close_connection(c); + return; + } + + if (!EVP_EncryptUpdate(ctx, mask, &outlen, sample, 16)) { + EVP_CIPHER_CTX_free(ctx); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_EncryptUpdate() failed"); + ngx_http_close_connection(c); + return; + } + + EVP_CIPHER_CTX_free(ctx); + + u_char clearflags = flags ^ (mask[0] & 0x0f); + ngx_int_t pnl = (clearflags & 0x03) + 1; + uint64_t pn = ngx_quic_parse_pn(&p, pnl, &mask[1]); + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, mask, 5) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic mask: %*s", m, buf); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic clear flags: %xi", clearflags); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic packet number: %uL, len: %xi", pn, pnl); + } +#endif + +// packet protection + + ngx_str_t ciphertext; + ciphertext.data = p; + ciphertext.len = plen - pnl; + + ngx_str_t ad; + ad.len = p - b; + ad.data = ngx_pnalloc(c->pool, ad.len); + if (ad.data == NULL) { + ngx_http_close_connection(c); + return; + } + + ngx_memcpy(ad.data, b, ad.len); + ad.data[0] = clearflags; + ad.data[ad.len - pnl] = (u_char)pn; + + uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_hs_iv); + nonce[11] ^= pn; + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, nonce, 12) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic nonce: %*s, len: %uz", m, buf, 12); + + m = ngx_hex_dump(buf, ad.data, ad.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic ad: %*s, len: %uz", m, buf, ad.len); + } +#endif + +#ifdef OPENSSL_IS_BORINGSSL + const EVP_AEAD *cipher; +#else + const EVP_CIPHER *cipher; +#endif + + u_char *name = (u_char *) SSL_get_cipher(c->ssl->connection); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic ssl cipher: %s", name); + + if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 + || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) + { +#ifdef OPENSSL_IS_BORINGSSL + cipher = EVP_aead_aes_128_gcm(); +#else + cipher = EVP_aes_128_gcm(); +#endif + + } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { +#ifdef OPENSSL_IS_BORINGSSL + cipher = EVP_aead_aes_256_gcm(); +#else + cipher = EVP_aes_256_gcm(); +#endif + + } else { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "unexpected cipher"); + ngx_http_close_connection(c); + return; + } + + + uint8_t cleartext[1600]; + size_t cleartext_len; + +#ifdef OPENSSL_IS_BORINGSSL + EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(cipher, + qc->client_hs_key.data, + qc->client_hs_key.len, + EVP_AEAD_DEFAULT_TAG_LENGTH); + if (aead == NULL) { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_AEAD_CTX_new() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext), + nonce, qc->client_hs_iv.len, ciphertext.data, + ciphertext.len, ad.data, ad.len) + != 1) + { + EVP_AEAD_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_AEAD_CTX_open() failed"); + ngx_http_close_connection(c); + return; + } + + EVP_AEAD_CTX_free(aead); +#else + int len; + u_char *tag; + EVP_CIPHER_CTX *aead; + + aead = EVP_CIPHER_CTX_new(); + if (aead == NULL) { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_CIPHER_CTX_new() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptInit_ex(aead, cipher, NULL, NULL, NULL) != 1) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, qc->client_hs_iv.len, + NULL) + == 0) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptInit_ex(aead, NULL, NULL, qc->client_hs_key.data, nonce) + != 1) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptUpdate(aead, NULL, &len, ad.data, ad.len) != 1) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptUpdate(aead, cleartext, &len, ciphertext.data, + ciphertext.len - EVP_GCM_TLS_TAG_LEN) + != 1) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); + ngx_http_close_connection(c); + return; + } + + cleartext_len = len; + tag = ciphertext.data + ciphertext.len - EVP_GCM_TLS_TAG_LEN; + + if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, + tag) + == 0) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_TAG) failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptFinal_ex(aead, cleartext + len, &len) <= 0) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptFinal_ex failed"); + ngx_http_close_connection(c); + return; + } + + cleartext_len += len; + + EVP_CIPHER_CTX_free(aead); +#endif + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, cleartext, ngx_min(cleartext_len, 256)) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic packet payload: %*s%s, len: %uz", + m, buf, m < 512 ? "" : "...", cleartext_len); + } +#endif + + ngx_http_close_connection(c); +} + + static void ngx_http_ssl_handshake(ngx_event_t *rev) { -- cgit From 27e5e87784f1464e9c40f31e8c119918073fb90b Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:52 +0300 Subject: Introduced ngx_quic_secret_t. --- src/http/ngx_http_request.c | 176 ++++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 88 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index b8bca5547..7f62643c1 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -831,9 +831,9 @@ ngx_http_quic_handshake(ngx_event_t *rev) /* draft-ietf-quic-tls-23#section-5.2 */ - qc->client_in.len = SHA256_DIGEST_LENGTH; - qc->client_in.data = ngx_pnalloc(c->pool, qc->client_in.len); - if (qc->client_in.data == NULL) { + qc->client_in.secret.len = SHA256_DIGEST_LENGTH; + qc->client_in.secret.data = ngx_pnalloc(c->pool, qc->client_in.secret.len); + if (qc->client_in.secret.data == NULL) { ngx_http_close_connection(c); return; } @@ -841,7 +841,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) hkdfl_len = 2 + 1 + sizeof("tls13 client in") - 1 + 1; bzero(hkdfl, sizeof(hkdfl)); hkdfl[0] = 0; - hkdfl[1] = qc->client_in.len; + hkdfl[1] = qc->client_in.secret.len; hkdfl[2] = sizeof("tls13 client in") - 1; p = ngx_cpymem(&hkdfl[3], "tls13 client in", sizeof("tls13 client in") - 1); *p = '\0'; @@ -856,7 +856,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) m, buf, sizeof(hkdfl)); #endif - if (ngx_hkdf_expand(qc->client_in.data, qc->client_in.len, + if (ngx_hkdf_expand(qc->client_in.secret.data, qc->client_in.secret.len, digest, is, is_len, hkdfl, hkdfl_len) != NGX_OK) { @@ -876,57 +876,57 @@ ngx_http_quic_handshake(ngx_event_t *rev) #ifdef OPENSSL_IS_BORINGSSL - qc->client_in_key.len = EVP_AEAD_key_length(cipher); + qc->client_in.key.len = EVP_AEAD_key_length(cipher); #else - qc->client_in_key.len = EVP_CIPHER_key_length(cipher); + qc->client_in.key.len = EVP_CIPHER_key_length(cipher); #endif - qc->client_in_key.data = ngx_pnalloc(c->pool, qc->client_in_key.len); - if (qc->client_in_key.data == NULL) { + qc->client_in.key.data = ngx_pnalloc(c->pool, qc->client_in.key.len); + if (qc->client_in.key.data == NULL) { ngx_http_close_connection(c); return; } hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1; - hkdfl[1] = qc->client_in_key.len; + hkdfl[1] = qc->client_in.key.len; hkdfl[2] = sizeof("tls13 quic key") - 1; p = ngx_cpymem(&hkdfl[3], "tls13 quic key", sizeof("tls13 quic key") - 1); *p = '\0'; - if (ngx_hkdf_expand(qc->client_in_key.data, qc->client_in_key.len, - digest, qc->client_in.data, qc->client_in.len, + if (ngx_hkdf_expand(qc->client_in.key.data, qc->client_in.key.len, + digest, qc->client_in.secret.data, qc->client_in.secret.len, hkdfl, hkdfl_len) != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in_key) failed"); + "ngx_hkdf_expand(client_in.key) failed"); ngx_http_close_connection(c); return; } #ifdef OPENSSL_IS_BORINGSSL - qc->client_in_iv.len = EVP_AEAD_nonce_length(cipher); + qc->client_in.iv.len = EVP_AEAD_nonce_length(cipher); #else - qc->client_in_iv.len = EVP_CIPHER_iv_length(cipher); + qc->client_in.iv.len = EVP_CIPHER_iv_length(cipher); #endif - qc->client_in_iv.data = ngx_pnalloc(c->pool, qc->client_in_iv.len); - if (qc->client_in_iv.data == NULL) { + qc->client_in.iv.data = ngx_pnalloc(c->pool, qc->client_in.iv.len); + if (qc->client_in.iv.data == NULL) { ngx_http_close_connection(c); return; } hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1; - hkdfl[1] = qc->client_in_iv.len; + hkdfl[1] = qc->client_in.iv.len; hkdfl[2] = sizeof("tls13 quic iv") - 1; p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); *p = '\0'; - if (ngx_hkdf_expand(qc->client_in_iv.data, qc->client_in_iv.len, - digest, qc->client_in.data, qc->client_in.len, + if (ngx_hkdf_expand(qc->client_in.iv.data, qc->client_in.iv.len, + digest, qc->client_in.secret.data, qc->client_in.secret.len, hkdfl, hkdfl_len) != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in_iv) failed"); + "ngx_hkdf_expand(client_in.iv) failed"); ngx_http_close_connection(c); return; } @@ -934,57 +934,57 @@ ngx_http_quic_handshake(ngx_event_t *rev) /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ #ifdef OPENSSL_IS_BORINGSSL - qc->client_in_hp.len = EVP_AEAD_key_length(cipher); + qc->client_in.hp.len = EVP_AEAD_key_length(cipher); #else - qc->client_in_hp.len = EVP_CIPHER_key_length(cipher); + qc->client_in.hp.len = EVP_CIPHER_key_length(cipher); #endif - qc->client_in_hp.data = ngx_pnalloc(c->pool, qc->client_in_hp.len); - if (qc->client_in_hp.data == NULL) { + qc->client_in.hp.data = ngx_pnalloc(c->pool, qc->client_in.hp.len); + if (qc->client_in.hp.data == NULL) { ngx_http_close_connection(c); return; } hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1; - hkdfl[1] = qc->client_in_hp.len; + hkdfl[1] = qc->client_in.hp.len; hkdfl[2] = sizeof("tls13 quic hp") - 1; p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); *p = '\0'; - if (ngx_hkdf_expand(qc->client_in_hp.data, qc->client_in_hp.len, - digest, qc->client_in.data, qc->client_in.len, + if (ngx_hkdf_expand(qc->client_in.hp.data, qc->client_in.hp.len, + digest, qc->client_in.secret.data, qc->client_in.secret.len, hkdfl, hkdfl_len) != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in_hp) failed"); + "ngx_hkdf_expand(client_in.hp) failed"); ngx_http_close_connection(c); return; } #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, qc->client_in.data, qc->client_in.len) - buf; + m = ngx_hex_dump(buf, qc->client_in.secret.data, qc->client_in.secret.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic client initial secret: %*s, len: %uz", - m, buf, qc->client_in.len); + m, buf, qc->client_in.secret.len); - m = ngx_hex_dump(buf, qc->client_in_key.data, qc->client_in_key.len) + m = ngx_hex_dump(buf, qc->client_in.key.data, qc->client_in.key.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic client key: %*s, len: %uz", - m, buf, qc->client_in_key.len); + m, buf, qc->client_in.key.len); - m = ngx_hex_dump(buf, qc->client_in_iv.data, qc->client_in_iv.len) + m = ngx_hex_dump(buf, qc->client_in.iv.data, qc->client_in.iv.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic client iv: %*s, len: %uz", - m, buf, qc->client_in_iv.len); + m, buf, qc->client_in.iv.len); - m = ngx_hex_dump(buf, qc->client_in_hp.data, qc->client_in_hp.len) + m = ngx_hex_dump(buf, qc->client_in.hp.data, qc->client_in.hp.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic client hp: %*s, len: %uz", - m, buf, qc->client_in_hp.len); + m, buf, qc->client_in.hp.len); } #endif @@ -992,21 +992,21 @@ ngx_http_quic_handshake(ngx_event_t *rev) /* draft-ietf-quic-tls-23#section-5.2 */ - qc->server_in.len = SHA256_DIGEST_LENGTH; - qc->server_in.data = ngx_pnalloc(c->pool, qc->server_in.len); - if (qc->server_in.data == NULL) { + qc->server_in.secret.len = SHA256_DIGEST_LENGTH; + qc->server_in.secret.data = ngx_pnalloc(c->pool, qc->server_in.secret.len); + if (qc->server_in.secret.data == NULL) { ngx_http_close_connection(c); return; } hkdfl_len = 2 + 1 + sizeof("tls13 server in") - 1 + 1; hkdfl[0] = 0; - hkdfl[1] = qc->server_in.len; + hkdfl[1] = qc->server_in.secret.len; hkdfl[2] = sizeof("tls13 server in") - 1; p = ngx_cpymem(&hkdfl[3], "tls13 server in", sizeof("tls13 server in") - 1); *p = '\0'; - if (ngx_hkdf_expand(qc->server_in.data, qc->server_in.len, + if (ngx_hkdf_expand(qc->server_in.secret.data, qc->server_in.secret.len, digest, is, is_len, hkdfl, hkdfl_len) != NGX_OK) { @@ -1019,57 +1019,57 @@ ngx_http_quic_handshake(ngx_event_t *rev) /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ #ifdef OPENSSL_IS_BORINGSSL - qc->server_in_key.len = EVP_AEAD_key_length(cipher); + qc->server_in.key.len = EVP_AEAD_key_length(cipher); #else - qc->server_in_key.len = EVP_CIPHER_key_length(cipher); + qc->server_in.key.len = EVP_CIPHER_key_length(cipher); #endif - qc->server_in_key.data = ngx_pnalloc(c->pool, qc->server_in_key.len); - if (qc->server_in_key.data == NULL) { + qc->server_in.key.data = ngx_pnalloc(c->pool, qc->server_in.key.len); + if (qc->server_in.key.data == NULL) { ngx_http_close_connection(c); return; } hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1; - hkdfl[1] = qc->server_in_key.len; + hkdfl[1] = qc->server_in.key.len; hkdfl[2] = sizeof("tls13 quic key") - 1; p = ngx_cpymem(&hkdfl[3], "tls13 quic key", sizeof("tls13 quic key") - 1); *p = '\0'; - if (ngx_hkdf_expand(qc->server_in_key.data, qc->server_in_key.len, - digest, qc->server_in.data, qc->server_in.len, + if (ngx_hkdf_expand(qc->server_in.key.data, qc->server_in.key.len, + digest, qc->server_in.secret.data, qc->server_in.secret.len, hkdfl, hkdfl_len) != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in_key) failed"); + "ngx_hkdf_expand(server_in.key) failed"); ngx_http_close_connection(c); return; } #ifdef OPENSSL_IS_BORINGSSL - qc->server_in_iv.len = EVP_AEAD_nonce_length(cipher); + qc->server_in.iv.len = EVP_AEAD_nonce_length(cipher); #else - qc->server_in_iv.len = EVP_CIPHER_iv_length(cipher); + qc->server_in.iv.len = EVP_CIPHER_iv_length(cipher); #endif - qc->server_in_iv.data = ngx_pnalloc(c->pool, qc->server_in_iv.len); - if (qc->server_in_iv.data == NULL) { + qc->server_in.iv.data = ngx_pnalloc(c->pool, qc->server_in.iv.len); + if (qc->server_in.iv.data == NULL) { ngx_http_close_connection(c); return; } hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1; - hkdfl[1] = qc->server_in_iv.len; + hkdfl[1] = qc->server_in.iv.len; hkdfl[2] = sizeof("tls13 quic iv") - 1; p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); *p = '\0'; - if (ngx_hkdf_expand(qc->server_in_iv.data, qc->server_in_iv.len, - digest, qc->server_in.data, qc->server_in.len, + if (ngx_hkdf_expand(qc->server_in.iv.data, qc->server_in.iv.len, + digest, qc->server_in.secret.data, qc->server_in.secret.len, hkdfl, hkdfl_len) != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in_iv) failed"); + "ngx_hkdf_expand(server_in.iv) failed"); ngx_http_close_connection(c); return; } @@ -1077,57 +1077,57 @@ ngx_http_quic_handshake(ngx_event_t *rev) /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ #ifdef OPENSSL_IS_BORINGSSL - qc->server_in_hp.len = EVP_AEAD_key_length(cipher); + qc->server_in.hp.len = EVP_AEAD_key_length(cipher); #else - qc->server_in_hp.len = EVP_CIPHER_key_length(cipher); + qc->server_in.hp.len = EVP_CIPHER_key_length(cipher); #endif - qc->server_in_hp.data = ngx_pnalloc(c->pool, qc->server_in_hp.len); - if (qc->server_in_hp.data == NULL) { + qc->server_in.hp.data = ngx_pnalloc(c->pool, qc->server_in.hp.len); + if (qc->server_in.hp.data == NULL) { ngx_http_close_connection(c); return; } hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1; - hkdfl[1] = qc->server_in_hp.len; + hkdfl[1] = qc->server_in.hp.len; hkdfl[2] = sizeof("tls13 quic hp") - 1; p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); *p = '\0'; - if (ngx_hkdf_expand(qc->server_in_hp.data, qc->server_in_hp.len, - digest, qc->server_in.data, qc->server_in.len, + if (ngx_hkdf_expand(qc->server_in.hp.data, qc->server_in.hp.len, + digest, qc->server_in.secret.data, qc->server_in.secret.len, hkdfl, hkdfl_len) != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in_hp) failed"); + "ngx_hkdf_expand(server_in.hp) failed"); ngx_http_close_connection(c); return; } #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, qc->server_in.data, qc->server_in.len) - buf; + m = ngx_hex_dump(buf, qc->server_in.secret.data, qc->server_in.secret.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic server initial secret: %*s, len: %uz", - m, buf, qc->server_in.len); + m, buf, qc->server_in.secret.len); - m = ngx_hex_dump(buf, qc->server_in_key.data, qc->server_in_key.len) + m = ngx_hex_dump(buf, qc->server_in.key.data, qc->server_in.key.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic server key: %*s, len: %uz", - m, buf, qc->server_in_key.len); + m, buf, qc->server_in.key.len); - m = ngx_hex_dump(buf, qc->server_in_iv.data, qc->server_in_iv.len) + m = ngx_hex_dump(buf, qc->server_in.iv.data, qc->server_in.iv.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic server iv: %*s, len: %uz", - m, buf, qc->server_in_iv.len); + m, buf, qc->server_in.iv.len); - m = ngx_hex_dump(buf, qc->server_in_hp.data, qc->server_in_hp.len) + m = ngx_hex_dump(buf, qc->server_in.hp.data, qc->server_in.hp.len) - buf; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic server hp: %*s, len: %uz", - m, buf, qc->server_in_hp.len); + m, buf, qc->server_in.hp.len); } #endif @@ -1138,7 +1138,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) int outlen; if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, - qc->client_in_hp.data, NULL) + qc->client_in.hp.data, NULL) != 1) { EVP_CIPHER_CTX_free(ctx); @@ -1195,7 +1195,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) ad.data[0] = clearflags; ad.data[ad.len - pnl] = (u_char)pn; - uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_in_iv); + uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_in.iv); nonce[11] ^= pn; #if (NGX_DEBUG) @@ -1215,8 +1215,8 @@ ngx_http_quic_handshake(ngx_event_t *rev) #ifdef OPENSSL_IS_BORINGSSL EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(cipher, - qc->client_in_key.data, - qc->client_in_key.len, + qc->client_in.key.data, + qc->client_in.key.len, EVP_AEAD_DEFAULT_TAG_LENGTH); if (aead == NULL) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_AEAD_CTX_new() failed"); @@ -1225,7 +1225,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) } if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext), - nonce, qc->client_in_iv.len, ciphertext.data, + nonce, qc->client_in.iv.len, ciphertext.data, ciphertext.len, ad.data, ad.len) != 1) { @@ -1256,7 +1256,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) return; } - if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, qc->client_in_iv.len, + if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, qc->client_in.iv.len, NULL) == 0) { @@ -1267,7 +1267,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) return; } - if (EVP_DecryptInit_ex(aead, NULL, NULL, qc->client_in_key.data, nonce) + if (EVP_DecryptInit_ex(aead, NULL, NULL, qc->client_in.key.data, nonce) != 1) { EVP_CIPHER_CTX_free(aead); @@ -1527,7 +1527,7 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) int outlen; if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, - qc->client_hs_hp.data, NULL) + qc->client_hs.hp.data, NULL) != 1) { EVP_CIPHER_CTX_free(ctx); @@ -1581,7 +1581,7 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) ad.data[0] = clearflags; ad.data[ad.len - pnl] = (u_char)pn; - uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_hs_iv); + uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_hs.iv); nonce[11] ^= pn; #if (NGX_DEBUG) @@ -1634,8 +1634,8 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) #ifdef OPENSSL_IS_BORINGSSL EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(cipher, - qc->client_hs_key.data, - qc->client_hs_key.len, + qc->client_hs.key.data, + qc->client_hs.key.len, EVP_AEAD_DEFAULT_TAG_LENGTH); if (aead == NULL) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_AEAD_CTX_new() failed"); @@ -1644,7 +1644,7 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) } if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext), - nonce, qc->client_hs_iv.len, ciphertext.data, + nonce, qc->client_hs.iv.len, ciphertext.data, ciphertext.len, ad.data, ad.len) != 1) { @@ -1675,7 +1675,7 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) return; } - if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, qc->client_hs_iv.len, + if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, qc->client_hs.iv.len, NULL) == 0) { @@ -1686,7 +1686,7 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) return; } - if (EVP_DecryptInit_ex(aead, NULL, NULL, qc->client_hs_key.data, nonce) + if (EVP_DecryptInit_ex(aead, NULL, NULL, qc->client_hs.key.data, nonce) != 1) { EVP_CIPHER_CTX_free(aead); -- cgit From 8c90e6f440f432908ad70002bb6acb1f9aec1758 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:52 +0300 Subject: Transport parameters stub, to complete handshake. --- src/http/ngx_http_request.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 7f62643c1..318933a40 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1358,6 +1358,17 @@ ngx_http_quic_handshake(ngx_event_t *rev) return; } + static const uint8_t params[12] = "\x00\x0a\x00\x3a\x00\x01\x00\x00\x09\x00\x01\x03"; + + if (SSL_set_quic_transport_params(c->ssl->connection, params, + sizeof(params)) == 0) + { + ngx_log_error(NGX_LOG_INFO, rev->log, 0, + "SSL_set_quic_transport_params() failed"); + ngx_http_close_connection(c); + return; + } + n = SSL_do_handshake(c->ssl->connection); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); @@ -1747,7 +1758,6 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) } #endif - ngx_http_close_connection(c); } -- cgit From 56eead6176d2d63392fc82668b2233dfadbae33e Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:52 +0300 Subject: AEAD routines, introduced ngx_quic_tls_open()/ngx_quic_tls_seal(). --- src/http/ngx_http_request.c | 270 +++++--------------------------------------- 1 file changed, 27 insertions(+), 243 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 318933a40..c9bddc6dd 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -783,27 +783,18 @@ ngx_http_quic_handshake(ngx_event_t *rev) // initial secret - size_t is_len; - uint8_t is[SHA256_DIGEST_LENGTH]; - const EVP_MD *digest; -#ifdef OPENSSL_IS_BORINGSSL - const EVP_AEAD *cipher; -#else - const EVP_CIPHER *cipher; -#endif + size_t is_len; + uint8_t is[SHA256_DIGEST_LENGTH]; + const EVP_MD *digest; + const ngx_aead_cipher_t *cipher; static const uint8_t salt[20] = "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"; - digest = EVP_sha256(); - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ -#ifdef OPENSSL_IS_BORINGSSL - cipher = EVP_aead_aes_128_gcm(); -#else - cipher = EVP_aes_128_gcm(); -#endif + cipher = NGX_QUIC_INITIAL_CIPHER; + digest = EVP_sha256(); if (ngx_hkdf_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, salt, sizeof(salt)) @@ -1179,9 +1170,9 @@ ngx_http_quic_handshake(ngx_event_t *rev) // packet protection - ngx_str_t ciphertext; - ciphertext.data = b->pos; - ciphertext.len = plen - pnl; + ngx_str_t in; + in.data = b->pos; + in.len = plen - pnl; ngx_str_t ad; ad.len = b->pos - b->start; @@ -1210,144 +1201,44 @@ ngx_http_quic_handshake(ngx_event_t *rev) } #endif - uint8_t cleartext[1600]; - size_t cleartext_len; - -#ifdef OPENSSL_IS_BORINGSSL - EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(cipher, - qc->client_in.key.data, - qc->client_in.key.len, - EVP_AEAD_DEFAULT_TAG_LENGTH); - if (aead == NULL) { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_AEAD_CTX_new() failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext), - nonce, qc->client_in.iv.len, ciphertext.data, - ciphertext.len, ad.data, ad.len) - != 1) - { - EVP_AEAD_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_AEAD_CTX_open() failed"); - ngx_http_close_connection(c); - return; - } - - EVP_AEAD_CTX_free(aead); -#else - int len; - u_char *tag; - EVP_CIPHER_CTX *aead; - - aead = EVP_CIPHER_CTX_new(); - if (aead == NULL) { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_CIPHER_CTX_new() failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_DecryptInit_ex(aead, cipher, NULL, NULL, NULL) != 1) { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); - ngx_http_close_connection(c); - return; - } + ngx_str_t out; - if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, qc->client_in.iv.len, - NULL) - == 0) - { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_DecryptInit_ex(aead, NULL, NULL, qc->client_in.key.data, nonce) - != 1) - { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_DecryptUpdate(aead, NULL, &len, ad.data, ad.len) != 1) { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_DecryptUpdate(aead, cleartext, &len, ciphertext.data, - ciphertext.len - EVP_GCM_TLS_TAG_LEN) - != 1) - { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); - ngx_http_close_connection(c); - return; - } - - cleartext_len = len; - tag = ciphertext.data + ciphertext.len - EVP_GCM_TLS_TAG_LEN; - - if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, - tag) - == 0) + if (ngx_quic_tls_open(c, cipher, &qc->client_in, &out, nonce, &in, &ad) + != NGX_OK) { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_TAG) failed"); ngx_http_close_connection(c); return; } - if (EVP_DecryptFinal_ex(aead, cleartext + len, &len) <= 0) { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptFinal_ex failed"); - ngx_http_close_connection(c); - return; - } - - cleartext_len += len; - - EVP_CIPHER_CTX_free(aead); -#endif - #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, cleartext, ngx_min(cleartext_len, 256)) - buf; + m = ngx_hex_dump(buf, out.data, ngx_min(out.len, 256)) - buf; ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic packet payload: %*s%s, len: %uz", - m, buf, m < 512 ? "" : "...", cleartext_len); + m, buf, m < 512 ? "" : "...", out.len); } #endif - if (cleartext[0] != 0x06) { + if (out.data[0] != 0x06) { ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected frame in initial packet"); ngx_http_close_connection(c); return; } - if (cleartext[1] != 0x00) { + if (out.data[1] != 0x00) { ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected CRYPTO offset in initial packet"); ngx_http_close_connection(c); return; } - uint8_t *crypto = &cleartext[2]; + uint8_t *crypto = &out.data[2]; uint64_t crypto_len = ngx_quic_parse_int(&crypto); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic initial packet CRYPTO length: %uL pp:%p:%p", - crypto_len, cleartext, crypto); + crypto_len, out.data, crypto); sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); @@ -1466,8 +1357,6 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake handler: %*s, len: %uz", m, buf, n); - /* XXX bug-for-bug compat - assuming initial ack in handshake pkt */ - if ((p[0] & 0xf0) != 0xe0) { ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid packet type"); ngx_http_close_connection(c); @@ -1576,9 +1465,9 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) // packet protection - ngx_str_t ciphertext; - ciphertext.data = p; - ciphertext.len = plen - pnl; + ngx_str_t in; + in.data = p; + in.len = plen - pnl; ngx_str_t ad; ad.len = p - b; @@ -1607,11 +1496,7 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) } #endif -#ifdef OPENSSL_IS_BORINGSSL - const EVP_AEAD *cipher; -#else - const EVP_CIPHER *cipher; -#endif + const ngx_aead_cipher_t *cipher; u_char *name = (u_char *) SSL_get_cipher(c->ssl->connection); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, @@ -1639,122 +1524,21 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) return; } + ngx_str_t out; - uint8_t cleartext[1600]; - size_t cleartext_len; - -#ifdef OPENSSL_IS_BORINGSSL - EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(cipher, - qc->client_hs.key.data, - qc->client_hs.key.len, - EVP_AEAD_DEFAULT_TAG_LENGTH); - if (aead == NULL) { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_AEAD_CTX_new() failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext), - nonce, qc->client_hs.iv.len, ciphertext.data, - ciphertext.len, ad.data, ad.len) - != 1) - { - EVP_AEAD_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_AEAD_CTX_open() failed"); - ngx_http_close_connection(c); - return; - } - - EVP_AEAD_CTX_free(aead); -#else - int len; - u_char *tag; - EVP_CIPHER_CTX *aead; - - aead = EVP_CIPHER_CTX_new(); - if (aead == NULL) { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_CIPHER_CTX_new() failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_DecryptInit_ex(aead, cipher, NULL, NULL, NULL) != 1) { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, qc->client_hs.iv.len, - NULL) - == 0) - { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_DecryptInit_ex(aead, NULL, NULL, qc->client_hs.key.data, nonce) - != 1) - { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_DecryptUpdate(aead, NULL, &len, ad.data, ad.len) != 1) { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); - ngx_http_close_connection(c); - return; - } - - if (EVP_DecryptUpdate(aead, cleartext, &len, ciphertext.data, - ciphertext.len - EVP_GCM_TLS_TAG_LEN) - != 1) - { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); - ngx_http_close_connection(c); - return; - } - - cleartext_len = len; - tag = ciphertext.data + ciphertext.len - EVP_GCM_TLS_TAG_LEN; - - if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, - tag) - == 0) + if (ngx_quic_tls_open(c, cipher, &qc->client_hs, &out, nonce, &in, &ad) + != NGX_OK) { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_TAG) failed"); ngx_http_close_connection(c); return; } - if (EVP_DecryptFinal_ex(aead, cleartext + len, &len) <= 0) { - EVP_CIPHER_CTX_free(aead); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptFinal_ex failed"); - ngx_http_close_connection(c); - return; - } - - cleartext_len += len; - - EVP_CIPHER_CTX_free(aead); -#endif - #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, cleartext, ngx_min(cleartext_len, 256)) - buf; + m = ngx_hex_dump(buf, out.data, ngx_min(out.len, 256)) - buf; ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic packet payload: %*s%s, len: %uz", - m, buf, m < 512 ? "" : "...", cleartext_len); + m, buf, m < 512 ? "" : "...", out.len); } #endif -- cgit From a3620d469f2378420b52199b5da4fff9fa0b8995 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:52 +0300 Subject: QUIC header protection routines, introduced ngx_quic_tls_hp(). --- src/http/ngx_http_request.c | 42 ++++-------------------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index c9bddc6dd..8fbc20424 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1124,31 +1124,14 @@ ngx_http_quic_handshake(ngx_event_t *rev) // header protection - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); uint8_t mask[16]; - int outlen; - - if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, - qc->client_in.hp.data, NULL) - != 1) + if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), &qc->client_in, mask, sample) + != NGX_OK) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_EncryptInit_ex() failed"); ngx_http_close_connection(c); return; } - if (!EVP_EncryptUpdate(ctx, mask, &outlen, sample, 16)) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_EncryptUpdate() failed"); - ngx_http_close_connection(c); - return; - } - - EVP_CIPHER_CTX_free(ctx); - u_char clearflags = flags ^ (mask[0] & 0x0f); ngx_int_t pnl = (clearflags & 0x03) + 1; uint64_t pn = ngx_quic_parse_pn(&b->pos, pnl, &mask[1]); @@ -1422,31 +1405,14 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) // header protection - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); uint8_t mask[16]; - int outlen; - - if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, - qc->client_hs.hp.data, NULL) - != 1) + if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), &qc->client_hs, mask, sample) + != NGX_OK) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_EncryptInit_ex() failed"); ngx_http_close_connection(c); return; } - if (!EVP_EncryptUpdate(ctx, mask, &outlen, sample, 16)) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "EVP_EncryptUpdate() failed"); - ngx_http_close_connection(c); - return; - } - - EVP_CIPHER_CTX_free(ctx); - u_char clearflags = flags ^ (mask[0] & 0x0f); ngx_int_t pnl = (clearflags & 0x03) + 1; uint64_t pn = ngx_quic_parse_pn(&p, pnl, &mask[1]); -- cgit From eb464a7feba7f3297e8127d5e707f36bec6b87a5 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Wed, 26 Feb 2020 16:56:47 +0300 Subject: Generic function for HKDF expansion. --- src/http/ngx_http_request.c | 351 +++++++++----------------------------------- 1 file changed, 69 insertions(+), 282 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 8fbc20424..7aa5a6408 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -785,6 +785,7 @@ ngx_http_quic_handshake(ngx_event_t *rev) size_t is_len; uint8_t is[SHA256_DIGEST_LENGTH]; + ngx_uint_t i; const EVP_MD *digest; const ngx_aead_cipher_t *cipher; static const uint8_t salt[20] = @@ -804,6 +805,11 @@ ngx_http_quic_handshake(ngx_event_t *rev) return; } + ngx_str_t iss = { + .data = is, + .len = is_len + }; + #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { m = ngx_hex_dump(buf, (uint8_t *) salt, sizeof(salt)) - buf; @@ -816,311 +822,92 @@ ngx_http_quic_handshake(ngx_event_t *rev) } #endif - size_t hkdfl_len; - uint8_t hkdfl[20]; - uint8_t *p; - /* draft-ietf-quic-tls-23#section-5.2 */ - qc->client_in.secret.len = SHA256_DIGEST_LENGTH; - qc->client_in.secret.data = ngx_pnalloc(c->pool, qc->client_in.secret.len); - if (qc->client_in.secret.data == NULL) { - ngx_http_close_connection(c); - return; - } - - hkdfl_len = 2 + 1 + sizeof("tls13 client in") - 1 + 1; - bzero(hkdfl, sizeof(hkdfl)); - hkdfl[0] = 0; - hkdfl[1] = qc->client_in.secret.len; - hkdfl[2] = sizeof("tls13 client in") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 client in", sizeof("tls13 client in") - 1); - *p = '\0'; - -#if 0 - ngx_memcpy(hkdfl, "\x00\x20\x0f\x74\x6c\x73\x31\x33\x20\x63" - "\x6c\x69\x65\x6e\x74\x20\x69\x6e\x00\x00", 20); - - m = ngx_hex_dump(buf, hkdfl, sizeof(hkdfl)) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic initial secret hkdf: %*s, len: %uz", - m, buf, sizeof(hkdfl)); -#endif - - if (ngx_hkdf_expand(qc->client_in.secret.data, qc->client_in.secret.len, - digest, is, is_len, hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in) failed"); - ngx_http_close_connection(c); - return; - } - -#ifdef OPENSSL_IS_BORINGSSL - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic EVP key:%d tag:%d nonce:%d", - EVP_AEAD_key_length(cipher), - EVP_AEAD_max_tag_len(cipher), - EVP_AEAD_nonce_length(cipher)); -#endif - + qc->server_in.secret.len = SHA256_DIGEST_LENGTH; #ifdef OPENSSL_IS_BORINGSSL qc->client_in.key.len = EVP_AEAD_key_length(cipher); -#else - qc->client_in.key.len = EVP_CIPHER_key_length(cipher); -#endif - qc->client_in.key.data = ngx_pnalloc(c->pool, qc->client_in.key.len); - if (qc->client_in.key.data == NULL) { - ngx_http_close_connection(c); - return; - } - - hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1; - hkdfl[1] = qc->client_in.key.len; - hkdfl[2] = sizeof("tls13 quic key") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic key", sizeof("tls13 quic key") - 1); - *p = '\0'; - - if (ngx_hkdf_expand(qc->client_in.key.data, qc->client_in.key.len, - digest, qc->client_in.secret.data, qc->client_in.secret.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in.key) failed"); - ngx_http_close_connection(c); - return; - } - -#ifdef OPENSSL_IS_BORINGSSL - qc->client_in.iv.len = EVP_AEAD_nonce_length(cipher); -#else - qc->client_in.iv.len = EVP_CIPHER_iv_length(cipher); -#endif - qc->client_in.iv.data = ngx_pnalloc(c->pool, qc->client_in.iv.len); - if (qc->client_in.iv.data == NULL) { - ngx_http_close_connection(c); - return; - } - - hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1; - hkdfl[1] = qc->client_in.iv.len; - hkdfl[2] = sizeof("tls13 quic iv") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); - *p = '\0'; - - if (ngx_hkdf_expand(qc->client_in.iv.data, qc->client_in.iv.len, - digest, qc->client_in.secret.data, qc->client_in.secret.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in.iv) failed"); - ngx_http_close_connection(c); - return; - } - - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ + qc->server_in.key.len = EVP_AEAD_key_length(cipher); -#ifdef OPENSSL_IS_BORINGSSL qc->client_in.hp.len = EVP_AEAD_key_length(cipher); -#else - qc->client_in.hp.len = EVP_CIPHER_key_length(cipher); -#endif - qc->client_in.hp.data = ngx_pnalloc(c->pool, qc->client_in.hp.len); - if (qc->client_in.hp.data == NULL) { - ngx_http_close_connection(c); - return; - } - - hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1; - hkdfl[1] = qc->client_in.hp.len; - hkdfl[2] = sizeof("tls13 quic hp") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); - *p = '\0'; - - if (ngx_hkdf_expand(qc->client_in.hp.data, qc->client_in.hp.len, - digest, qc->client_in.secret.data, qc->client_in.secret.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(client_in.hp) failed"); - ngx_http_close_connection(c); - return; - } - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, qc->client_in.secret.data, qc->client_in.secret.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic client initial secret: %*s, len: %uz", - m, buf, qc->client_in.secret.len); - - m = ngx_hex_dump(buf, qc->client_in.key.data, qc->client_in.key.len) - - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic client key: %*s, len: %uz", - m, buf, qc->client_in.key.len); - - m = ngx_hex_dump(buf, qc->client_in.iv.data, qc->client_in.iv.len) - - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic client iv: %*s, len: %uz", - m, buf, qc->client_in.iv.len); - - m = ngx_hex_dump(buf, qc->client_in.hp.data, qc->client_in.hp.len) - - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic client hp: %*s, len: %uz", - m, buf, qc->client_in.hp.len); - } -#endif - -// server initial - - /* draft-ietf-quic-tls-23#section-5.2 */ - - qc->server_in.secret.len = SHA256_DIGEST_LENGTH; - qc->server_in.secret.data = ngx_pnalloc(c->pool, qc->server_in.secret.len); - if (qc->server_in.secret.data == NULL) { - ngx_http_close_connection(c); - return; - } - - hkdfl_len = 2 + 1 + sizeof("tls13 server in") - 1 + 1; - hkdfl[0] = 0; - hkdfl[1] = qc->server_in.secret.len; - hkdfl[2] = sizeof("tls13 server in") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 server in", sizeof("tls13 server in") - 1); - *p = '\0'; - - if (ngx_hkdf_expand(qc->server_in.secret.data, qc->server_in.secret.len, - digest, is, is_len, hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in) failed"); - ngx_http_close_connection(c); - return; - } - - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ + qc->server_in.hp.len = EVP_AEAD_key_length(cipher); -#ifdef OPENSSL_IS_BORINGSSL - qc->server_in.key.len = EVP_AEAD_key_length(cipher); + qc->client_in.iv.len = EVP_AEAD_nonce_length(cipher); + qc->server_in.iv.len = EVP_AEAD_nonce_length(cipher); #else + qc->client_in.key.len = EVP_CIPHER_key_length(cipher); qc->server_in.key.len = EVP_CIPHER_key_length(cipher); -#endif - qc->server_in.key.data = ngx_pnalloc(c->pool, qc->server_in.key.len); - if (qc->server_in.key.data == NULL) { - ngx_http_close_connection(c); - return; - } - - hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1; - hkdfl[1] = qc->server_in.key.len; - hkdfl[2] = sizeof("tls13 quic key") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic key", sizeof("tls13 quic key") - 1); - *p = '\0'; - if (ngx_hkdf_expand(qc->server_in.key.data, qc->server_in.key.len, - digest, qc->server_in.secret.data, qc->server_in.secret.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in.key) failed"); - ngx_http_close_connection(c); - return; - } + qc->client_in.hp.len = EVP_CIPHER_key_length(cipher); + qc->server_in.hp.len = EVP_CIPHER_key_length(cipher); -#ifdef OPENSSL_IS_BORINGSSL - qc->server_in.iv.len = EVP_AEAD_nonce_length(cipher); -#else + qc->client_in.iv.len = EVP_CIPHER_iv_length(cipher); qc->server_in.iv.len = EVP_CIPHER_iv_length(cipher); #endif - qc->server_in.iv.data = ngx_pnalloc(c->pool, qc->server_in.iv.len); - if (qc->server_in.iv.data == NULL) { - ngx_http_close_connection(c); - return; - } - - hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1; - hkdfl[1] = qc->server_in.iv.len; - hkdfl[2] = sizeof("tls13 quic iv") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); - *p = '\0'; - - if (ngx_hkdf_expand(qc->server_in.iv.data, qc->server_in.iv.len, - digest, qc->server_in.secret.data, qc->server_in.secret.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in.iv) failed"); - ngx_http_close_connection(c); - return; - } - - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ #ifdef OPENSSL_IS_BORINGSSL - qc->server_in.hp.len = EVP_AEAD_key_length(cipher); -#else - qc->server_in.hp.len = EVP_CIPHER_key_length(cipher); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic EVP key:%d tag:%d nonce:%d", + EVP_AEAD_key_length(cipher), + EVP_AEAD_max_tag_len(cipher), + EVP_AEAD_nonce_length(cipher)); #endif - qc->server_in.hp.data = ngx_pnalloc(c->pool, qc->server_in.hp.len); - if (qc->server_in.hp.data == NULL) { - ngx_http_close_connection(c); - return; - } - - hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1; - hkdfl[1] = qc->server_in.hp.len; - hkdfl[2] = sizeof("tls13 quic hp") - 1; - p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); - *p = '\0'; - if (ngx_hkdf_expand(qc->server_in.hp.data, qc->server_in.hp.len, - digest, qc->server_in.secret.data, qc->server_in.secret.len, - hkdfl, hkdfl_len) - != NGX_OK) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "ngx_hkdf_expand(server_in.hp) failed"); - ngx_http_close_connection(c); - return; - } + struct { + ngx_str_t id; + ngx_str_t *in; + ngx_str_t *prk; + } seq[] = { -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, qc->server_in.secret.data, qc->server_in.secret.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic server initial secret: %*s, len: %uz", - m, buf, qc->server_in.secret.len); + /* draft-ietf-quic-tls-23#section-5.2 */ + { ngx_string("tls13 client in"), &qc->client_in.secret, &iss }, + { + ngx_string("tls13 quic key"), + &qc->client_in.key, + &qc->client_in.secret, + }, + { + ngx_string("tls13 quic iv"), + &qc->client_in.iv, + &qc->client_in.secret, + }, + { + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ + ngx_string("tls13 quic hp"), + &qc->client_in.hp, + &qc->client_in.secret, + }, + { ngx_string("tls13 server in"), &qc->server_in.secret, &iss }, + { + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ + ngx_string("tls13 quic key"), + &qc->server_in.key, + &qc->server_in.secret, + }, + { + ngx_string("tls13 quic iv"), + &qc->server_in.iv, + &qc->server_in.secret, + }, + { + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ + ngx_string("tls13 quic hp"), + &qc->server_in.hp, + &qc->server_in.secret + }, - m = ngx_hex_dump(buf, qc->server_in.key.data, qc->server_in.key.len) - - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic server key: %*s, len: %uz", - m, buf, qc->server_in.key.len); + }; - m = ngx_hex_dump(buf, qc->server_in.iv.data, qc->server_in.iv.len) - - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic server iv: %*s, len: %uz", - m, buf, qc->server_in.iv.len); + for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { - m = ngx_hex_dump(buf, qc->server_in.hp.data, qc->server_in.hp.len) - - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic server hp: %*s, len: %uz", - m, buf, qc->server_in.hp.len); + if (ngx_quic_hkdf_expand(c, digest, seq[i].in, seq[i].prk, &seq[i].id, 0) + != NGX_OK) + { + ngx_http_close_connection(c); + return; + } } -#endif // header protection -- cgit From ef8b06b186a2f7ac25b8ee49a325c935c3e5bb9f Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:52 +0300 Subject: Cleanup. --- src/http/modules/ngx_http_ssl_module.c | 3 -- src/http/ngx_http_request.c | 67 ++++++++++------------------------ 2 files changed, 19 insertions(+), 51 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 693e45a1c..8640c2211 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -706,7 +706,6 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } } -printf("ngx_ssl_create\n"); if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -1156,7 +1155,6 @@ ngx_http_ssl_init(ngx_conf_t *cf) addr = port[p].addrs.elts; for (a = 0; a < port[p].addrs.nelts; a++) { -printf("ssl %d http3 %d\n", addr[a].opt.ssl, addr[a].opt.http3); if (!addr[a].opt.ssl && !addr[a].opt.http3) { continue; @@ -1164,7 +1162,6 @@ printf("ssl %d http3 %d\n", addr[a].opt.ssl, addr[a].opt.http3); cscf = addr[a].default_server; sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; -printf("sscf->protocols %lx\n", sscf->protocols); if (sscf->certificates == NULL) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 7aa5a6408..bc2797b21 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -678,16 +678,6 @@ ngx_http_quic_handshake(ngx_event_t *rev) hc = c->data; b = c->buffer; - qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); - if (qc == NULL) { - ngx_http_close_connection(c); - return; - } - - c->quic = qc; - - printf("buffer %p %p:%p:%p:%p \n", b, b->start, b->pos, b->last, b->end); - if ((b->pos[0] & 0xf0) != 0xc0) { ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid initial packet"); ngx_http_close_connection(c); @@ -713,6 +703,14 @@ ngx_http_quic_handshake(ngx_event_t *rev) return; } + qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); + if (qc == NULL) { + ngx_http_close_connection(c); + return; + } + + c->quic = qc; + qc->dcid.len = *b->pos++; qc->dcid.data = ngx_pnalloc(c->pool, qc->dcid.len); if (qc->dcid.data == NULL) { @@ -787,14 +785,14 @@ ngx_http_quic_handshake(ngx_event_t *rev) uint8_t is[SHA256_DIGEST_LENGTH]; ngx_uint_t i; const EVP_MD *digest; - const ngx_aead_cipher_t *cipher; + const EVP_CIPHER *cipher; static const uint8_t salt[20] = "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"; /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ - cipher = NGX_QUIC_INITIAL_CIPHER; + cipher = EVP_aes_128_gcm(); digest = EVP_sha256(); if (ngx_hkdf_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, @@ -826,16 +824,6 @@ ngx_http_quic_handshake(ngx_event_t *rev) qc->client_in.secret.len = SHA256_DIGEST_LENGTH; qc->server_in.secret.len = SHA256_DIGEST_LENGTH; -#ifdef OPENSSL_IS_BORINGSSL - qc->client_in.key.len = EVP_AEAD_key_length(cipher); - qc->server_in.key.len = EVP_AEAD_key_length(cipher); - - qc->client_in.hp.len = EVP_AEAD_key_length(cipher); - qc->server_in.hp.len = EVP_AEAD_key_length(cipher); - - qc->client_in.iv.len = EVP_AEAD_nonce_length(cipher); - qc->server_in.iv.len = EVP_AEAD_nonce_length(cipher); -#else qc->client_in.key.len = EVP_CIPHER_key_length(cipher); qc->server_in.key.len = EVP_CIPHER_key_length(cipher); @@ -844,19 +832,10 @@ ngx_http_quic_handshake(ngx_event_t *rev) qc->client_in.iv.len = EVP_CIPHER_iv_length(cipher); qc->server_in.iv.len = EVP_CIPHER_iv_length(cipher); -#endif - -#ifdef OPENSSL_IS_BORINGSSL - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic EVP key:%d tag:%d nonce:%d", - EVP_AEAD_key_length(cipher), - EVP_AEAD_max_tag_len(cipher), - EVP_AEAD_nonce_length(cipher)); -#endif struct { - ngx_str_t id; - ngx_str_t *in; + ngx_str_t label; + ngx_str_t *key; ngx_str_t *prk; } seq[] = { @@ -894,14 +873,15 @@ ngx_http_quic_handshake(ngx_event_t *rev) /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ ngx_string("tls13 quic hp"), &qc->server_in.hp, - &qc->server_in.secret + &qc->server_in.secret, }, }; for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { - if (ngx_quic_hkdf_expand(c, digest, seq[i].in, seq[i].prk, &seq[i].id, 0) + if (ngx_quic_hkdf_expand(c, digest, seq[i].key, &seq[i].label, + seq[i].prk->data, seq[i].prk->len) != NGX_OK) { ngx_http_close_connection(c); @@ -973,7 +953,8 @@ ngx_http_quic_handshake(ngx_event_t *rev) ngx_str_t out; - if (ngx_quic_tls_open(c, cipher, &qc->client_in, &out, nonce, &in, &ad) + if (ngx_quic_tls_open(c, EVP_aes_128_gcm(), &qc->client_in, &out, nonce, + &in, &ad) != NGX_OK) { ngx_http_close_connection(c); @@ -1090,7 +1071,9 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) { size_t m; ssize_t n; + ngx_str_t out; ngx_connection_t *c; + const EVP_CIPHER *cipher; ngx_quic_connection_t *qc; u_char buf[4096], b[512], *p; @@ -1249,8 +1232,6 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) } #endif - const ngx_aead_cipher_t *cipher; - u_char *name = (u_char *) SSL_get_cipher(c->ssl->connection); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic ssl cipher: %s", name); @@ -1258,18 +1239,10 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) { -#ifdef OPENSSL_IS_BORINGSSL - cipher = EVP_aead_aes_128_gcm(); -#else cipher = EVP_aes_128_gcm(); -#endif } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { -#ifdef OPENSSL_IS_BORINGSSL - cipher = EVP_aead_aes_256_gcm(); -#else cipher = EVP_aes_256_gcm(); -#endif } else { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "unexpected cipher"); @@ -1277,8 +1250,6 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) return; } - ngx_str_t out; - if (ngx_quic_tls_open(c, cipher, &qc->client_hs, &out, nonce, &in, &ad) != NGX_OK) { -- cgit From 899372129879b5874ffddca4247fca12d3b7c257 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 28 Feb 2020 13:09:52 +0300 Subject: Introduced quic_version macro, uint16/uint32 routines ported. --- src/http/ngx_http_request.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index bc2797b21..d40f50496 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -691,13 +691,13 @@ ngx_http_quic_handshake(ngx_event_t *rev) } ngx_int_t flags = *b->pos++; - uint32_t version = ngx_http_v2_parse_uint32(b->pos); - b->pos += 4; + uint32_t version = ngx_quic_parse_uint32(b->pos); + b->pos += sizeof(uint32_t); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic flags:%xi version:%xD", flags, version); - if (version != 0xff000018) { + if (version != quic_version) { ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); ngx_http_close_connection(c); return; @@ -1117,13 +1117,13 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) } ngx_int_t flags = *p++; - uint32_t version = ngx_http_v2_parse_uint32(p); - p += 4; + uint32_t version = ngx_quic_parse_uint32(p); + p += sizeof(uint32_t); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic flags:%xi version:%xD", flags, version); - if (version != 0xff000018) { + if (version != quic_version) { ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); ngx_http_close_connection(c); return; -- cgit From b20ed8f7f1acc2a1ef38714ddd75c7ba7add5f1d Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Fri, 28 Feb 2020 16:23:25 +0300 Subject: Moved all QUIC code into ngx_event_quic.c Introduced ngx_quic_input() and ngx_quic_output() as interface between nginx and protocol. They are the only functions that are exported. While there, added copyrights. --- src/http/ngx_http_request.c | 555 +------------------------------------------- 1 file changed, 10 insertions(+), 545 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index d40f50496..6d89fef24 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -661,402 +661,22 @@ ngx_http_alloc_request(ngx_connection_t *c) static void ngx_http_quic_handshake(ngx_event_t *rev) { - int n, sslerr; -#if (NGX_DEBUG) - u_char buf[512]; - size_t m; -#endif - ngx_buf_t *b; ngx_connection_t *c; ngx_http_connection_t *hc; - ngx_quic_connection_t *qc; ngx_http_ssl_srv_conf_t *sscf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake"); c = rev->data; hc = c->data; - b = c->buffer; - - if ((b->pos[0] & 0xf0) != 0xc0) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid initial packet"); - ngx_http_close_connection(c); - return; - } - - if (ngx_buf_size(b) < 1200) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "too small UDP datagram"); - ngx_http_close_connection(c); - return; - } - - ngx_int_t flags = *b->pos++; - uint32_t version = ngx_quic_parse_uint32(b->pos); - b->pos += sizeof(uint32_t); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic flags:%xi version:%xD", flags, version); - - if (version != quic_version) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); - ngx_http_close_connection(c); - return; - } - - qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); - if (qc == NULL) { - ngx_http_close_connection(c); - return; - } - - c->quic = qc; - - qc->dcid.len = *b->pos++; - qc->dcid.data = ngx_pnalloc(c->pool, qc->dcid.len); - if (qc->dcid.data == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(qc->dcid.data, b->pos, qc->dcid.len); - b->pos += qc->dcid.len; - - qc->scid.len = *b->pos++; - qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len); - if (qc->scid.data == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(qc->scid.data, b->pos, qc->scid.len); - b->pos += qc->scid.len; - - qc->token.len = ngx_quic_parse_int(&b->pos); - qc->token.data = ngx_pnalloc(c->pool, qc->token.len); - if (qc->token.data == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(qc->token.data, b->pos, qc->token.len); - b->pos += qc->token.len; - - ngx_int_t plen = ngx_quic_parse_int(&b->pos); - - if (plen > b->last - b->pos) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "truncated initial packet"); - ngx_http_close_connection(c); - return; - } - - /* draft-ietf-quic-tls-23#section-5.4.2: - * the Packet Number field is assumed to be 4 bytes long - * draft-ietf-quic-tls-23#section-5.4.[34]: - * AES-Based and ChaCha20-Based header protections sample 16 bytes - */ - u_char *sample = b->pos + 4; - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, qc->dcid.data, qc->dcid.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic DCID: %*s, len: %uz", m, buf, qc->dcid.len); - - m = ngx_hex_dump(buf, qc->scid.data, qc->scid.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic SCID: %*s, len: %uz", m, buf, qc->scid.len); - - m = ngx_hex_dump(buf, qc->token.data, qc->token.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic token: %*s, len: %uz", m, buf, qc->token.len); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet length: %d", plen); - - m = ngx_hex_dump(buf, sample, 16) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic sample: %*s", m, buf); - } -#endif - -// initial secret - - size_t is_len; - uint8_t is[SHA256_DIGEST_LENGTH]; - ngx_uint_t i; - const EVP_MD *digest; - const EVP_CIPHER *cipher; - static const uint8_t salt[20] = - "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" - "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"; - - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ - - cipher = EVP_aes_128_gcm(); - digest = EVP_sha256(); - - if (ngx_hkdf_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, - salt, sizeof(salt)) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - - ngx_str_t iss = { - .data = is, - .len = is_len - }; - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, (uint8_t *) salt, sizeof(salt)) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic salt: %*s, len: %uz", m, buf, sizeof(salt)); - - m = ngx_hex_dump(buf, is, is_len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic initial secret: %*s, len: %uz", m, buf, is_len); - } -#endif - - /* draft-ietf-quic-tls-23#section-5.2 */ - qc->client_in.secret.len = SHA256_DIGEST_LENGTH; - qc->server_in.secret.len = SHA256_DIGEST_LENGTH; - - qc->client_in.key.len = EVP_CIPHER_key_length(cipher); - qc->server_in.key.len = EVP_CIPHER_key_length(cipher); - - qc->client_in.hp.len = EVP_CIPHER_key_length(cipher); - qc->server_in.hp.len = EVP_CIPHER_key_length(cipher); - - qc->client_in.iv.len = EVP_CIPHER_iv_length(cipher); - qc->server_in.iv.len = EVP_CIPHER_iv_length(cipher); - - struct { - ngx_str_t label; - ngx_str_t *key; - ngx_str_t *prk; - } seq[] = { - - /* draft-ietf-quic-tls-23#section-5.2 */ - { ngx_string("tls13 client in"), &qc->client_in.secret, &iss }, - { - ngx_string("tls13 quic key"), - &qc->client_in.key, - &qc->client_in.secret, - }, - { - ngx_string("tls13 quic iv"), - &qc->client_in.iv, - &qc->client_in.secret, - }, - { - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ - ngx_string("tls13 quic hp"), - &qc->client_in.hp, - &qc->client_in.secret, - }, - { ngx_string("tls13 server in"), &qc->server_in.secret, &iss }, - { - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ - ngx_string("tls13 quic key"), - &qc->server_in.key, - &qc->server_in.secret, - }, - { - ngx_string("tls13 quic iv"), - &qc->server_in.iv, - &qc->server_in.secret, - }, - { - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ - ngx_string("tls13 quic hp"), - &qc->server_in.hp, - &qc->server_in.secret, - }, - - }; - - for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { - - if (ngx_quic_hkdf_expand(c, digest, seq[i].key, &seq[i].label, - seq[i].prk->data, seq[i].prk->len) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - } - -// header protection - - uint8_t mask[16]; - if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), &qc->client_in, mask, sample) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - - u_char clearflags = flags ^ (mask[0] & 0x0f); - ngx_int_t pnl = (clearflags & 0x03) + 1; - uint64_t pn = ngx_quic_parse_pn(&b->pos, pnl, &mask[1]); - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, sample, 16) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic sample: %*s", m, buf); - - m = ngx_hex_dump(buf, mask, 5) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic mask: %*s", m, buf); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet number: %uL, len: %xi", pn, pnl); - } -#endif - -// packet protection - - ngx_str_t in; - in.data = b->pos; - in.len = plen - pnl; - - ngx_str_t ad; - ad.len = b->pos - b->start; - ad.data = ngx_pnalloc(c->pool, ad.len); - if (ad.data == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(ad.data, b->start, ad.len); - ad.data[0] = clearflags; - ad.data[ad.len - pnl] = (u_char)pn; - - uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_in.iv); - nonce[11] ^= pn; - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, nonce, 12) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic nonce: %*s, len: %uz", m, buf, 12); - - m = ngx_hex_dump(buf, ad.data, ad.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic ad: %*s, len: %uz", m, buf, ad.len); - } -#endif - - ngx_str_t out; - - if (ngx_quic_tls_open(c, EVP_aes_128_gcm(), &qc->client_in, &out, nonce, - &in, &ad) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, out.data, ngx_min(out.len, 256)) - buf; - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet payload: %*s%s, len: %uz", - m, buf, m < 512 ? "" : "...", out.len); - } -#endif - - if (out.data[0] != 0x06) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, - "unexpected frame in initial packet"); - ngx_http_close_connection(c); - return; - } - - if (out.data[1] != 0x00) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, - "unexpected CRYPTO offset in initial packet"); - ngx_http_close_connection(c); - return; - } - - uint8_t *crypto = &out.data[2]; - uint64_t crypto_len = ngx_quic_parse_int(&crypto); - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic initial packet CRYPTO length: %uL pp:%p:%p", - crypto_len, out.data, crypto); sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); - if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - - static const uint8_t params[12] = "\x00\x0a\x00\x3a\x00\x01\x00\x00\x09\x00\x01\x03"; - - if (SSL_set_quic_transport_params(c->ssl->connection, params, - sizeof(params)) == 0) - { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, - "SSL_set_quic_transport_params() failed"); + if (ngx_quic_input(c, &sscf->ssl, c->buffer) != NGX_OK) { ngx_http_close_connection(c); return; } - n = SSL_do_handshake(c->ssl->connection); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); - - if (n == -1) { - sslerr = SSL_get_error(c->ssl->connection, n); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", - sslerr); - } - - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL_quic_read_level: %d, SSL_quic_write_level: %d", - (int) SSL_quic_read_level(c->ssl->connection), - (int) SSL_quic_write_level(c->ssl->connection)); - - if (!SSL_provide_quic_data(c->ssl->connection, - SSL_quic_read_level(c->ssl->connection), - crypto, crypto_len)) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "SSL_provide_quic_data() failed"); - ngx_http_close_connection(c); - return; - } - - n = SSL_do_handshake(c->ssl->connection); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); - - if (n == -1) { - sslerr = SSL_get_error(c->ssl->connection, n); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", - sslerr); - - if (sslerr == SSL_ERROR_SSL) { - ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed"); - } - } - - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL_quic_read_level: %d, SSL_quic_write_level: %d", - (int) SSL_quic_read_level(c->ssl->connection), - (int) SSL_quic_write_level(c->ssl->connection)); - if (!rev->timer_set) { ngx_add_timer(rev, c->listening->post_accept_timeout); } @@ -1069,17 +689,16 @@ ngx_http_quic_handshake(ngx_event_t *rev) static void ngx_http_quic_handshake_handler(ngx_event_t *rev) { - size_t m; ssize_t n; - ngx_str_t out; ngx_connection_t *c; - const EVP_CIPHER *cipher; - ngx_quic_connection_t *qc; - u_char buf[4096], b[512], *p; + u_char buf[512]; + ngx_buf_t b; + + b.start = buf; + b.end = buf + 512; + b.pos = b.last = b.start; c = rev->data; - qc = c->quic; - p = b; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake handler"); @@ -1094,7 +713,7 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) return; } - n = c->recv(c, b, sizeof(b)); + n = c->recv(c, b.start, b.end - b.start); if (n == NGX_AGAIN) { return; @@ -1106,166 +725,12 @@ ngx_http_quic_handshake_handler(ngx_event_t *rev) return; } - m = ngx_hex_dump(buf, b, n) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic handshake handler: %*s, len: %uz", m, buf, n); + b.last += n; - if ((p[0] & 0xf0) != 0xe0) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid packet type"); + if (ngx_quic_input(c, NULL, &b) != NGX_OK) { ngx_http_close_connection(c); return; } - - ngx_int_t flags = *p++; - uint32_t version = ngx_quic_parse_uint32(p); - p += sizeof(uint32_t); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic flags:%xi version:%xD", flags, version); - - if (version != quic_version) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); - ngx_http_close_connection(c); - return; - } - - if (*p++ != qc->dcid.len) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic dcidl"); - ngx_http_close_connection(c); - return; - } - - if (ngx_memcmp(p, qc->dcid.data, qc->dcid.len) != 0) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic dcid"); - ngx_http_close_connection(c); - return; - } - - p += qc->dcid.len; - - if (*p++ != qc->scid.len) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic scidl"); - ngx_http_close_connection(c); - return; - } - - if (ngx_memcmp(p, qc->scid.data, qc->scid.len) != 0) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic scid"); - ngx_http_close_connection(c); - return; - } - - p += qc->scid.len; - - ngx_int_t plen = ngx_quic_parse_int(&p); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet length: %d", plen); - - if (plen > b + n - p) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "truncated handshake packet"); - ngx_http_close_connection(c); - return; - } - - u_char *sample = p + 4; - - m = ngx_hex_dump(buf, sample, 16) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic sample: %*s", m, buf); - -// header protection - - uint8_t mask[16]; - if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), &qc->client_hs, mask, sample) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - - u_char clearflags = flags ^ (mask[0] & 0x0f); - ngx_int_t pnl = (clearflags & 0x03) + 1; - uint64_t pn = ngx_quic_parse_pn(&p, pnl, &mask[1]); - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, mask, 5) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic mask: %*s", m, buf); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic clear flags: %xi", clearflags); - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet number: %uL, len: %xi", pn, pnl); - } -#endif - -// packet protection - - ngx_str_t in; - in.data = p; - in.len = plen - pnl; - - ngx_str_t ad; - ad.len = p - b; - ad.data = ngx_pnalloc(c->pool, ad.len); - if (ad.data == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(ad.data, b, ad.len); - ad.data[0] = clearflags; - ad.data[ad.len - pnl] = (u_char)pn; - - uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_hs.iv); - nonce[11] ^= pn; - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, nonce, 12) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic nonce: %*s, len: %uz", m, buf, 12); - - m = ngx_hex_dump(buf, ad.data, ad.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic ad: %*s, len: %uz", m, buf, ad.len); - } -#endif - - u_char *name = (u_char *) SSL_get_cipher(c->ssl->connection); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic ssl cipher: %s", name); - - if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 - || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) - { - cipher = EVP_aes_128_gcm(); - - } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { - cipher = EVP_aes_256_gcm(); - - } else { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "unexpected cipher"); - ngx_http_close_connection(c); - return; - } - - if (ngx_quic_tls_open(c, cipher, &qc->client_hs, &out, nonce, &in, &ad) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, out.data, ngx_min(out.len, 256)) - buf; - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet payload: %*s%s, len: %uz", - m, buf, m < 512 ? "" : "...", out.len); - } -#endif - } -- cgit From 4f4f56f013eb0dbe5eb66bb2f22584aec26b13e6 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Thu, 12 Mar 2020 16:54:43 +0300 Subject: HTTP/QUIC interface reworked. - events handling moved into src/event/ngx_event_quic.c - http invokes once ngx_quic_run() and passes stream callback (diff to original http_request.c is now minimal) - streams are stored in rbtree using ID as a key - when a new stream is registered, appropriate callback is called - ngx_quic_stream_t type represents STREAM and stored in c->qs --- src/http/ngx_http_request.c | 105 ++++++++------------------------------------ 1 file changed, 18 insertions(+), 87 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 6d89fef24..7a2c78046 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -62,11 +62,9 @@ static u_char *ngx_http_log_error_handler(ngx_http_request_t *r, #if (NGX_HTTP_SSL) static void ngx_http_ssl_handshake(ngx_event_t *rev); static void ngx_http_ssl_handshake_handler(ngx_connection_t *c); - -static void ngx_http_quic_handshake(ngx_event_t *rev); -static void ngx_http_quic_handshake_handler(ngx_event_t *rev); #endif +static void ngx_http_quic_stream_handler(ngx_connection_t *c); static char *ngx_http_client_errors[] = { @@ -333,9 +331,15 @@ ngx_http_init_connection(ngx_connection_t *c) #if (NGX_HTTP_SSL) if (hc->addr_conf->http3) { + ngx_http_ssl_srv_conf_t *sscf; + hc->quic = 1; - c->log->action = "QUIC handshaking"; - rev->handler = ngx_http_quic_handshake; + + 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_http_quic_stream_handler); + return; } #endif @@ -386,6 +390,15 @@ ngx_http_init_connection(ngx_connection_t *c) } +static void +ngx_http_quic_stream_handler(ngx_connection_t *c) +{ + ngx_quic_stream_t *qs = c->qs; + + printf("quic stream: 0x%lx\n", qs->id); +} + + static void ngx_http_wait_request_handler(ngx_event_t *rev) { @@ -401,10 +414,6 @@ ngx_http_wait_request_handler(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler"); - if (c->shared) { - goto request; - } - if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); ngx_http_close_connection(c); @@ -505,8 +514,6 @@ ngx_http_wait_request_handler(ngx_event_t *rev) } } -request: - c->log->action = "reading client request line"; ngx_reusable_connection(c, 0); @@ -658,82 +665,6 @@ ngx_http_alloc_request(ngx_connection_t *c) #if (NGX_HTTP_SSL) -static void -ngx_http_quic_handshake(ngx_event_t *rev) -{ - ngx_connection_t *c; - ngx_http_connection_t *hc; - ngx_http_ssl_srv_conf_t *sscf; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake"); - - c = rev->data; - hc = c->data; - - sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); - - if (ngx_quic_input(c, &sscf->ssl, c->buffer) != NGX_OK) { - ngx_http_close_connection(c); - return; - } - - if (!rev->timer_set) { - ngx_add_timer(rev, c->listening->post_accept_timeout); - } - - rev->handler = ngx_http_quic_handshake_handler; - return; -} - - -static void -ngx_http_quic_handshake_handler(ngx_event_t *rev) -{ - ssize_t n; - ngx_connection_t *c; - u_char buf[512]; - ngx_buf_t b; - - b.start = buf; - b.end = buf + 512; - b.pos = b.last = b.start; - - c = rev->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake handler"); - - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - ngx_http_close_connection(c); - return; - } - - if (c->close) { - ngx_http_close_connection(c); - return; - } - - n = c->recv(c, b.start, b.end - b.start); - - if (n == NGX_AGAIN) { - return; - } - - if (n == NGX_ERROR) { - c->read->eof = 1; - ngx_http_close_connection(c); - return; - } - - b.last += n; - - if (ngx_quic_input(c, NULL, &b) != NGX_OK) { - ngx_http_close_connection(c); - return; - } -} - - static void ngx_http_ssl_handshake(ngx_event_t *rev) { -- cgit From 5bc8cd4044fe49d672607c365929459969a338fc Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 12 Mar 2020 18:08:26 +0300 Subject: Fix build. --- src/http/ngx_http_request.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 7a2c78046..f463e674e 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -395,7 +395,8 @@ ngx_http_quic_stream_handler(ngx_connection_t *c) { ngx_quic_stream_t *qs = c->qs; - printf("quic stream: 0x%lx\n", qs->id); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "quic stream: 0x%uXL", qs->id); } -- cgit From 05d1464c68a269e2f6ed08b5559edb90dfd3ab1f Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Fri, 13 Mar 2020 14:39:23 +0300 Subject: Stream "connection" read/write methods. --- src/http/ngx_http_request.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index f463e674e..55af26bf8 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -395,8 +395,39 @@ ngx_http_quic_stream_handler(ngx_connection_t *c) { ngx_quic_stream_t *qs = c->qs; + // STUB for stream read/write + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "quic stream: 0x%uXL", qs->id); + ssize_t n; + ngx_buf_t b; + + u_char buf[512]; + + b.start = buf; + b.end = buf + 512; + b.pos = b.last = b.start; + + n = c->recv(c, b.pos, b.end - b.start); + if (n < 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream read failed"); + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "quic stream: 0x%uXL %ui bytes read", qs->id, n); + + b.last += n; + + n = c->send(c, b.start, n); + + if (n < 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream write failed"); + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "quic stream: 0x%uXL %ui bytes written", qs->id, n); } -- cgit From 7739b6073b11086d9a3dc4b9744418070e182c33 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 13 Mar 2020 19:36:33 +0300 Subject: HTTP/3. --- src/http/ngx_http.h | 3 + src/http/ngx_http_core_module.c | 6 +- src/http/ngx_http_header_filter_module.c | 15 + src/http/ngx_http_parse.c | 3 +- src/http/ngx_http_request.c | 178 +++-- src/http/ngx_http_request.h | 13 + src/http/v3/ngx_http_v3.c | 176 +++++ src/http/v3/ngx_http_v3.h | 89 +++ src/http/v3/ngx_http_v3_module.c | 46 ++ src/http/v3/ngx_http_v3_request.c | 971 ++++++++++++++++++++++++++ src/http/v3/ngx_http_v3_streams.c | 1097 ++++++++++++++++++++++++++++++ src/http/v3/ngx_http_v3_tables.c | 385 +++++++++++ 12 files changed, 2926 insertions(+), 56 deletions(-) create mode 100644 src/http/v3/ngx_http_v3.c create mode 100644 src/http/v3/ngx_http_v3.h create mode 100644 src/http/v3/ngx_http_v3_module.c create mode 100644 src/http/v3/ngx_http_v3_request.c create mode 100644 src/http/v3/ngx_http_v3_streams.c create mode 100644 src/http/v3/ngx_http_v3_tables.c (limited to 'src/http') diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 8b43857ee..8772001c0 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -38,6 +38,9 @@ typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r, #if (NGX_HTTP_V2) #include #endif +#if (NGX_HTTP_V3) +#include +#endif #if (NGX_HTTP_CACHE) #include #endif diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 5c210bebd..576c679d7 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -809,7 +809,7 @@ ngx_http_handler(ngx_http_request_t *r) if (!r->internal) { switch (r->headers_in.connection_type) { case 0: - r->keepalive = (r->http_version > NGX_HTTP_VERSION_10); + r->keepalive = (r->http_version == NGX_HTTP_VERSION_11); break; case NGX_HTTP_CONNECTION_CLOSE: @@ -4000,14 +4000,14 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } if (ngx_strcmp(value[n].data, "http3") == 0) { -#if (NGX_HTTP_SSL) +#if (NGX_HTTP_V3) lsopt.http3 = 1; lsopt.type = SOCK_DGRAM; continue; #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the \"http3\" parameter requires " - "ngx_http_ssl_module"); + "ngx_http_v3_module"); return NGX_CONF_ERROR; #endif } diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c index 9b8940590..02e251f79 100644 --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -179,6 +179,21 @@ ngx_http_header_filter(ngx_http_request_t *r) return NGX_OK; } +#if (NGX_HTTP_V3) + + if (r->http_version == NGX_HTTP_VERSION_30) { + ngx_chain_t *cl; + + cl = ngx_http_v3_create_header(r); + if (cl == NULL) { + return NGX_ERROR; + } + + return ngx_http_write_filter(r, cl); + } + +#endif + if (r->http_version < NGX_HTTP_VERSION_10) { return NGX_OK; } diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index cfc42f9dd..28aa8b0dd 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -144,6 +144,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) /* HTTP methods: GET, HEAD, POST */ case sw_start: r->request_start = p; + r->method_start = p; if (ch == CR || ch == LF) { break; @@ -158,7 +159,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) case sw_method: if (ch == ' ') { - r->method_end = p - 1; + r->method_end = p; m = r->request_start; switch (p - m) { diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 55af26bf8..ef305faf4 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -64,7 +64,9 @@ static void ngx_http_ssl_handshake(ngx_event_t *rev); static void ngx_http_ssl_handshake_handler(ngx_connection_t *c); #endif +#if (NGX_HTTP_V3) static void ngx_http_quic_stream_handler(ngx_connection_t *c); +#endif static char *ngx_http_client_errors[] = { @@ -219,7 +221,15 @@ ngx_http_init_connection(ngx_connection_t *c) ngx_http_in6_addr_t *addr6; #endif - hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); +#if (NGX_HTTP_V3) + if (c->type == SOCK_DGRAM) { + hc = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t)); + hc->quic = 1; + + } else +#endif + hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); + if (hc == NULL) { ngx_http_close_connection(c); return; @@ -329,11 +339,9 @@ ngx_http_init_connection(ngx_connection_t *c) rev->ready = 1; } -#if (NGX_HTTP_SSL) - if (hc->addr_conf->http3) { - ngx_http_ssl_srv_conf_t *sscf; - - hc->quic = 1; +#if (NGX_HTTP_V3) + if (hc->quic) { + ngx_http_ssl_srv_conf_t *sscf; sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); @@ -390,46 +398,63 @@ ngx_http_init_connection(ngx_connection_t *c) } +#if (NGX_HTTP_V3) + static void ngx_http_quic_stream_handler(ngx_connection_t *c) { - ngx_quic_stream_t *qs = c->qs; + ngx_event_t *rev; + ngx_connection_t *pc; + ngx_http_log_ctx_t *ctx; + ngx_http_connection_t *hc; + ngx_http_v3_connection_t *h3c; - // STUB for stream read/write + pc = c->qs->parent; + h3c = pc->data; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "quic stream: 0x%uXL", qs->id); - ssize_t n; - ngx_buf_t b; + if (c->qs->unidirectional) { + ngx_http_v3_handle_client_uni_stream(c); + return; + } - u_char buf[512]; + hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); + if (hc == NULL) { + ngx_http_close_connection(c); + return; + } - b.start = buf; - b.end = buf + 512; - b.pos = b.last = b.start; + ngx_memcpy(hc, h3c, sizeof(ngx_http_connection_t)); + c->data = hc; - n = c->recv(c, b.pos, b.end - b.start); - if (n < 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream read failed"); + ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); + if (ctx == NULL) { + ngx_http_close_connection(c); return; } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "quic stream: 0x%uXL %ui bytes read", qs->id, n); + ctx->connection = c; + ctx->request = NULL; + ctx->current_request = NULL; - b.last += n; + c->log->connection = c->number; + c->log->handler = ngx_http_log_error; + c->log->data = ctx; + c->log->action = "waiting for request"; - n = c->send(c, b.start, n); + c->log_error = NGX_ERROR_INFO; - if (n < 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream write failed"); - return; - } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 new stream id:0x%uXL", c->qs->id); - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "quic stream: 0x%uXL %ui bytes written", qs->id, n); + rev = c->read; + rev->handler = ngx_http_wait_request_handler; + c->write->handler = ngx_http_empty_handler; + + rev->handler(rev); } +#endif + static void ngx_http_wait_request_handler(ngx_event_t *rev) @@ -679,6 +704,13 @@ ngx_http_alloc_request(ngx_connection_t *c) r->method = NGX_HTTP_UNKNOWN; r->http_version = NGX_HTTP_VERSION_10; +#if (NGX_HTTP_V3) + if (hc->quic) { + r->http_version = NGX_HTTP_VERSION_30; + r->filter_need_in_memory = 1; + } +#endif + r->headers_in.content_length_n = -1; r->headers_in.keep_alive_n = -1; r->headers_out.content_length_n = -1; @@ -1128,7 +1160,16 @@ ngx_http_process_request_line(ngx_event_t *rev) } } - rc = ngx_http_parse_request_line(r, r->header_in); + switch (r->http_version) { +#if (NGX_HTTP_V3) + case NGX_HTTP_VERSION_30: + rc = ngx_http_v3_parse_header(r, r->header_in, 1); + break; +#endif + + default: /* HTTP/1.x */ + rc = ngx_http_parse_request_line(r, r->header_in); + } if (rc == NGX_OK) { @@ -1141,8 +1182,8 @@ ngx_http_process_request_line(ngx_event_t *rev) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http request line: \"%V\"", &r->request_line); - r->method_name.len = r->method_end - r->request_start + 1; - r->method_name.data = r->request_line.data; + r->method_name.len = r->method_end - r->method_start; + r->method_name.data = r->method_start; if (r->http_protocol.data) { r->http_protocol.len = r->request_end - r->http_protocol.data; @@ -1213,6 +1254,15 @@ ngx_http_process_request_line(ngx_event_t *rev) break; } + if (rc == NGX_DONE) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + break; + } + if (rc != NGX_AGAIN) { /* there was error while a request line parsing */ @@ -1403,7 +1453,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - rc = NGX_AGAIN; + rc = NGX_OK; for ( ;; ) { @@ -1457,11 +1507,21 @@ ngx_http_process_request_headers(ngx_event_t *rev) /* the host header could change the server configuration context */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - rc = ngx_http_parse_header_line(r, r->header_in, - cscf->underscores_in_headers); + switch (r->http_version) { +#if (NGX_HTTP_V3) + case NGX_HTTP_VERSION_30: + rc = ngx_http_v3_parse_header(r, r->header_in, 0); + break; +#endif + + default: /* HTTP/1.x */ + rc = ngx_http_parse_header_line(r, r->header_in, + cscf->underscores_in_headers); + } if (rc == NGX_OK) { + /* XXX */ r->request_length += r->header_in->pos - r->header_name_start; if (r->invalid_header && cscf->ignore_invalid_headers) { @@ -1487,11 +1547,11 @@ ngx_http_process_request_headers(ngx_event_t *rev) h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; - h->key.data[h->key.len] = '\0'; + //h->key.data[h->key.len] = '\0'; h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; - h->value.data[h->value.len] = '\0'; + //h->value.data[h->value.len] = '\0'; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { @@ -1642,7 +1702,7 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, return NGX_OK; } - old = request_line ? r->request_start : r->header_name_start; + old = request_line ? r->request_start : r->header_name_start; /* XXX */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); @@ -1721,45 +1781,59 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, r->request_end = new + (r->request_end - old); } - r->method_end = new + (r->method_end - old); + if (r->method_start >= old && r->method_start < r->header_in->pos) { + r->method_start = new + (r->method_start - old); + r->method_end = new + (r->method_end - old); + } - r->uri_start = new + (r->uri_start - old); - r->uri_end = new + (r->uri_end - old); + if (r->uri_start >= old && r->uri_start < r->header_in->pos) { + r->uri_start = new + (r->uri_start - old); + r->uri_end = new + (r->uri_end - old); + } - if (r->schema_start) { + if (r->schema_start >= old && r->schema_start < r->header_in->pos) { r->schema_start = new + (r->schema_start - old); r->schema_end = new + (r->schema_end - old); } - if (r->host_start) { + if (r->host_start >= old && r->host_start < r->header_in->pos) { r->host_start = new + (r->host_start - old); if (r->host_end) { r->host_end = new + (r->host_end - old); } } - if (r->port_start) { + if (r->port_start >= old && r->port_start < r->header_in->pos) { r->port_start = new + (r->port_start - old); r->port_end = new + (r->port_end - old); } - if (r->uri_ext) { + if (r->uri_ext >= old && r->uri_ext < r->header_in->pos) { r->uri_ext = new + (r->uri_ext - old); } - if (r->args_start) { + if (r->args_start >= old && r->args_start < r->header_in->pos) { r->args_start = new + (r->args_start - old); } - if (r->http_protocol.data) { + if (r->http_protocol.data >= old + && r->http_protocol.data < r->header_in->pos) + { r->http_protocol.data = new + (r->http_protocol.data - old); } } else { - r->header_name_start = new; - r->header_name_end = new + (r->header_name_end - old); - r->header_start = new + (r->header_start - old); - r->header_end = new + (r->header_end - old); + if (r->header_name_start >= old + && r->header_name_start < r->header_in->pos) + { + r->header_name_start = new; + r->header_name_end = new + (r->header_name_end - old); + } + + if (r->header_start >= old && r->header_start < r->header_in->pos) { + r->header_start = new + (r->header_start - old); + r->header_end = new + (r->header_end - old); + } } r->header_in = b; @@ -1984,7 +2058,7 @@ ngx_http_process_request_header(ngx_http_request_t *r) return NGX_ERROR; } - if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { + if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent HTTP/1.1 request without \"Host\" header"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 8cc5d6432..04d88db7b 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -24,6 +24,7 @@ #define NGX_HTTP_VERSION_10 1000 #define NGX_HTTP_VERSION_11 1001 #define NGX_HTTP_VERSION_20 2000 +#define NGX_HTTP_VERSION_30 3000 #define NGX_HTTP_UNKNOWN 0x0001 #define NGX_HTTP_GET 0x0002 @@ -584,6 +585,7 @@ struct ngx_http_request_s { u_char *args_start; u_char *request_start; u_char *request_end; + u_char *method_start; u_char *method_end; u_char *schema_start; u_char *schema_end; @@ -592,6 +594,17 @@ struct ngx_http_request_s { u_char *port_start; u_char *port_end; +#if (NGX_HTTP_V3) + ngx_uint_t h3_length; + ngx_uint_t h3_index; + ngx_uint_t h3_insert_count; + ngx_uint_t h3_sign; + ngx_uint_t h3_delta_base; + ngx_uint_t h3_huffman; + ngx_uint_t h3_dynamic; + ngx_uint_t h3_offset; +#endif + unsigned http_minor:16; unsigned http_major:16; }; diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c new file mode 100644 index 000000000..e804cf6f5 --- /dev/null +++ b/src/http/v3/ngx_http_v3.c @@ -0,0 +1,176 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +uintptr_t +ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value) +{ + if (value <= 63) { + if (p == NULL) { + return 1; + } + + *p++ = value; + return (uintptr_t) p; + } + + if (value <= 16383) { + if (p == NULL) { + return 2; + } + + *p++ = 0x40 | (value >> 8); + *p++ = value; + return (uintptr_t) p; + } + + if (value <= 1073741823) { + if (p == NULL) { + return 3; + } + + *p++ = 0x80 | (value >> 16); + *p++ = (value >> 8); + *p++ = value; + return (uintptr_t) p; + + } + + if (p == NULL) { + return 4; + } + + *p++ = 0xc0 | (value >> 24); + *p++ = (value >> 16); + *p++ = (value >> 8); + *p++ = value; + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix) +{ + ngx_uint_t thresh, n; + + thresh = (1 << prefix) - 1; + + if (value < thresh) { + if (p == NULL) { + return 1; + } + + *p++ |= value; + return (uintptr_t) p; + } + + value -= thresh; + + for (n = 10; n > 1; n--) { + if (value >> (7 * (n - 1))) { + break; + } + } + + if (p == NULL) { + return n + 1; + } + + *p++ |= thresh; + + for ( /* void */ ; n > 1; n--) { + *p++ = 0x80 | (value >> 7 * (n - 1)); + } + + *p++ = value & 0x7f; + + return (uintptr_t) p; +} + + +uint64_t +ngx_http_v3_decode_varlen_int(u_char *p) +{ + uint64_t value; + ngx_uint_t len; + + len = *p >> 6; + value = *p & 0x3f; + + while (len--) { + value = (value << 8) + *p++; + } + + return value; +} + + +int64_t +ngx_http_v3_decode_prefix_int(u_char **src, size_t len, ngx_uint_t prefix) +{ + u_char *p; + int64_t value, thresh; + + if (len == 0) { + return NGX_ERROR; + } + + p = *src; + + thresh = (1 << prefix) - 1; + value = *p++ & thresh; + + if (value != thresh) { + *src = p; + return value; + } + + value = 0; + + /* XXX handle overflows */ + + while (--len) { + value = (value << 7) + (*p & 0x7f); + if ((*p++ & 0x80) == 0) { + *src = p; + return value + thresh; + } + } + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_decode_huffman(ngx_connection_t *c, ngx_str_t *s) +{ + u_char state, *p, *data; + + state = 0; + + p = ngx_pnalloc(c->pool, s->len * 8 / 5); + if (p == NULL) { + return NGX_ERROR; + } + + data = p; + + if (ngx_http_v2_huff_decode(&state, s->data, s->len, &p, 1, c->log) + != NGX_OK) + { + return NGX_ERROR; + } + + s->len = p - data; + s->data = data; + + return NGX_OK; +} diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h new file mode 100644 index 000000000..8fa54d8e5 --- /dev/null +++ b/src/http/v3/ngx_http_v3.h @@ -0,0 +1,89 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_H_INCLUDED_ +#define _NGX_HTTP_V3_H_INCLUDED_ + + +#include +#include +#include + + +#define NGX_HTTP_V3_STREAM 0x48335354 /* "H3ST" */ + + +#define NGX_HTTP_V3_VARLEN_INT_LEN 4 +#define NGX_HTTP_V3_PREFIX_INT_LEN 11 + + +typedef struct { + ngx_http_connection_t hc; + + ngx_array_t *dynamic; + + ngx_connection_t *client_encoder; + ngx_connection_t *client_decoder; + ngx_connection_t *server_encoder; + ngx_connection_t *server_decoder; +} ngx_http_v3_connection_t; + + +typedef struct { + ngx_str_t name; + ngx_str_t value; +} ngx_http_v3_header_t; + + +ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, + ngx_uint_t pseudo); +ngx_chain_t *ngx_http_v3_create_header(ngx_http_request_t *r); + + +uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); +uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, + ngx_uint_t prefix); +uint64_t ngx_http_v3_decode_varlen_int(u_char *p); +int64_t ngx_http_v3_decode_prefix_int(u_char **src, size_t len, + ngx_uint_t prefix); +ngx_int_t ngx_http_v3_decode_huffman(ngx_connection_t *c, ngx_str_t *s); + +void ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c); + +ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value); +ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value); +ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); +ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index); +ngx_int_t ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); +ngx_http_v3_header_t *ngx_http_v3_lookup_table(ngx_connection_t *c, + ngx_uint_t dynamic, ngx_uint_t index); +ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, + ngx_uint_t insert_count); + +ngx_int_t ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value); +ngx_int_t ngx_http_v3_client_insert(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value); +ngx_int_t ngx_http_v3_client_set_capacity(ngx_connection_t *c, + ngx_uint_t capacity); +ngx_int_t ngx_http_v3_client_duplicate(ngx_connection_t *c, ngx_uint_t index); +ngx_int_t ngx_http_v3_client_ack_header(ngx_connection_t *c, + ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_client_cancel_stream(ngx_connection_t *c, + ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_client_inc_insert_count(ngx_connection_t *c, + ngx_uint_t inc); + + +extern ngx_module_t ngx_http_v3_module; + + +#endif /* _NGX_HTTP_V3_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c new file mode 100644 index 000000000..af310bd44 --- /dev/null +++ b/src/http/v3/ngx_http_v3_module.c @@ -0,0 +1,46 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Roman Arutyunyan + */ + + +#include +#include +#include + + +static ngx_command_t ngx_http_v3_commands[] = { + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_v3_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_v3_module = { + NGX_MODULE_V1, + &ngx_http_v3_module_ctx, /* module context */ + ngx_http_v3_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c new file mode 100644 index 000000000..b34eed98e --- /dev/null +++ b/src/http/v3/ngx_http_v3_request.c @@ -0,0 +1,971 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_HTTP_V3_FRAME_DATA 0x00 +#define NGX_HTTP_V3_FRAME_HEADERS 0x01 +#define NGX_HTTP_V3_FRAME_CANCEL_PUSH 0x03 +#define NGX_HTTP_V3_FRAME_SETTINGS 0x04 +#define NGX_HTTP_V3_FRAME_PUSH_PROMISE 0x05 +#define NGX_HTTP_V3_FRAME_GOAWAY 0x07 +#define NGX_HTTP_V3_FRAME_MAX_PUSH_ID 0x0d + + +static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, + ngx_str_t *name, ngx_str_t *value); + + +struct { + ngx_str_t name; + ngx_uint_t method; +} ngx_http_v3_methods[] = { + + { ngx_string("GET"), NGX_HTTP_GET }, + { ngx_string("POST"), NGX_HTTP_POST }, + { ngx_string("HEAD"), NGX_HTTP_HEAD }, + { ngx_string("OPTIONS"), NGX_HTTP_OPTIONS }, + { ngx_string("PROPFIND"), NGX_HTTP_PROPFIND }, + { ngx_string("PUT"), NGX_HTTP_PUT }, + { ngx_string("MKCOL"), NGX_HTTP_MKCOL }, + { ngx_string("DELETE"), NGX_HTTP_DELETE }, + { ngx_string("COPY"), NGX_HTTP_COPY }, + { ngx_string("MOVE"), NGX_HTTP_MOVE }, + { ngx_string("PROPPATCH"), NGX_HTTP_PROPPATCH }, + { ngx_string("LOCK"), NGX_HTTP_LOCK }, + { ngx_string("UNLOCK"), NGX_HTTP_UNLOCK }, + { ngx_string("PATCH"), NGX_HTTP_PATCH }, + { ngx_string("TRACE"), NGX_HTTP_TRACE } +}; + + +ngx_int_t +ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t pseudo) +{ + u_char *p, ch; + ngx_str_t name, value; + ngx_int_t rc; + ngx_uint_t length, index, insert_count, sign, base, delta_base, + huffman, dynamic, offset; + ngx_connection_t *c; + ngx_http_v3_header_t *h; + enum { + sw_start = 0, + sw_length, + sw_length_1, + sw_length_2, + sw_length_3, + sw_header_block, + sw_req_insert_count, + sw_delta_base, + sw_read_delta_base, + sw_header, + sw_old_header, + sw_header_ri, + sw_header_pbi, + sw_header_lri, + sw_header_lpbi, + sw_header_l_name_len, + sw_header_l_name, + sw_header_value_len, + sw_header_read_value_len, + sw_header_value + } state; + + c = r->connection; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header, pseudo:%ui", pseudo); + + if (r->state == sw_old_header) { + r->state = sw_header; + return NGX_OK; + } + + length = r->h3_length; + index = r->h3_index; + insert_count = r->h3_insert_count; + sign = r->h3_sign; + delta_base = r->h3_delta_base; + huffman = r->h3_huffman; + dynamic = r->h3_dynamic; + offset = r->h3_offset; + + name.data = r->header_name_start; + name.len = r->header_name_end - r->header_name_start; + value.data = r->header_start; + value.len = r->header_end - r->header_start; + + if (r->state == sw_start) { + length = 1; + } + +again: + + state = r->state; + + if (state == sw_header && length == 0) { + r->state = sw_start; + return NGX_HTTP_PARSE_HEADER_DONE; + } + + for (p = b->pos; p < b->last; p++) { + + if (state >= sw_header_block && length-- == 0) { + goto failed; + } + + ch = *p; + + switch (state) { + + case sw_start: + + if (ch != NGX_HTTP_V3_FRAME_HEADERS) { + goto failed; + } + + r->request_start = p; + state = sw_length; + break; + + case sw_length: + + length = ch; + if (length & 0xc0) { + state = sw_length_1; + break; + } + + state = sw_header_block; + break; + + case sw_length_1: + + length = (length << 8) + ch; + if ((length & 0xc000) != 0x4000) { + state = sw_length_2; + break; + } + + length &= 0x3fff; + state = sw_header_block; + break; + + case sw_length_2: + + length = (length << 8) + ch; + if ((length & 0xc00000) != 0x800000) { + state = sw_length_3; + break; + } + + /* fall through */ + + case sw_length_3: + + length &= 0x3fffff; + state = sw_header_block; + break; + + case sw_header_block: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 header block length:%ui", length); + + if (ch != 0xff) { + insert_count = ch; + state = sw_delta_base; + break; + } + + insert_count = 0; + state = sw_req_insert_count; + break; + + case sw_req_insert_count: + + insert_count = (insert_count << 7) + (ch & 0x7f); + if (ch & 0x80) { + break; + } + + insert_count += 0xff; + state = sw_delta_base; + break; + + case sw_delta_base: + + sign = (ch & 0x80) ? 1 : 0; + delta_base = ch & 0x7f; + + if (delta_base != 0x7f) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 header block " + "insert_count:%ui, sign:%ui, delta_base:%ui", + insert_count, sign, delta_base); + goto done; + } + + delta_base = 0; + state = sw_read_delta_base; + break; + + case sw_read_delta_base: + + delta_base = (delta_base << 7) + (ch & 0x7f); + if (ch & 0x80) { + break; + } + + delta_base += 0x7f; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 header block " + "insert_count:%ui, sign:%ui, delta_base:%ui", + insert_count, sign, delta_base); + goto done; + + case sw_header: + + index = 0; + huffman = 0; + ngx_str_null(&name); + ngx_str_null(&value); + + if (ch & 0x80) { + /* Indexed Header Field */ + + dynamic = (ch & 0x40) ? 0 : 1; + index = ch & 0x3f; + + if (index != 0x3f) { + goto done; + } + + index = 0; + state = sw_header_ri; + break; + } + + if (ch & 0x40) { + /* Literal Header Field With Name Reference */ + + dynamic = (ch & 0x10) ? 0 : 1; + index = ch & 0x0f; + + if (index != 0x0f) { + state = sw_header_value_len; + break; + } + + index = 0; + state = sw_header_lri; + break; + } + + if (ch & 0x20) { + /* Literal Header Field Without Name Reference */ + + huffman = (ch & 0x08) ? 1 : 0; + name.len = ch & 0x07; + + if (name.len == 0) { + goto failed; + } + + if (name.len != 0x07) { + offset = 0; + state = sw_header_l_name; + break; + } + + name.len = 0; + state = sw_header_l_name_len; + break; + } + + if (ch & 10) { + /* Indexed Header Field With Post-Base Index */ + + dynamic = 2; + index = ch & 0x0f; + + if (index != 0x0f) { + goto done; + } + + index = 0; + state = sw_header_pbi; + break; + } + + /* Literal Header Field With Post-Base Name Reference */ + + dynamic = 2; + index = ch & 0x07; + + if (index != 0x07) { + state = sw_header_value_len; + break; + } + + index = 0; + state = sw_header_lpbi; + break; + + case sw_header_ri: + + index = (index << 7) + (ch & 0x7f); + if (ch & 0x80) { + break; + } + + index += 0x3f; + goto done; + + case sw_header_pbi: + + index = (index << 7) + (ch & 0x7f); + if (ch & 0x80) { + break; + } + + index += 0x0f; + goto done; + + case sw_header_lri: + + index = (index << 7) + (ch & 0x7f); + if (ch & 0x80) { + break; + } + + index += 0x0f; + state = sw_header_value_len; + break; + + case sw_header_lpbi: + + index = (index << 7) + (ch & 0x7f); + if (ch & 0x80) { + break; + } + + index += 0x07; + state = sw_header_value_len; + break; + + + case sw_header_l_name_len: + + name.len = (name.len << 7) + (ch & 0x7f); + if (ch & 0x80) { + break; + } + + name.len += 0x07; + offset = 0; + state = sw_header_l_name; + break; + + case sw_header_l_name: + if (offset++ == 0) { + name.data = p; + } + + if (offset != name.len) { + break; + } + + if (huffman) { + if (ngx_http_v3_decode_huffman(c, &name) != NGX_OK) { + goto failed; + } + } + + state = sw_header_value_len; + break; + + case sw_header_value_len: + + huffman = (ch & 0x80) ? 1 : 0; + value.len = ch & 0x7f; + + if (value.len == 0) { + value.data = p; + goto done; + } + + if (value.len != 0x7f) { + offset = 0; + state = sw_header_value; + break; + } + + value.len = 0; + state = sw_header_read_value_len; + break; + + case sw_header_read_value_len: + + value.len = (value.len << 7) + (ch & 0x7f); + if (ch & 0x80) { + break; + } + + value.len += 0x7f; + offset = 0; + state = sw_header_value; + break; + + case sw_header_value: + + if (offset++ == 0) { + value.data = p; + } + + if (offset != value.len) { + break; + } + + if (huffman) { + if (ngx_http_v3_decode_huffman(c, &value) != NGX_OK) { + goto failed; + } + } + + goto done; + + case sw_old_header: + + break; + } + } + + b->pos = p; + r->state = state; + r->h3_length = length; + r->h3_index = index; + r->h3_insert_count = insert_count; + r->h3_sign = sign; + r->h3_delta_base = delta_base; + r->h3_huffman = huffman; + r->h3_dynamic = dynamic; + r->h3_offset = offset; + + /* XXX fix large reallocations */ + r->header_name_start = name.data; + r->header_name_end = name.data + name.len; + r->header_start = value.data; + r->header_end = value.data + value.len; + + /* XXX r->lowcase_index = i; */ + + return NGX_AGAIN; + +done: + + b->pos = p + 1; + r->state = sw_header; + r->h3_length = length; + r->h3_insert_count = insert_count; + r->h3_sign = sign; + r->h3_delta_base = delta_base; + + if (state < sw_header) { + if (ngx_http_v3_check_insert_count(c, insert_count) != NGX_OK) { + return NGX_DONE; + } + + goto again; + } + + if (sign == 0) { + base = insert_count + delta_base; + } else { + base = insert_count - delta_base - 1; + } + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 header %s[%ui], base:%ui, \"%V\":\"%V\"", + dynamic ? "dynamic" : "static", index, base, &name, &value); + + if (name.data == NULL) { + + if (dynamic == 2) { + index = base - index - 1; + } else if (dynamic == 1) { + index += base; + } + + h = ngx_http_v3_lookup_table(c, dynamic, index); + if (h == NULL) { + goto failed; + } + + name = h->name; + + if (value.data == NULL) { + value = h->value; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 header \"%V\":\"%V\"", &name, &value); + + if (pseudo) { + rc = ngx_http_v3_process_pseudo_header(r, &name, &value); + + if (rc == NGX_ERROR) { + goto failed; + } + + if (rc == NGX_OK) { + r->request_end = p + 1; + goto again; + } + + /* rc == NGX_DONE */ + + r->state = sw_old_header; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 header left:%ui", length); + + r->header_name_start = name.data; + r->header_name_end = name.data + name.len; + r->header_start = value.data; + r->header_end = value.data + value.len; + r->header_hash = ngx_hash_key(name.data, name.len); /* XXX */ + + /* XXX r->lowcase_index = i; */ + + return NGX_OK; + +failed: + + return NGX_HTTP_PARSE_INVALID_REQUEST; +} + + +static ngx_int_t +ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, + ngx_str_t *value) +{ + ngx_uint_t i; + ngx_connection_t *c; + + c = r->connection; + + if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) { + r->method_start = value->data; + r->method_end = value->data + value->len; + + for (i = 0; i < sizeof(ngx_http_v3_methods) + / sizeof(ngx_http_v3_methods[0]); i++) + { + if (value->len == ngx_http_v3_methods[i].name.len + && ngx_strncmp(value->data, ngx_http_v3_methods[i].name.data, + value->len) == 0) + { + r->method = ngx_http_v3_methods[i].method; + break; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 method \"%V\" %ui", value, r->method); + return NGX_OK; + } + + if (name->len == 5 && ngx_strncmp(name->data, ":path", 5) == 0) { + r->uri_start = value->data; + r->uri_end = value->data + value->len; + + if (ngx_http_parse_uri(r) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid :path header: \"%V\"", value); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 path \"%V\"", value); + + return NGX_OK; + } + + if (name->len == 7 && ngx_strncmp(name->data, ":scheme", 7) == 0) { + r->schema_start = value->data; + r->schema_end = value->data + value->len; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 schema \"%V\"", value); + + return NGX_OK; + } + + if (name->len == 10 && ngx_strncmp(name->data, ":authority", 10) == 0) { + r->host_start = value->data; + r->host_end = value->data + value->len; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 authority \"%V\"", value); + + return NGX_OK; + } + + if (name->len && name->data[0] == ':') { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 unknown pseudo header \"%V\" \"%V\"", + name, value); + return NGX_OK; + } + + return NGX_DONE; +} + + +ngx_chain_t * +ngx_http_v3_create_header(ngx_http_request_t *r) +{ + u_char *p; + size_t len, hlen, n; + ngx_buf_t *b; + ngx_uint_t i, j; + ngx_chain_t *hl, *cl, *bl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + + c = r->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create header"); + + /* XXX support chunked body in the chunked filter */ + if (r->headers_out.content_length_n == -1) { + return NULL; + } + + len = 0; + + if (r->headers_out.status == NGX_HTTP_OK) { + len += ngx_http_v3_encode_prefix_int(NULL, 25, 6); + + } else { + len += 3 + ngx_http_v3_encode_prefix_int(NULL, 25, 4) + + ngx_http_v3_encode_prefix_int(NULL, 3, 7); + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->headers_out.server == NULL) { + if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { + n = sizeof(NGINX_VER) - 1; + + } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { + n = sizeof(NGINX_VER_BUILD) - 1; + + } else { + n = sizeof("nginx") - 1; + } + + len += ngx_http_v3_encode_prefix_int(NULL, 92, 4) + + ngx_http_v3_encode_prefix_int(NULL, n, 7) + n; + } + + if (r->headers_out.date == NULL) { + len += ngx_http_v3_encode_prefix_int(NULL, 6, 4) + + ngx_http_v3_encode_prefix_int(NULL, ngx_cached_http_time.len, + 7) + + ngx_cached_http_time.len; + } + + if (r->headers_out.content_type.len) { + n = r->headers_out.content_type.len; + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + n += sizeof("; charset=") - 1 + r->headers_out.charset.len; + } + + len += ngx_http_v3_encode_prefix_int(NULL, 53, 4) + + ngx_http_v3_encode_prefix_int(NULL, n, 7) + n; + } + + if (r->headers_out.content_length_n == 0) { + len += ngx_http_v3_encode_prefix_int(NULL, 4, 6); + + } else { + len += ngx_http_v3_encode_prefix_int(NULL, 4, 4) + 1 + NGX_OFF_T_LEN; + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + len += ngx_http_v3_encode_prefix_int(NULL, 10, 4) + 1 + + sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT"); + } + + /* XXX location */ + +#if (NGX_HTTP_GZIP) + if (r->gzip_vary) { + if (clcf->gzip_vary) { + /* Vary: Accept-Encoding */ + len += ngx_http_v3_encode_prefix_int(NULL, 59, 6); + + } else { + r->gzip_vary = 0; + } + } +#endif + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += ngx_http_v3_encode_prefix_int(NULL, header[i].key.len, 3) + + header[i].key.len + + ngx_http_v3_encode_prefix_int(NULL, header[i].value.len, 7 ) + + header[i].value.len; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NULL; + } + + *b->last++ = 0; + *b->last++ = 0; + + if (r->headers_out.status == NGX_HTTP_OK) { + /* :status: 200 */ + *b->last = 0xc0; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 6); + + } else { + /* :status: 200 */ + *b->last = 0x70; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 4); + *b->last = 0; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 3, 7); + b->last = ngx_sprintf(b->last, "%03ui ", r->headers_out.status); + } + + if (r->headers_out.server == NULL) { + if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { + p = (u_char *) NGINX_VER; + n = sizeof(NGINX_VER) - 1; + + } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { + p = (u_char *) NGINX_VER_BUILD; + n = sizeof(NGINX_VER_BUILD) - 1; + + } else { + p = (u_char *) "nginx"; + n = sizeof("nginx") - 1; + } + + /* server */ + *b->last = 0x70; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 92, 4); + *b->last = 0; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, n, 7); + b->last = ngx_cpymem(b->last, p, n); + } + + if (r->headers_out.date == NULL) { + /* date */ + *b->last = 0x70; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 6, 4); + *b->last = 0; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, + ngx_cached_http_time.len, 7); + b->last = ngx_cpymem(b->last, ngx_cached_http_time.data, + ngx_cached_http_time.len); + } + + if (r->headers_out.content_type.len) { + n = r->headers_out.content_type.len; + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + n += sizeof("; charset=") - 1 + r->headers_out.charset.len; + } + + /* content-type: text/plain */ + *b->last = 0x70; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 53, 4); + *b->last = 0; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, n, 7); + + p = b->last; + b->last = ngx_copy(b->last, r->headers_out.content_type.data, + r->headers_out.content_type.len); + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + b->last = ngx_cpymem(b->last, "; charset=", + sizeof("; charset=") - 1); + b->last = ngx_copy(b->last, r->headers_out.charset.data, + r->headers_out.charset.len); + + /* update r->headers_out.content_type for possible logging */ + + r->headers_out.content_type.len = b->last - p; + r->headers_out.content_type.data = p; + } + } + + if (r->headers_out.content_length_n == 0) { + /* content-length: 0 */ + *b->last = 0xc0; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 6); + + } else if (r->headers_out.content_length_n > 0) { + /* content-length: 0 */ + *b->last = 0x70; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 4); + p = b->last++; + b->last = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); + *p = b->last - p - 1; + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + /* last-modified */ + *b->last = 0x70; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 10, 4); + p = b->last++; + b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); + *p = b->last - p - 1; + } + +#if (NGX_HTTP_GZIP) + if (r->gzip_vary) { + /* vary: accept-encoding */ + *b->last = 0xc0; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 59, 6); + } +#endif + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + *b->last = 0x30; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, + header[i].key.len, + 3); + for (j = 0; j < header[i].key.len; j++) { + *b->last++ = ngx_tolower(header[i].key.data[j]); + } + + *b->last = 0; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, + header[i].value.len, + 7); + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + } + + cl = ngx_alloc_chain_link(c->pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = b; + cl->next = NULL; + + n = b->last - b->pos; + + len = 1 + ngx_http_v3_encode_varlen_int(NULL, n); + + b = ngx_create_temp_buf(c->pool, len); + if (b == NULL) { + return NULL; + } + + *b->last++ = NGX_HTTP_V3_FRAME_HEADERS; + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); + + hl = ngx_alloc_chain_link(c->pool); + if (hl == NULL) { + return NULL; + } + + hl->buf = b; + hl->next = cl; + + hlen = 1 + ngx_http_v3_encode_varlen_int(NULL, len); + + if (r->headers_out.content_length_n >= 0) { + len = 1 + ngx_http_v3_encode_varlen_int(NULL, + r->headers_out.content_length_n); + + b = ngx_create_temp_buf(c->pool, len); + if (b == NULL) { + NULL; + } + + *b->last++ = NGX_HTTP_V3_FRAME_DATA; + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + r->headers_out.content_length_n); + + bl = ngx_alloc_chain_link(c->pool); + if (bl == NULL) { + return NULL; + } + + bl->buf = b; + bl->next = NULL; + cl->next = bl; + } + + return hl; +} diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c new file mode 100644 index 000000000..2b757d81f --- /dev/null +++ b/src/http/v3/ngx_http_v3_streams.c @@ -0,0 +1,1097 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_HTTP_V3_CONTROL_STREAM 0x00 +#define NGX_HTTP_V3_PUSH_STREAM 0x01 +#define NGX_HTTP_V3_ENCODER_STREAM 0x02 +#define NGX_HTTP_V3_DECODER_STREAM 0x03 + + +typedef struct { + uint32_t signature; /* QSTR */ + u_char buf[4]; + + ngx_uint_t len; + ngx_uint_t type; + ngx_uint_t state; + ngx_uint_t index; + ngx_uint_t offset; + + ngx_str_t name; + ngx_str_t value; + + unsigned client:1; + unsigned dynamic:1; + unsigned huffman:1; +} ngx_http_v3_uni_stream_t; + + +static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); +static void ngx_http_v3_uni_stream_cleanup(void *data); +static void ngx_http_v3_read_uni_stream_type(ngx_event_t *rev); +static void ngx_http_v3_dummy_stream_handler(ngx_event_t *rev); +static void ngx_http_v3_client_encoder_handler(ngx_event_t *rev); +static void ngx_http_v3_client_decoder_handler(ngx_event_t *rev); + +static ngx_connection_t *ngx_http_v3_create_uni_stream(ngx_connection_t *c, + ngx_uint_t type); +static ngx_connection_t *ngx_http_v3_get_server_encoder(ngx_connection_t *c); +static ngx_connection_t *ngx_http_v3_get_server_decoder(ngx_connection_t *c); + + +void +ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) +{ + ngx_pool_cleanup_t *cln; + ngx_http_v3_uni_stream_t *us; + + c->log->connection = c->number; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 new uni stream id:0x%uXL", c->qs->id); + + us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); + if (us == NULL) { + ngx_http_v3_close_uni_stream(c); + return; + } + + us->signature = NGX_HTTP_V3_STREAM; + us->client = 1; + us->type = (ngx_uint_t) -1; + + c->data = us; + + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + ngx_http_v3_close_uni_stream(c); + return; + } + + cln->handler = ngx_http_v3_uni_stream_cleanup; + cln->data = c; + + c->read->handler = ngx_http_v3_read_uni_stream_type; + c->read->handler(c->read); +} + + +static void +ngx_http_v3_close_uni_stream(ngx_connection_t *c) +{ + ngx_pool_t *pool; + + c->destroyed = 1; + + pool = c->pool; + + ngx_close_connection(c); + + ngx_destroy_pool(pool); +} + + +static void +ngx_http_v3_uni_stream_cleanup(void *data) +{ + ngx_connection_t *c = data; + + ngx_http_v3_connection_t *h3c; + ngx_http_v3_uni_stream_t *us; + + us = c->data; + h3c = c->qs->parent->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 close stream"); + + switch (us->type) { + + case NGX_HTTP_V3_ENCODER_STREAM: + + if (us->client) { + h3c->client_encoder = NULL; + } else { + h3c->server_encoder = NULL; + } + + break; + + case NGX_HTTP_V3_DECODER_STREAM: + + if (us->client) { + h3c->client_decoder = NULL; + } else { + h3c->server_decoder = NULL; + } + + break; + } +} + + +static void +ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) +{ + u_char *p; + ssize_t n, len; + ngx_connection_t *c; + ngx_http_v3_connection_t *h3c; + ngx_http_v3_uni_stream_t *us; + + c = rev->data; + us = c->data; + h3c = c->qs->parent->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read stream type"); + + while (rev->ready) { + + p = &us->buf[us->len]; + + if (us->len == 0) { + len = 1; + } else { + len = (us->buf[0] >> 6) + 1 - us->len; + } + + n = c->recv(c, p, len); + + if (n == NGX_ERROR) { + goto failed; + } + + if (n == NGX_AGAIN) { + break; + } + + us->len += n; + + if (n != len) { + break; + } + + if ((us->buf[0] >> 6) + 1 == us->len) { + us->type = ngx_http_v3_decode_varlen_int(us->buf); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 stream type:%ui", us->type); + + switch (us->type) { + + case NGX_HTTP_V3_ENCODER_STREAM: + if (h3c->client_encoder) { + goto failed; + } + + h3c->client_encoder = c; + rev->handler = ngx_http_v3_client_encoder_handler; + break; + + case NGX_HTTP_V3_DECODER_STREAM: + if (h3c->client_decoder) { + goto failed; + } + + h3c->client_decoder = c; + rev->handler = ngx_http_v3_client_decoder_handler; + break; + + case NGX_HTTP_V3_CONTROL_STREAM: + case NGX_HTTP_V3_PUSH_STREAM: + + /* ignore these */ + + default: + rev->handler = ngx_http_v3_dummy_stream_handler; + } + + rev->handler(rev); + return; + } + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + goto failed; + } + + return; + +failed: + + ngx_http_v3_close_uni_stream(c); +} + + +static void +ngx_http_v3_dummy_stream_handler(ngx_event_t *rev) +{ + u_char buf[128]; + ngx_connection_t *c; + + /* read out and ignore */ + + c = rev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy stream reader"); + + while (rev->ready) { + if (c->recv(c, buf, sizeof(buf)) == NGX_ERROR) { + goto failed; + } + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + goto failed; + } + + return; + +failed: + + ngx_http_v3_close_uni_stream(c); +} + + +static void +ngx_http_v3_client_encoder_handler(ngx_event_t *rev) +{ + u_char v; + ssize_t n; + ngx_str_t name, value; + ngx_uint_t dynamic, huffman, index, offset; + ngx_connection_t *c, *pc; + ngx_http_v3_uni_stream_t *st; + enum { + sw_start = 0, + sw_inr_name_index, + sw_inr_value_length, + sw_inr_read_value_length, + sw_inr_value, + sw_iwnr_name_length, + sw_iwnr_name, + sw_iwnr_value_length, + sw_iwnr_read_value_length, + sw_iwnr_value, + sw_capacity, + sw_duplicate + } state; + + c = rev->data; + st = c->data; + pc = c->qs->parent; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client encoder"); + + state = st->state; + dynamic = st->dynamic; + huffman = st->huffman; + index = st->index; + offset = st->offset; + name = st->name; + value = st->value; + + while (rev->ready) { + + /* XXX limit checks */ + /* XXX buffer input */ + + n = c->recv(c, &v, 1); + + if (n == NGX_ERROR || n == 0) { + goto failed; + } + + if (n != 1) { + break; + } + + /* XXX v -> ch */ + + switch (state) { + + case sw_start: + + if (v & 0x80) { + /* Insert With Name Reference */ + + dynamic = (v & 0x40) ? 0 : 1; + index = v & 0x3f; + + if (index != 0x3f) { + state = sw_inr_value_length; + break; + } + + index = 0; + state = sw_inr_name_index; + break; + } + + if (v & 0x40) { + /* Insert Without Name Reference */ + + huffman = (v & 0x20) ? 1 : 0; + name.len = v & 0x1f; + + if (name.len != 0x1f) { + offset = 0; + state = sw_iwnr_name; + break; + } + + name.len = 0; + state = sw_iwnr_name_length; + break; + } + + if (v & 0x20) { + /* Set Dynamic Table Capacity */ + + index = v & 0x1f; + + if (index != 0x1f) { + if (ngx_http_v3_set_capacity(c, index) != NGX_OK) { + goto failed; + } + + break; + } + + index = 0; + state = sw_capacity; + break; + } + + /* Duplicate */ + + index = v & 0x1f; + + if (index != 0x1f) { + if (ngx_http_v3_duplicate(c, index) != NGX_OK) { + goto failed; + } + + break; + } + + index = 0; + state = sw_duplicate; + break; + + case sw_inr_name_index: + + index = (index << 7) + (v & 0x7f); + if (v & 0x80) { + break; + } + + index += 0x3f; + state = sw_inr_value_length; + break; + + case sw_inr_value_length: + + huffman = (v & 0x80) ? 1 : 0; + value.len = v & 0x7f; + + if (value.len == 0) { + value.data = NULL; + + if (ngx_http_v3_ref_insert(c, dynamic, index, &value) != NGX_OK) + { + goto failed; + } + + state = sw_start; + break; + } + + if (value.len != 0x7f) { + value.data = ngx_pnalloc(pc->pool, value.len); + if (value.data == NULL) { + goto failed; + } + + state = sw_inr_value; + offset = 0; + break; + } + + value.len = 0; + state = sw_inr_read_value_length; + break; + + case sw_inr_read_value_length: + + value.len = (value.len << 7) + (v & 0x7f); + if (v & 0x80) { + break; + } + + value.len += 0x7f; + + value.data = ngx_pnalloc(pc->pool, value.len); + if (value.data == NULL) { + goto failed; + } + + state = sw_inr_value; + offset = 0; + break; + + case sw_inr_value: + + value.data[offset++] = v; + if (offset != value.len) { + break; + } + + if (huffman) { + if (ngx_http_v3_decode_huffman(pc, &value) != NGX_OK) { + goto failed; + } + } + + if (ngx_http_v3_ref_insert(c, dynamic, index, &value) != NGX_OK) { + goto failed; + } + + state = sw_start; + break; + + case sw_iwnr_name_length: + + name.len = (name.len << 7) + (v & 0x7f); + if (v & 0x80) { + break; + } + + name.len += 0x1f; + + name.data = ngx_pnalloc(pc->pool, name.len); + if (name.data == NULL) { + goto failed; + } + + offset = 0; + state = sw_iwnr_name; + break; + + case sw_iwnr_name: + + name.data[offset++] = v; + if (offset != name.len) { + break; + } + + if (huffman) { + if (ngx_http_v3_decode_huffman(pc, &name) != NGX_OK) { + goto failed; + } + } + + state = sw_iwnr_value_length; + break; + + case sw_iwnr_value_length: + + huffman = (v & 0x80) ? 1 : 0; + value.len = v & 0x7f; + + if (value.len == 0) { + value.data = NULL; + + if (ngx_http_v3_insert(c, &name, &value) != NGX_OK) { + goto failed; + } + + state = sw_start; + break; + } + + if (value.len != 0x7f) { + value.data = ngx_pnalloc(pc->pool, value.len); + if (value.data == NULL) { + goto failed; + } + + offset = 0; + state = sw_iwnr_value; + break; + } + + state = sw_iwnr_read_value_length; + break; + + case sw_iwnr_read_value_length: + + value.len = (value.len << 7) + (v & 0x7f); + if (v & 0x80) { + break; + } + + value.data = ngx_pnalloc(pc->pool, value.len); + if (value.data == NULL) { + goto failed; + } + + offset = 0; + state = sw_iwnr_value; + break; + + case sw_iwnr_value: + + value.data[offset++] = v; + if (offset != value.len) { + break; + } + + if (huffman) { + if (ngx_http_v3_decode_huffman(pc, &value) != NGX_OK) { + goto failed; + } + } + + if (ngx_http_v3_insert(c, &name, &value) != NGX_OK) { + goto failed; + } + + state = sw_start; + break; + + + case sw_capacity: + + index = (index << 7) + (v & 0x7f); + if (v & 0x80) { + break; + } + + index += 0x1f; + + if (ngx_http_v3_set_capacity(c, index) != NGX_OK) { + goto failed; + } + + state = sw_start; + break; + + case sw_duplicate: + + index = (index << 7) + (v & 0x7f); + if (v & 0x80) { + break; + } + + index += 0x1f; + + if (ngx_http_v3_duplicate(c, index) != NGX_OK) { + goto failed; + } + + state = sw_start; + break; + } + } + + st->state = state; + st->dynamic = dynamic; + st->huffman = huffman; + st->index = index; + st->offset = offset; + st->name = name; + st->value = value; + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + goto failed; + } + + return; + +failed: + + ngx_http_v3_close_uni_stream(c); +} + + +static void +ngx_http_v3_client_decoder_handler(ngx_event_t *rev) +{ + u_char v; + ssize_t n; + ngx_uint_t index; + ngx_connection_t *c; + ngx_http_v3_uni_stream_t *st; + enum { + sw_start = 0, + sw_ack_header, + sw_cancel_stream, + sw_inc_insert_count + } state; + + c = rev->data; + st = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client decoder"); + + state = st->state; + index = st->index; + + while (rev->ready) { + + /* XXX limit checks */ + /* XXX buffer input */ + + n = c->recv(c, &v, 1); + + if (n == NGX_ERROR || n == 0) { + goto failed; + } + + if (n != 1) { + break; + } + + switch (state) { + + case sw_start: + + if (v & 0x80) { + /* Header Acknowledgement */ + + index = v & 0x7f; + + if (index != 0x7f) { + if (ngx_http_v3_ack_header(c, index) != NGX_OK) { + goto failed; + } + + break; + } + + index = 0; + state = sw_ack_header; + break; + } + + if (v & 0x40) { + /* Stream Cancellation */ + + index = v & 0x3f; + + if (index != 0x3f) { + if (ngx_http_v3_cancel_stream(c, index) != NGX_OK) { + goto failed; + } + + break; + } + + index = 0; + state = sw_cancel_stream; + break; + } + + /* Insert Count Increment */ + + index = v & 0x3f; + + if (index != 0x3f) { + if (ngx_http_v3_inc_insert_count(c, index) != NGX_OK) { + goto failed; + } + + break; + } + + index = 0; + state = sw_inc_insert_count; + break; + + case sw_ack_header: + + index = (index << 7) + (v & 0x7f); + if (v & 0x80) { + break; + } + + index += 0x7f; + + if (ngx_http_v3_ack_header(c, index) != NGX_OK) { + goto failed; + } + + state = sw_start; + break; + + case sw_cancel_stream: + + index = (index << 7) + (v & 0x7f); + if (v & 0x80) { + break; + } + + index += 0x3f; + + if (ngx_http_v3_cancel_stream(c, index) != NGX_OK) { + goto failed; + } + + state = sw_start; + break; + + case sw_inc_insert_count: + + index = (index << 7) + (v & 0x7f); + if (v & 0x80) { + break; + } + + index += 0x3f; + + if (ngx_http_v3_inc_insert_count(c, index) != NGX_OK) { + goto failed; + } + + state = sw_start; + break; + } + } + + st->state = state; + st->index = index; + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + goto failed; + } + + return; + +failed: + + ngx_http_v3_close_uni_stream(c); +} + + +/* XXX async & buffered stream writes */ + +static ngx_connection_t * +ngx_http_v3_create_uni_stream(ngx_connection_t *c, ngx_uint_t type) +{ + u_char buf[NGX_HTTP_V3_VARLEN_INT_LEN]; + size_t n; + ngx_connection_t *sc; + ngx_pool_cleanup_t *cln; + ngx_http_v3_uni_stream_t *us; + + sc = ngx_quic_create_uni_stream(c->qs->parent); + if (sc == NULL) { + return NULL; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 create uni stream, type:%ui", type); + + us = ngx_pcalloc(sc->pool, sizeof(ngx_http_v3_uni_stream_t)); + if (us == NULL) { + goto failed; + } + + us->signature = NGX_HTTP_V3_STREAM; + us->type = type; + sc->data = us; + + cln = ngx_pool_cleanup_add(sc->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->handler = ngx_http_v3_uni_stream_cleanup; + cln->data = sc; + + n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf; + + if (sc->send(sc, buf, n) != (ssize_t) n) { + goto failed; + } + + return sc; + +failed: + + ngx_http_v3_close_uni_stream(sc); + + return NULL; +} + + +static ngx_connection_t * +ngx_http_v3_get_server_encoder(ngx_connection_t *c) +{ + ngx_http_v3_connection_t *h3c; + + h3c = c->qs->parent->data; + + if (h3c->server_encoder == NULL) { + h3c->server_encoder = ngx_http_v3_create_uni_stream(c, + NGX_HTTP_V3_ENCODER_STREAM); + } + + return h3c->server_encoder; +} + + +static ngx_connection_t * +ngx_http_v3_get_server_decoder(ngx_connection_t *c) +{ + ngx_http_v3_connection_t *h3c; + + h3c = c->qs->parent->data; + + if (h3c->server_decoder == NULL) { + h3c->server_decoder = ngx_http_v3_create_uni_stream(c, + NGX_HTTP_V3_DECODER_STREAM); + } + + return h3c->server_decoder; +} + + +ngx_int_t +ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value) +{ + u_char *p, buf[NGX_HTTP_V3_PREFIX_INT_LEN * 2]; + size_t n; + ngx_connection_t *ec; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client ref insert, %s[%ui] \"%V\"", + dynamic ? "dynamic" : "static", index, value); + + ec = ngx_http_v3_get_server_encoder(c); + if (ec == NULL) { + return NGX_ERROR; + } + + p = buf; + + *p = (dynamic ? 0x80 : 0xc0); + p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 6); + + /* XXX option for huffman? */ + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, value->len, 7); + + n = p - buf; + + if (ec->send(ec, buf, n) != (ssize_t) n) { + goto failed; + } + + if (ec->send(ec, value->data, value->len) != (ssize_t) value->len) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_http_v3_close_uni_stream(ec); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_client_insert(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *ec; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client insert \"%V\":\"%V\"", name, value); + + ec = ngx_http_v3_get_server_encoder(c); + if (ec == NULL) { + return NGX_ERROR; + } + + /* XXX option for huffman? */ + buf[0] = 0x40; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, name->len, 5) - buf; + + if (ec->send(ec, buf, n) != (ssize_t) n) { + goto failed; + } + + if (ec->send(ec, name->data, name->len) != (ssize_t) name->len) { + goto failed; + } + + /* XXX option for huffman? */ + buf[0] = 0; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, value->len, 7) - buf; + + if (ec->send(ec, buf, n) != (ssize_t) n) { + goto failed; + } + + if (ec->send(ec, value->data, value->len) != (ssize_t) value->len) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_http_v3_close_uni_stream(ec); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_client_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *ec; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client set capacity %ui", capacity); + + ec = ngx_http_v3_get_server_encoder(c); + if (ec == NULL) { + return NGX_ERROR; + } + + buf[0] = 0x20; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, capacity, 5) - buf; + + if (ec->send(ec, buf, n) != (ssize_t) n) { + ngx_http_v3_close_uni_stream(ec); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_client_duplicate(ngx_connection_t *c, ngx_uint_t index) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *ec; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client duplicate %ui", index); + + ec = ngx_http_v3_get_server_encoder(c); + if (ec == NULL) { + return NGX_ERROR; + } + + buf[0] = 0; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, index, 5) - buf; + + if (ec->send(ec, buf, n) != (ssize_t) n) { + ngx_http_v3_close_uni_stream(ec); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_client_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client ack header %ui", stream_id); + + dc = ngx_http_v3_get_server_decoder(c); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0x80; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 7) - buf; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + ngx_http_v3_close_uni_stream(dc); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_client_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client cancel stream %ui", stream_id); + + dc = ngx_http_v3_get_server_decoder(c); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0x40; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 6) - buf; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + ngx_http_v3_close_uni_stream(dc); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_client_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client increment insert count %ui", inc); + + dc = ngx_http_v3_get_server_decoder(c); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, inc, 6) - buf; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + ngx_http_v3_close_uni_stream(dc); + return NGX_ERROR; + } + + return NGX_OK; +} diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c new file mode 100644 index 000000000..1c1d8c051 --- /dev/null +++ b/src/http/v3/ngx_http_v3_tables.c @@ -0,0 +1,385 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static ngx_array_t *ngx_http_v3_get_dynamic_table(ngx_connection_t *c); +static ngx_int_t ngx_http_v3_new_header(ngx_connection_t *c); + + +static ngx_http_v3_header_t ngx_http_v3_static_table[] = { + + { ngx_string(":authority"), ngx_string("") }, + { ngx_string(":path"), ngx_string("/") }, + { ngx_string("age"), ngx_string("0") }, + { ngx_string("content-disposition"), ngx_string("") }, + { ngx_string("content-length"), ngx_string("0") }, + { ngx_string("cookie"), ngx_string("") }, + { ngx_string("date"), ngx_string("") }, + { ngx_string("etag"), ngx_string("") }, + { ngx_string("if-modified-since"), ngx_string("") }, + { ngx_string("if-none-match"), ngx_string("") }, + { ngx_string("last-modified"), ngx_string("") }, + { ngx_string("link"), ngx_string("") }, + { ngx_string("location"), ngx_string("") }, + { ngx_string("referer"), ngx_string("") }, + { ngx_string("set-cookie"), ngx_string("") }, + { ngx_string(":method"), ngx_string("CONNECT") }, + { ngx_string(":method"), ngx_string("DELETE") }, + { ngx_string(":method"), ngx_string("GET") }, + { ngx_string(":method"), ngx_string("HEAD") }, + { ngx_string(":method"), ngx_string("OPTIONS") }, + { ngx_string(":method"), ngx_string("POST") }, + { ngx_string(":method"), ngx_string("PUT") }, + { ngx_string(":scheme"), ngx_string("http") }, + { ngx_string(":scheme"), ngx_string("https") }, + { ngx_string(":status"), ngx_string("103") }, + { ngx_string(":status"), ngx_string("200") }, + { ngx_string(":status"), ngx_string("304") }, + { ngx_string(":status"), ngx_string("404") }, + { ngx_string(":status"), ngx_string("503") }, + { ngx_string("accept"), ngx_string("*/*") }, + { ngx_string("accept"), + ngx_string("application/dns-message ") }, + { ngx_string("accept-encoding"), ngx_string("gzip,") }, + { ngx_string("accept-ranges"), ngx_string("bytes") }, + { ngx_string("access-control-allow-headers"), + ngx_string("cache-control") }, + { ngx_string("access-control-allow-headers"), + ngx_string("content-type") }, + { ngx_string("access-control-allow-origin"), + ngx_string("*") }, + { ngx_string("cache-control"), ngx_string("max-age=0") }, + { ngx_string("cache-control"), ngx_string("max-age=2592000") }, + { ngx_string("cache-control"), ngx_string("max-age=604800") }, + { ngx_string("cache-control"), ngx_string("no-cache") }, + { ngx_string("cache-control"), ngx_string("no-store") }, + { ngx_string("cache-control"), + ngx_string("public, max-age=31536000 ") }, + { ngx_string("content-encoding"), ngx_string("br") }, + { ngx_string("content-encoding"), ngx_string("gzip") }, + { ngx_string("content-type"), + ngx_string("application/dns-message") }, + { ngx_string("content-type"), + ngx_string("application/javascript") }, + { ngx_string("content-type"), ngx_string("application/json") }, + { ngx_string("content-type"), + ngx_string("application/x-www-form-urlencoded") }, + { ngx_string("content-type"), ngx_string("image/gif") }, + { ngx_string("content-type"), ngx_string("image/jpeg") }, + { ngx_string("content-type"), ngx_string("image/png") }, + { ngx_string("content-type"), ngx_string("text/css") }, + { ngx_string("content-type"), + ngx_string("text/html;charset=utf-8") }, + { ngx_string("content-type"), ngx_string("text/plain") }, + { ngx_string("content-type"), + ngx_string("text/plain;charset=utf-8") }, + { ngx_string("range"), ngx_string("bytes=0-") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000;includesubdomains") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000;includesubdomains;preload") }, + { ngx_string("vary"), ngx_string("accept-encoding") }, + { ngx_string("vary"), ngx_string("origin") }, + { ngx_string("x-content-type-options"),ngx_string("nosniff") }, + { ngx_string("x-xss-protection"), ngx_string("1;mode=block") }, + { ngx_string(":status"), ngx_string("100") }, + { ngx_string(":status"), ngx_string("204") }, + { ngx_string(":status"), ngx_string("206") }, + { ngx_string(":status"), ngx_string("302") }, + { ngx_string(":status"), ngx_string("400") }, + { ngx_string(":status"), ngx_string("403") }, + { ngx_string(":status"), ngx_string("421") }, + { ngx_string(":status"), ngx_string("425") }, + { ngx_string(":status"), ngx_string("500") }, + { ngx_string("accept-language"), ngx_string("") }, + { ngx_string("access-control-allow-credentials"), + ngx_string("FALSE") }, + { ngx_string("access-control-allow-credentials"), + ngx_string("TRUE") }, + { ngx_string("access-control-allow-headers"), + ngx_string("*") }, + { ngx_string("access-control-allow-methods"), + ngx_string("get") }, + { ngx_string("access-control-allow-methods"), + ngx_string("get, post, options") }, + { ngx_string("access-control-allow-methods"), + ngx_string("options") }, + { ngx_string("access-control-expose-headers"), + ngx_string("content-length") }, + { ngx_string("access-control-request-headers"), + ngx_string("content-type") }, + { ngx_string("access-control-request-method"), + ngx_string("get") }, + { ngx_string("access-control-request-method"), + ngx_string("post") }, + { ngx_string("alt-svc"), ngx_string("clear") }, + { ngx_string("horization"), ngx_string("") }, + { ngx_string("content-security-policy"), + ngx_string("script-src") }, + { ngx_string("early-data"), ngx_string("1") }, + { ngx_string("expect-ct"), ngx_string("") }, + { ngx_string("forwarded"), ngx_string("") }, + { ngx_string("if-range"), ngx_string("") }, + { ngx_string("origin"), ngx_string("") }, + { ngx_string("purpose"), ngx_string("prefetch") }, + { ngx_string("server"), ngx_string("") }, + { ngx_string("timing-allow-origin"), ngx_string("*") }, + { ngx_string("upgrade-insecure-requests"), + ngx_string("1") }, + { ngx_string("user-agent"), ngx_string("") }, + { ngx_string("x-forwarded-for"), ngx_string("") }, + { ngx_string("x-frame-options"), ngx_string("deny") }, + { ngx_string("x-frame-options"), ngx_string("sameorigin") } +}; + + +ngx_int_t +ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value) +{ + ngx_array_t *dt; + ngx_http_v3_header_t *ref, *h; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ref insert %s[$ui] \"%V\"", + dynamic ? "dynamic" : "static", index, value); + + ref = ngx_http_v3_lookup_table(c, dynamic, index); + if (ref == NULL) { + return NGX_ERROR; + } + + dt = ngx_http_v3_get_dynamic_table(c); + if (dt == NULL) { + return NGX_ERROR; + } + + h = ngx_array_push(dt); + if (h == NULL) { + return NGX_ERROR; + } + + h->name = ref->name; + h->value = *value; + + if (ngx_http_v3_new_header(c) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value) +{ + ngx_array_t *dt; + ngx_http_v3_header_t *h; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 insert \"%V\":\"%V\"", name, value); + + dt = ngx_http_v3_get_dynamic_table(c); + if (dt == NULL) { + return NGX_ERROR; + } + + h = ngx_array_push(dt); + if (h == NULL) { + return NGX_ERROR; + } + + h->name = *name; + h->value = *value; + + if (ngx_http_v3_new_header(c) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 set capacity %ui", capacity); + + /* XXX ignore capacity */ + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) +{ + ngx_array_t *dt; + ngx_http_v3_header_t *ref, *h; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); + + ref = ngx_http_v3_lookup_table(c, 1, index); + if (ref == NULL) { + return NGX_ERROR; + } + + dt = ngx_http_v3_get_dynamic_table(c); + if (dt == NULL) { + return NGX_ERROR; + } + + h = ngx_array_push(dt); + if (h == NULL) { + return NGX_ERROR; + } + + *h = *ref; + + if (ngx_http_v3_new_header(c) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ack header %ui", stream_id); + + /* XXX */ + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 cancel stream %ui", stream_id); + + /* XXX */ + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 increment insert count %ui", inc); + + /* XXX */ + + return NGX_OK; +} + + +static ngx_array_t * +ngx_http_v3_get_dynamic_table(ngx_connection_t *c) +{ + ngx_connection_t *pc; + ngx_http_v3_connection_t *h3c; + + pc = c->qs->parent; + h3c = pc->data; + + if (h3c->dynamic) { + return h3c->dynamic; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create dynamic table"); + + h3c->dynamic = ngx_array_create(pc->pool, 1, sizeof(ngx_http_v3_header_t)); + + return h3c->dynamic; +} + + +ngx_http_v3_header_t * +ngx_http_v3_lookup_table(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index) +{ + ngx_uint_t nelts; + ngx_array_t *dt; + ngx_http_v3_header_t *table; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 lookup %s[%ui]", + dynamic ? "dynamic" : "static", index); + + if (dynamic) { + dt = ngx_http_v3_get_dynamic_table(c); + if (dt == NULL) { + return NULL; + } + + table = dt->elts; + nelts = dt->nelts; + + } else { + table = ngx_http_v3_static_table; + nelts = sizeof(ngx_http_v3_static_table) + / sizeof(ngx_http_v3_static_table[0]); + } + + if (index >= nelts) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 lookup out of bounds: %ui", nelts); + return NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 lookup \"%V\":\"%V\"", + &table[index].name, &table[index].value); + + return &table[index]; +} + + +ngx_int_t +ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) +{ + size_t n; + ngx_http_v3_connection_t *h3c; + + h3c = c->qs->parent->data; + n = h3c->dynamic ? h3c->dynamic->nelts : 0; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 check insert count %ui/%ui", insert_count, n); + + if (n < insert_count) { + /* XXX how to get notified? */ + /* XXX wake all streams on any arrival to the encoder stream? */ + return NGX_AGAIN; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_new_header(ngx_connection_t *c) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 new dynamic header"); + + /* XXX report all waiting streams of a new header */ + + return NGX_OK; +} -- cgit From 5399670fcc51b440f00a9a584654f7bcc52d3f88 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Sat, 14 Mar 2020 13:18:55 +0300 Subject: Temporary fix for header null-termination in HTTP/3. --- src/http/ngx_http_request.c | 4 ++-- src/http/v3/ngx_http_v3_request.c | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index ef305faf4..85b090d5f 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1547,11 +1547,11 @@ ngx_http_process_request_headers(ngx_event_t *rev) h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; - //h->key.data[h->key.len] = '\0'; + h->key.data[h->key.len] = '\0'; h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; - //h->value.data[h->value.len] = '\0'; + h->value.data[h->value.len] = '\0'; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index b34eed98e..9cb351c2d 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -518,6 +518,18 @@ done: } } + /* XXX ugly reallocation for the trailing '\0' */ + + p = ngx_pnalloc(c->pool, name.len + value.len + 2); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, name.data, name.len); + name.data = p; + ngx_memcpy(p + name.len + 1, value.data, value.len); + value.data = p + name.len + 1; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header \"%V\":\"%V\"", &name, &value); -- cgit From 01dc7445f0fc392edd4f4e23f4fa1af69af68e41 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 18 Mar 2020 13:46:35 +0300 Subject: Refactored HTTP/3 parser. --- src/http/ngx_http_request.c | 14 +- src/http/ngx_http_request.h | 9 +- src/http/v3/ngx_http_v3.c | 80 --- src/http/v3/ngx_http_v3.h | 35 +- src/http/v3/ngx_http_v3_parse.c | 1424 +++++++++++++++++++++++++++++++++++++ src/http/v3/ngx_http_v3_parse.h | 145 ++++ src/http/v3/ngx_http_v3_request.c | 137 +++- src/http/v3/ngx_http_v3_streams.c | 692 +++--------------- src/http/v3/ngx_http_v3_tables.c | 30 + 9 files changed, 1872 insertions(+), 694 deletions(-) create mode 100644 src/http/v3/ngx_http_v3_parse.c create mode 100644 src/http/v3/ngx_http_v3_parse.h (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 85b090d5f..166d8ffba 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1163,7 +1163,7 @@ ngx_http_process_request_line(ngx_event_t *rev) switch (r->http_version) { #if (NGX_HTTP_V3) case NGX_HTTP_VERSION_30: - rc = ngx_http_v3_parse_header(r, r->header_in, 1); + rc = ngx_http_v3_parse_header(r, r->header_in); break; #endif @@ -1510,7 +1510,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) switch (r->http_version) { #if (NGX_HTTP_V3) case NGX_HTTP_VERSION_30: - rc = ngx_http_v3_parse_header(r, r->header_in, 0); + rc = ngx_http_v3_parse_header(r, r->header_in); break; #endif @@ -1547,11 +1547,17 @@ ngx_http_process_request_headers(ngx_event_t *rev) h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; - h->key.data[h->key.len] = '\0'; + + if (h->key.data[h->key.len]) { + h->key.data[h->key.len] = '\0'; + } h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; - h->value.data[h->value.len] = '\0'; + + if (h->value.data[h->value.len]) { + h->value.data[h->value.len] = '\0'; + } h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 04d88db7b..772d53b2d 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -595,14 +595,7 @@ struct ngx_http_request_s { u_char *port_end; #if (NGX_HTTP_V3) - ngx_uint_t h3_length; - ngx_uint_t h3_index; - ngx_uint_t h3_insert_count; - ngx_uint_t h3_sign; - ngx_uint_t h3_delta_base; - ngx_uint_t h3_huffman; - ngx_uint_t h3_dynamic; - ngx_uint_t h3_offset; + void *h3_parse; #endif unsigned http_minor:16; diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index e804cf6f5..cca84dbc1 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -94,83 +94,3 @@ ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix) return (uintptr_t) p; } - - -uint64_t -ngx_http_v3_decode_varlen_int(u_char *p) -{ - uint64_t value; - ngx_uint_t len; - - len = *p >> 6; - value = *p & 0x3f; - - while (len--) { - value = (value << 8) + *p++; - } - - return value; -} - - -int64_t -ngx_http_v3_decode_prefix_int(u_char **src, size_t len, ngx_uint_t prefix) -{ - u_char *p; - int64_t value, thresh; - - if (len == 0) { - return NGX_ERROR; - } - - p = *src; - - thresh = (1 << prefix) - 1; - value = *p++ & thresh; - - if (value != thresh) { - *src = p; - return value; - } - - value = 0; - - /* XXX handle overflows */ - - while (--len) { - value = (value << 7) + (*p & 0x7f); - if ((*p++ & 0x80) == 0) { - *src = p; - return value + thresh; - } - } - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_decode_huffman(ngx_connection_t *c, ngx_str_t *s) -{ - u_char state, *p, *data; - - state = 0; - - p = ngx_pnalloc(c->pool, s->len * 8 / 5); - if (p == NULL) { - return NGX_ERROR; - } - - data = p; - - if (ngx_http_v2_huff_decode(&state, s->data, s->len, &p, 1, c->log) - != NGX_OK) - { - return NGX_ERROR; - } - - s->len = p - data; - s->data = data; - - return NGX_OK; -} diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 8fa54d8e5..fc1cadd79 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -12,13 +12,31 @@ #include #include #include +#include #define NGX_HTTP_V3_STREAM 0x48335354 /* "H3ST" */ -#define NGX_HTTP_V3_VARLEN_INT_LEN 4 -#define NGX_HTTP_V3_PREFIX_INT_LEN 11 +#define NGX_HTTP_V3_VARLEN_INT_LEN 4 +#define NGX_HTTP_V3_PREFIX_INT_LEN 11 + +#define NGX_HTTP_V3_STREAM_CONTROL 0x00 +#define NGX_HTTP_V3_STREAM_PUSH 0x01 +#define NGX_HTTP_V3_STREAM_ENCODER 0x02 +#define NGX_HTTP_V3_STREAM_DECODER 0x03 + +#define NGX_HTTP_V3_FRAME_DATA 0x00 +#define NGX_HTTP_V3_FRAME_HEADERS 0x01 +#define NGX_HTTP_V3_FRAME_CANCEL_PUSH 0x03 +#define NGX_HTTP_V3_FRAME_SETTINGS 0x04 +#define NGX_HTTP_V3_FRAME_PUSH_PROMISE 0x05 +#define NGX_HTTP_V3_FRAME_GOAWAY 0x07 +#define NGX_HTTP_V3_FRAME_MAX_PUSH_ID 0x0d + +#define NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY 0x01 +#define NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE 0x06 +#define NGX_HTTP_V3_PARAM_BLOCKED_STREAMS 0x07 typedef struct { @@ -28,8 +46,11 @@ typedef struct { ngx_connection_t *client_encoder; ngx_connection_t *client_decoder; + ngx_connection_t *client_control; + ngx_connection_t *server_encoder; ngx_connection_t *server_decoder; + ngx_connection_t *server_control; } ngx_http_v3_connection_t; @@ -39,18 +60,12 @@ typedef struct { } ngx_http_v3_header_t; -ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, - ngx_uint_t pseudo); +ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b); ngx_chain_t *ngx_http_v3_create_header(ngx_http_request_t *r); - uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix); -uint64_t ngx_http_v3_decode_varlen_int(u_char *p); -int64_t ngx_http_v3_decode_prefix_int(u_char **src, size_t len, - ngx_uint_t prefix); -ngx_int_t ngx_http_v3_decode_huffman(ngx_connection_t *c, ngx_str_t *s); void ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c); @@ -67,6 +82,8 @@ ngx_http_v3_header_t *ngx_http_v3_lookup_table(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index); ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count); +ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, + uint64_t value); ngx_int_t ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value); diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c new file mode 100644 index 000000000..e0b28a6c3 --- /dev/null +++ b/src/http/v3/ngx_http_v3_parse.c @@ -0,0 +1,1424 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ngx_int_t +ngx_http_v3_parse_varlen_int(ngx_connection_t *c, + ngx_http_v3_parse_varlen_int_t *st, u_char ch) +{ + enum { + sw_start = 0, + sw_length_2, + sw_length_3, + sw_length_4, + sw_length_5, + sw_length_6, + sw_length_7, + sw_length_8 + }; + + switch (st->state) { + + case sw_start: + + st->value = ch; + if (st->value & 0xc0) { + st->state = sw_length_2; + break; + } + + goto done; + + case sw_length_2: + + st->value = (st->value << 8) + ch; + if ((st->value & 0xc000) == 0x4000) { + st->value &= 0x3fff; + goto done; + } + + st->state = sw_length_3; + break; + + case sw_length_4: + + st->value = (st->value << 8) + ch; + if ((st->value & 0xc0000000) == 0x80000000) { + st->value &= 0x3fffffff; + goto done; + } + + st->state = sw_length_5; + break; + + case sw_length_3: + case sw_length_5: + case sw_length_6: + case sw_length_7: + + st->value = (st->value << 8) + ch; + st->state++; + break; + + case sw_length_8: + + st->value = (st->value << 8) + ch; + st->value &= 0x3fffffffffffffff; + goto done; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse varlen int %uL", st->value); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_prefix_int(ngx_connection_t *c, + ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch) +{ + enum { + sw_start = 0, + sw_value + }; + + switch (st->state) { + + case sw_start: + + st->mask = (1 << prefix) - 1; + st->value = (ch & st->mask); + + if (st->value != st->mask) { + goto done; + } + + st->value = 0; + st->state = sw_value; + break; + + case sw_value: + + st->value = (st->value << 7) + (ch & 0x7f); + if (ch & 0x80) { + break; + } + + st->value += st->mask; + goto done; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse prefix int %uL", st->value); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, + u_char ch) +{ + ngx_int_t rc; + enum { + sw_start = 0, + sw_length, + sw_prefix, + sw_header_rep, + sw_done + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers"); + + if (ch != NGX_HTTP_V3_FRAME_HEADERS) { + return NGX_ERROR; + } + + st->state = sw_length; + break; + + case sw_length: + + if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { + break; + } + + st->length = st->vlint.value; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse headers len:%ui", st->length); + + st->state = sw_prefix; + break; + + case sw_prefix: + + if (st->length-- == 0) { + return NGX_ERROR; + } + + rc = ngx_http_v3_parse_header_block_prefix(c, &st->prefix, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc != NGX_DONE) { + break; + } + + if (st->length == 0) { + return NGX_ERROR; + } + + st->state = sw_header_rep; + break; + + case sw_header_rep: + + rc = ngx_http_v3_parse_header_rep(c, &st->header_rep, st->prefix.base, + ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (--st->length == 0) { + if (rc != NGX_DONE) { +ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "XXX len"); + return NGX_ERROR; + } + + goto done; + } + + if (rc == NGX_DONE) { + return NGX_OK; + } + + break; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers done"); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, + ngx_http_v3_parse_header_block_prefix_t *st, u_char ch) +{ + enum { + sw_start = 0, + sw_req_insert_count, + sw_delta_base, + sw_read_delta_base + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header block prefix"); + + st->state = sw_req_insert_count; + + /* fall through */ + + case sw_req_insert_count: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 8, ch) != NGX_DONE) { + break; + } + + st->insert_count = st->pint.value; + st->state = sw_delta_base; + break; + + case sw_delta_base: + + st->sign = (ch & 0x80) ? 1 : 0; + st->state = sw_read_delta_base; + + /* fall through */ + + case sw_read_delta_base: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { + break; + } + + st->delta_base = st->pint.value; + goto done; + } + + return NGX_AGAIN; + +done: + + if (st->sign) { + st->base = st->insert_count - st->delta_base - 1; + } else { + st->base = st->insert_count + st->delta_base; + } + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header block prefix done " + "i:%ui, s:%ui, d:%ui, base:%uL", + st->insert_count, st->sign, st->delta_base, st->base); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_header_rep(ngx_connection_t *c, + ngx_http_v3_parse_header_rep_t *st, ngx_uint_t base, u_char ch) +{ + ngx_int_t rc; + enum { + sw_start = 0, + sw_header_ri, + sw_header_lri, + sw_header_l, + sw_header_pbi, + sw_header_lpbi + }; + + if (st->state == sw_start) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header representation"); + + ngx_memzero(&st->header, sizeof(ngx_http_v3_parse_header_t)); + + st->header.base = base; + + if (ch & 0x80) { + /* Indexed Header Field */ + + st->state = sw_header_ri; + + } else if (ch & 0x40) { + /* Literal Header Field With Name Reference */ + + st->state = sw_header_lri; + + } else if (ch & 0x20) { + /* Literal Header Field Without Name Reference */ + + st->state = sw_header_l; + + } else if (ch & 0x10) { + /* Indexed Header Field With Post-Base Index */ + + st->state = sw_header_pbi; + + } else { + /* Literal Header Field With Post-Base Name Reference */ + + st->state = sw_header_lpbi; + } + } + + switch (st->state) { + + case sw_header_ri: + rc = ngx_http_v3_parse_header_ri(c, &st->header, ch); + break; + + case sw_header_lri: + rc = ngx_http_v3_parse_header_lri(c, &st->header, ch); + break; + + case sw_header_l: + rc = ngx_http_v3_parse_header_l(c, &st->header, ch); + break; + + case sw_header_pbi: + rc = ngx_http_v3_parse_header_pbi(c, &st->header, ch); + break; + + case sw_header_lpbi: + rc = ngx_http_v3_parse_header_lpbi(c, &st->header, ch); + break; + + default: + rc = NGX_OK; + } + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_AGAIN) { + return NGX_AGAIN; + } + + /* rc == NGX_DONE */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header representation done"); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, + u_char ch) +{ + ngx_uint_t n; + enum { + sw_start = 0, + sw_value + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse literal huff:%ui, len:%ui", + st->huffman, st->length); + + n = st->length; + + if (st->huffman) { + n = n * 8 / 5; + st->huffstate = 0; + } + + st->last = ngx_pnalloc(c->pool, n + 1); + if (st->last == NULL) { + return NGX_ERROR; + } + + st->value.data = st->last; + st->state = sw_value; + + /* fall through */ + + case sw_value: + + if (st->huffman) { + if (ngx_http_v2_huff_decode(&st->huffstate, &ch, 1, &st->last, + st->length == 1, c->log) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + *st->last++ = ch; + } + + if (--st->length) { + break; + } + + st->value.len = st->last - st->value.data; + *st->last = '\0'; + goto done; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse literal done \"%V\"", &st->value); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_header_ri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, + u_char ch) +{ + ngx_http_v3_header_t *h; + enum { + sw_start = 0, + sw_index + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header ri"); + + st->dynamic = (ch & 0x40) ? 0 : 1; + st->state = sw_index; + + /* fall through */ + + case sw_index: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch) != NGX_DONE) { + break; + } + + st->index = st->pint.value; + goto done; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header ri done %s%ui]", + st->dynamic ? "dynamic[-" : "static[", st->index); + + if (st->dynamic) { + st->index = st->base - st->index - 1; + } + + h = ngx_http_v3_lookup_table(c, st->dynamic, st->index); + if (h == NULL) { + return NGX_ERROR; + } + + st->name = h->name; + st->value = h->value; + st->state = sw_start; + + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_header_lri(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch) +{ + ngx_int_t rc; + ngx_http_v3_header_t *h; + enum { + sw_start = 0, + sw_index, + sw_value_len, + sw_read_value_len, + sw_value + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header lri"); + + st->dynamic = (ch & 0x10) ? 0 : 1; + st->state = sw_index; + + /* fall through */ + + case sw_index: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 4, ch) != NGX_DONE) { + break; + } + + st->index = st->pint.value; + st->state = sw_value_len; + break; + + case sw_value_len: + + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; + + /* fall through */ + + case sw_read_value_len: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { + break; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + st->value = st->literal.value; + goto done; + } + + break; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header lri done %s%ui] \"%V\"", + st->dynamic ? "dynamic[-" : "static[", + st->index, &st->value); + + if (st->dynamic) { + st->index = st->base - st->index - 1; + } + + h = ngx_http_v3_lookup_table(c, st->dynamic, st->index); + if (h == NULL) { + return NGX_ERROR; + } + + st->name = h->name; + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_header_l(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch) +{ + ngx_int_t rc; + enum { + sw_start = 0, + sw_name_len, + sw_name, + sw_value_len, + sw_read_value_len, + sw_value + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header l"); + + st->literal.huffman = (ch & 0x08) ? 1 : 0; + st->state = sw_name_len; + + /* fall through */ + + case sw_name_len: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 3, ch) != NGX_DONE) { + break; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + return NGX_ERROR; + } + + st->state = sw_name; + break; + + case sw_name: + + rc = ngx_http_v3_parse_literal(c, &st->literal, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + st->name = st->literal.value; + st->state = sw_value_len; + } + + break; + + case sw_value_len: + + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; + + /* fall through */ + + case sw_read_value_len: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { + break; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + st->value = st->literal.value; + goto done; + } + + break; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header l done \"%V\" \"%V\"", + &st->name, &st->value); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_header_pbi(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch) +{ + ngx_http_v3_header_t *h; + enum { + sw_start = 0, + sw_index + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header pbi"); + + st->state = sw_index; + + /* fall through */ + + case sw_index: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 4, ch) != NGX_DONE) { + break; + } + + st->index = st->pint.value; + goto done; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header pbi done dynamic[+%ui]", st->index); + + h = ngx_http_v3_lookup_table(c, 1, st->base + st->index); + if (h == NULL) { + return NGX_ERROR; + } + + st->name = h->name; + st->value = h->value; + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch) +{ + ngx_int_t rc; + ngx_http_v3_header_t *h; + enum { + sw_start = 0, + sw_index, + sw_value_len, + sw_read_value_len, + sw_value + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header lpbi"); + + st->state = sw_index; + + /* fall through */ + + case sw_index: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 3, ch) != NGX_DONE) { + break; + } + + st->index = st->pint.value; + st->state = sw_value_len; + break; + + case sw_value_len: + + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; + + /* fall through */ + + case sw_read_value_len: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { + break; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + st->value = st->literal.value; + goto done; + } + + break; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header lpbi done dynamic[+%ui] \"%V\"", + st->index, &st->value); + + h = ngx_http_v3_lookup_table(c, 1, st->base + st->index); + if (h == NULL) { + return NGX_ERROR; + } + + st->name = h->name; + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) +{ + ngx_http_v3_parse_control_t *st = data; + + ngx_int_t rc; + enum { + sw_start = 0, + sw_type, + sw_length, + sw_settings, + sw_max_push_id, + sw_skip + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse control"); + + st->state = sw_type; + + /* fall through */ + + case sw_type: + + if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { + break; + } + + st->type = st->vlint.value; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse frame type:%ui", st->type); + + st->state = sw_length; + break; + + case sw_length: + + if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { + break; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse frame len:%uL", st->vlint.value); + + st->length = st->vlint.value; + if (st->length == 0) { + st->state = sw_type; + break; + } + + switch (st->type) { + + case NGX_HTTP_V3_FRAME_SETTINGS: + st->state = sw_settings; + break; + + case NGX_HTTP_V3_FRAME_MAX_PUSH_ID: + st->state = sw_max_push_id; + break; + + default: + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse skip unknown frame"); + st->state = sw_skip; + } + + break; + + case sw_settings: + + rc = ngx_http_v3_parse_settings(c, &st->settings, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (--st->length > 0) { + break; + } + + if (rc != NGX_DONE) { + return NGX_ERROR; + } + + st->state = sw_type; + break; + + case sw_max_push_id: + + if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { + break; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse MAX_PUSH_ID:%uL", st->vlint.value); + + st->state = sw_type; + break; + + case sw_skip: + + if (--st->length == 0) { + st->state = sw_type; + } + + break; + } + + return NGX_AGAIN; +} + + +ngx_int_t +ngx_http_v3_parse_settings(ngx_connection_t *c, + ngx_http_v3_parse_settings_t *st, u_char ch) +{ + enum { + sw_start = 0, + sw_id, + sw_value + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse settings"); + + st->state = sw_id; + + /* fall through */ + + case sw_id: + + if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { + break; + } + + st->id = st->vlint.value; + st->state = sw_value; + break; + + case sw_value: + + if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { + break; + } + + if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) { + return NGX_ERROR; + } + + goto done; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse settings done"); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) +{ + ngx_http_v3_parse_encoder_t *st = data; + + ngx_int_t rc; + enum { + sw_start = 0, + sw_inr, + sw_iwnr, + sw_capacity, + sw_duplicate + }; + + if (st->state == sw_start) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse encoder instruction"); + + if (ch & 0x80) { + /* Insert With Name Reference */ + + st->state = sw_inr; + + } else if (ch & 0x40) { + /* Insert Without Name Reference */ + + st->state = sw_iwnr; + + } else if (ch & 0x20) { + /* Set Dynamic Table Capacity */ + + st->state = sw_capacity; + + } else { + /* Duplicate */ + + st->state = sw_duplicate; + } + } + + switch (st->state) { + + case sw_inr: + + rc = ngx_http_v3_parse_header_inr(c, &st->header, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc != NGX_DONE) { + break; + } + + goto done; + + case sw_iwnr: + + rc = ngx_http_v3_parse_header_iwnr(c, &st->header, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc != NGX_DONE) { + break; + } + + goto done; + + case sw_capacity: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch) != NGX_DONE) { + break; + } + + if (ngx_http_v3_set_capacity(c, st->pint.value) != NGX_OK) { + return NGX_ERROR; + } + + goto done; + + case sw_duplicate: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch) != NGX_DONE) { + break; + } + + if (ngx_http_v3_duplicate(c, st->pint.value) != NGX_OK) { + return NGX_ERROR; + } + + goto done; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse encoder instruction done"); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_header_inr(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch) +{ + ngx_int_t rc; + enum { + sw_start = 0, + sw_name_index, + sw_value_len, + sw_read_value_len, + sw_value + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header inr"); + + st->dynamic = (ch & 0x40) ? 0 : 1; + st->state = sw_name_index; + + /* fall through */ + + case sw_name_index: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch) != NGX_DONE) { + break; + } + + st->index = st->pint.value; + st->state = sw_value_len; + break; + + case sw_value_len: + + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; + + /* fall through */ + + case sw_read_value_len: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { + break; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + st->value = st->literal.value; + goto done; + } + + break; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header inr done %s[%ui] \"%V\"", + st->dynamic ? "dynamic" : "static", + st->index, &st->value); + + if (ngx_http_v3_ref_insert(c, st->dynamic, st->index, &st->value) != NGX_OK) + { + return NGX_ERROR; + } + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch) +{ + ngx_int_t rc; + enum { + sw_start = 0, + sw_name_len, + sw_name, + sw_value_len, + sw_read_value_len, + sw_value + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header iwnr"); + + st->literal.huffman = (ch & 0x20) ? 1 : 0; + st->state = sw_name_len; + + /* fall through */ + + case sw_name_len: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch) != NGX_DONE) { + break; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + return NGX_ERROR; + } + + st->state = sw_name; + break; + + case sw_name: + + rc = ngx_http_v3_parse_literal(c, &st->literal, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + st->name = st->literal.value; + st->state = sw_value_len; + } + + break; + + case sw_value_len: + + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; + + /* fall through */ + + case sw_read_value_len: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { + break; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, ch); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + st->value = st->literal.value; + goto done; + } + + break; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header iwnr done \"%V\":\"%V\"", + &st->name, &st->value); + + if (ngx_http_v3_insert(c, &st->name, &st->value) != NGX_OK) { + return NGX_ERROR; + } + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) +{ + ngx_http_v3_parse_decoder_t *st = data; + + enum { + sw_start = 0, + sw_ack_header, + sw_cancel_stream, + sw_inc_insert_count + }; + + if (st->state == sw_start) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse decoder instruction"); + + if (ch & 0x80) { + /* Header Acknowledgement */ + + st->state = sw_ack_header; + + } else if (ch & 0x40) { + /* Stream Cancellation */ + + st->state = sw_cancel_stream; + + } else { + /* Insert Count Increment */ + + st->state = sw_inc_insert_count; + } + } + + switch (st->state) { + + case sw_ack_header: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch) != NGX_DONE) { + break; + } + + if (ngx_http_v3_ack_header(c, st->pint.value) != NGX_OK) { + return NGX_ERROR; + } + + goto done; + + case sw_cancel_stream: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch) != NGX_DONE) { + break; + } + + if (ngx_http_v3_cancel_stream(c, st->pint.value) != NGX_OK) { + return NGX_ERROR; + } + + goto done; + + case sw_inc_insert_count: + + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch) != NGX_DONE) { + break; + } + + if (ngx_http_v3_inc_insert_count(c, st->pint.value) != NGX_OK) { + return NGX_ERROR; + } + + goto done; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse decoder instruction done"); + + st->state = sw_start; + return NGX_DONE; +} diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h new file mode 100644 index 000000000..959da7941 --- /dev/null +++ b/src/http/v3/ngx_http_v3_parse.h @@ -0,0 +1,145 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_PARSE_H_INCLUDED_ +#define _NGX_HTTP_V3_PARSE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_uint_t state; + uint64_t value; +} ngx_http_v3_parse_varlen_int_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t mask; + uint64_t value; +} ngx_http_v3_parse_prefix_int_t; + + +typedef struct { + ngx_uint_t state; + uint64_t id; + ngx_http_v3_parse_varlen_int_t vlint; +} ngx_http_v3_parse_settings_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t insert_count; + ngx_uint_t delta_base; + ngx_uint_t sign; + ngx_uint_t base; + ngx_http_v3_parse_prefix_int_t pint; +} ngx_http_v3_parse_header_block_prefix_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t length; + ngx_uint_t huffman; + ngx_str_t value; + u_char *last; + u_char huffstate; +} ngx_http_v3_parse_literal_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t index; + ngx_uint_t base; + ngx_uint_t dynamic; + + ngx_str_t name; + ngx_str_t value; + + ngx_http_v3_parse_prefix_int_t pint; + ngx_http_v3_parse_literal_t literal; +} ngx_http_v3_parse_header_t; + + +typedef struct { + ngx_uint_t state; + ngx_http_v3_parse_header_t header; +} ngx_http_v3_parse_header_rep_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t length; + ngx_http_v3_parse_varlen_int_t vlint; + ngx_http_v3_parse_header_block_prefix_t prefix; + ngx_http_v3_parse_header_rep_t header_rep; +} ngx_http_v3_parse_headers_t; + + +typedef struct { + ngx_uint_t state; + ngx_http_v3_parse_header_t header; + ngx_http_v3_parse_prefix_int_t pint; +} ngx_http_v3_parse_encoder_t; + + +typedef struct { + ngx_uint_t state; + ngx_http_v3_parse_prefix_int_t pint; +} ngx_http_v3_parse_decoder_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t type; + ngx_uint_t length; + ngx_http_v3_parse_varlen_int_t vlint; + ngx_http_v3_parse_settings_t settings; +} ngx_http_v3_parse_control_t; + + +ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, + ngx_http_v3_parse_varlen_int_t *st, u_char ch); +ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, + ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch); + +ngx_int_t ngx_http_v3_parse_headers(ngx_connection_t *c, + ngx_http_v3_parse_headers_t *st, u_char ch); +ngx_int_t ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, + ngx_http_v3_parse_header_block_prefix_t *st, u_char ch); +ngx_int_t ngx_http_v3_parse_header_rep(ngx_connection_t *c, + ngx_http_v3_parse_header_rep_t *st, ngx_uint_t base, u_char ch); +ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, + ngx_http_v3_parse_literal_t *st, u_char ch); +ngx_int_t ngx_http_v3_parse_header_ri(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); +ngx_int_t ngx_http_v3_parse_header_lri(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); +ngx_int_t ngx_http_v3_parse_header_l(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); +ngx_int_t ngx_http_v3_parse_header_pbi(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); +ngx_int_t ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); + +ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch); +ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, + ngx_http_v3_parse_settings_t *st, u_char ch); + +ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch); +ngx_int_t ngx_http_v3_parse_header_inr(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); +ngx_int_t ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); + +ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch); + + +#endif /* _NGX_HTTP_V3_PARSE_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 9cb351c2d..e6cd27183 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,15 +10,6 @@ #include -#define NGX_HTTP_V3_FRAME_DATA 0x00 -#define NGX_HTTP_V3_FRAME_HEADERS 0x01 -#define NGX_HTTP_V3_FRAME_CANCEL_PUSH 0x03 -#define NGX_HTTP_V3_FRAME_SETTINGS 0x04 -#define NGX_HTTP_V3_FRAME_PUSH_PROMISE 0x05 -#define NGX_HTTP_V3_FRAME_GOAWAY 0x07 -#define NGX_HTTP_V3_FRAME_MAX_PUSH_ID 0x0d - - static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); @@ -46,6 +37,110 @@ struct { }; +ngx_int_t +ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b) +{ + ngx_int_t rc; + ngx_str_t *name, *value; + ngx_connection_t *c; + ngx_http_v3_parse_headers_t *st; + enum { + sw_start = 0, + sw_prev, + sw_headers, + sw_last, + sw_done + }; + + c = r->connection; + st = r->h3_parse; + + if (st == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header"); + + st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_headers_t)); + if (st == NULL) { + goto failed; + } + + r->h3_parse = st; + } + + switch (r->state) { + + case sw_prev: + r->state = sw_headers; + return NGX_OK; + + case sw_done: + goto done; + + case sw_last: + r->state = sw_done; + return NGX_OK; + + default: + break; + } + + for ( /* void */ ; b->pos < b->last; b->pos++) { + + rc = ngx_http_v3_parse_headers(c, st, *b->pos); + + if (rc == NGX_ERROR) { + goto failed; + } + + if (rc == NGX_AGAIN) { + continue; + } + + name = &st->header_rep.header.name; + value = &st->header_rep.header.value; + + if (r->state == sw_start + && ngx_http_v3_process_pseudo_header(r, name, value) != NGX_OK) + { + if (rc == NGX_DONE) { + r->state = sw_last; + } else { + r->state = sw_prev; + } + + } else if (rc == NGX_DONE) { + r->state = sw_done; + } + + if (r->state == sw_start) { + continue; + } + + r->header_name_start = name->data; + r->header_name_end = name->data + name->len; + r->header_start = value->data; + r->header_end = value->data + value->len; + r->header_hash = ngx_hash_key(name->data, name->len); + + /* XXX r->lowcase_index = i; */ + + return NGX_OK; + } + + return NGX_AGAIN; + +failed: + + return r->state == sw_start ? NGX_HTTP_PARSE_INVALID_REQUEST + : NGX_HTTP_PARSE_INVALID_HEADER; + +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header done"); + + return NGX_HTTP_PARSE_HEADER_DONE; +} + +#if 0 ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t pseudo) { @@ -167,11 +262,14 @@ again: break; } - /* fall through */ + length &= 0x3fffff; + state = sw_header_block; + break; case sw_length_3: - length &= 0x3fffff; + length = (length << 8) + ch; + length &= 0x3fffffff; state = sw_header_block; break; @@ -567,6 +665,7 @@ failed: return NGX_HTTP_PARSE_INVALID_REQUEST; } +#endif static ngx_int_t @@ -576,6 +675,10 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t i; ngx_connection_t *c; + if (name->len == 0 || name->data[0] != ':') { + return NGX_DECLINED; + } + c = r->connection; if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) { @@ -635,14 +738,10 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, return NGX_OK; } - if (name->len && name->data[0] == ':') { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 unknown pseudo header \"%V\" \"%V\"", - name, value); - return NGX_OK; - } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 unknown pseudo header \"%V\" \"%V\"", name, value); - return NGX_DONE; + return NGX_OK; } @@ -789,7 +888,7 @@ ngx_http_v3_create_header(ngx_http_request_t *r) b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 4); *b->last = 0; b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 3, 7); - b->last = ngx_sprintf(b->last, "%03ui ", r->headers_out.status); + b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); } if (r->headers_out.server == NULL) { diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 2b757d81f..18e38a68d 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -10,42 +10,30 @@ #include -#define NGX_HTTP_V3_CONTROL_STREAM 0x00 -#define NGX_HTTP_V3_PUSH_STREAM 0x01 -#define NGX_HTTP_V3_ENCODER_STREAM 0x02 -#define NGX_HTTP_V3_DECODER_STREAM 0x03 +typedef ngx_int_t (*ngx_http_v3_handler_pt)(ngx_connection_t *c, void *data, + u_char ch); typedef struct { - uint32_t signature; /* QSTR */ - u_char buf[4]; + uint32_t signature; /* QSTR */ - ngx_uint_t len; - ngx_uint_t type; - ngx_uint_t state; - ngx_uint_t index; - ngx_uint_t offset; + ngx_http_v3_handler_pt handler; + void *data; - ngx_str_t name; - ngx_str_t value; - - unsigned client:1; - unsigned dynamic:1; - unsigned huffman:1; + ngx_uint_t type; + ngx_uint_t client; /* unsigned client:1; */ } ngx_http_v3_uni_stream_t; static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); static void ngx_http_v3_uni_stream_cleanup(void *data); static void ngx_http_v3_read_uni_stream_type(ngx_event_t *rev); -static void ngx_http_v3_dummy_stream_handler(ngx_event_t *rev); -static void ngx_http_v3_client_encoder_handler(ngx_event_t *rev); -static void ngx_http_v3_client_decoder_handler(ngx_event_t *rev); - +static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); static ngx_connection_t *ngx_http_v3_create_uni_stream(ngx_connection_t *c, ngx_uint_t type); -static ngx_connection_t *ngx_http_v3_get_server_encoder(ngx_connection_t *c); -static ngx_connection_t *ngx_http_v3_get_server_decoder(ngx_connection_t *c); +static ngx_connection_t *ngx_http_v3_get_control(ngx_connection_t *c); +static ngx_connection_t *ngx_http_v3_get_encoder(ngx_connection_t *c); +static ngx_connection_t *ngx_http_v3_get_decoder(ngx_connection_t *c); void @@ -56,8 +44,13 @@ ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) c->log->connection = c->number; + /* XXX */ + (void) ngx_http_v3_get_control(c); + (void) ngx_http_v3_get_encoder(c); + (void) ngx_http_v3_get_decoder(c); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 new uni stream id:0x%uXL", c->qs->id); + "http3 new uni stream id:0x%uxL", c->qs->id); us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { @@ -81,7 +74,7 @@ ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) cln->data = c; c->read->handler = ngx_http_v3_read_uni_stream_type; - c->read->handler(c->read); + ngx_http_v3_read_uni_stream_type(c->read); } @@ -115,7 +108,7 @@ ngx_http_v3_uni_stream_cleanup(void *data) switch (us->type) { - case NGX_HTTP_V3_ENCODER_STREAM: + case NGX_HTTP_V3_STREAM_ENCODER: if (us->client) { h3c->client_encoder = NULL; @@ -125,7 +118,7 @@ ngx_http_v3_uni_stream_cleanup(void *data) break; - case NGX_HTTP_V3_DECODER_STREAM: + case NGX_HTTP_V3_STREAM_DECODER: if (us->client) { h3c->client_decoder = NULL; @@ -134,482 +127,114 @@ ngx_http_v3_uni_stream_cleanup(void *data) } break; - } -} - - -static void -ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) -{ - u_char *p; - ssize_t n, len; - ngx_connection_t *c; - ngx_http_v3_connection_t *h3c; - ngx_http_v3_uni_stream_t *us; - - c = rev->data; - us = c->data; - h3c = c->qs->parent->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read stream type"); - - while (rev->ready) { - p = &us->buf[us->len]; + case NGX_HTTP_V3_STREAM_CONTROL: - if (us->len == 0) { - len = 1; + if (us->client) { + h3c->client_control = NULL; } else { - len = (us->buf[0] >> 6) + 1 - us->len; - } - - n = c->recv(c, p, len); - - if (n == NGX_ERROR) { - goto failed; - } - - if (n == NGX_AGAIN) { - break; - } - - us->len += n; - - if (n != len) { - break; - } - - if ((us->buf[0] >> 6) + 1 == us->len) { - us->type = ngx_http_v3_decode_varlen_int(us->buf); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 stream type:%ui", us->type); - - switch (us->type) { - - case NGX_HTTP_V3_ENCODER_STREAM: - if (h3c->client_encoder) { - goto failed; - } - - h3c->client_encoder = c; - rev->handler = ngx_http_v3_client_encoder_handler; - break; - - case NGX_HTTP_V3_DECODER_STREAM: - if (h3c->client_decoder) { - goto failed; - } - - h3c->client_decoder = c; - rev->handler = ngx_http_v3_client_decoder_handler; - break; - - case NGX_HTTP_V3_CONTROL_STREAM: - case NGX_HTTP_V3_PUSH_STREAM: - - /* ignore these */ - - default: - rev->handler = ngx_http_v3_dummy_stream_handler; - } - - rev->handler(rev); - return; + h3c->server_control = NULL; } - } - - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - goto failed; - } - - return; -failed: - - ngx_http_v3_close_uni_stream(c); -} - - -static void -ngx_http_v3_dummy_stream_handler(ngx_event_t *rev) -{ - u_char buf[128]; - ngx_connection_t *c; - - /* read out and ignore */ - - c = rev->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy stream reader"); - - while (rev->ready) { - if (c->recv(c, buf, sizeof(buf)) == NGX_ERROR) { - goto failed; - } - } - - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - goto failed; + break; } - - return; - -failed: - - ngx_http_v3_close_uni_stream(c); } static void -ngx_http_v3_client_encoder_handler(ngx_event_t *rev) +ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) { - u_char v; + u_char ch; ssize_t n; - ngx_str_t name, value; - ngx_uint_t dynamic, huffman, index, offset; - ngx_connection_t *c, *pc; + ngx_connection_t *c; + ngx_http_v3_connection_t *h3c; ngx_http_v3_uni_stream_t *st; - enum { - sw_start = 0, - sw_inr_name_index, - sw_inr_value_length, - sw_inr_read_value_length, - sw_inr_value, - sw_iwnr_name_length, - sw_iwnr_name, - sw_iwnr_value_length, - sw_iwnr_read_value_length, - sw_iwnr_value, - sw_capacity, - sw_duplicate - } state; c = rev->data; st = c->data; - pc = c->qs->parent; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client encoder"); + h3c = c->qs->parent->data; - state = st->state; - dynamic = st->dynamic; - huffman = st->huffman; - index = st->index; - offset = st->offset; - name = st->name; - value = st->value; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read stream type"); while (rev->ready) { - /* XXX limit checks */ - /* XXX buffer input */ - - n = c->recv(c, &v, 1); + n = c->recv(c, &ch, 1); - if (n == NGX_ERROR || n == 0) { + if (n == NGX_ERROR) { goto failed; } - if (n != 1) { + if (n == NGX_AGAIN || n != 1) { break; } - /* XXX v -> ch */ - - switch (state) { - - case sw_start: - - if (v & 0x80) { - /* Insert With Name Reference */ - - dynamic = (v & 0x40) ? 0 : 1; - index = v & 0x3f; - - if (index != 0x3f) { - state = sw_inr_value_length; - break; - } - - index = 0; - state = sw_inr_name_index; - break; - } - - if (v & 0x40) { - /* Insert Without Name Reference */ - - huffman = (v & 0x20) ? 1 : 0; - name.len = v & 0x1f; - - if (name.len != 0x1f) { - offset = 0; - state = sw_iwnr_name; - break; - } - - name.len = 0; - state = sw_iwnr_name_length; - break; - } - - if (v & 0x20) { - /* Set Dynamic Table Capacity */ - - index = v & 0x1f; - - if (index != 0x1f) { - if (ngx_http_v3_set_capacity(c, index) != NGX_OK) { - goto failed; - } - - break; - } - - index = 0; - state = sw_capacity; - break; - } - - /* Duplicate */ - - index = v & 0x1f; - - if (index != 0x1f) { - if (ngx_http_v3_duplicate(c, index) != NGX_OK) { - goto failed; - } - - break; - } - - index = 0; - state = sw_duplicate; - break; - - case sw_inr_name_index: - - index = (index << 7) + (v & 0x7f); - if (v & 0x80) { - break; - } - - index += 0x3f; - state = sw_inr_value_length; - break; - - case sw_inr_value_length: - - huffman = (v & 0x80) ? 1 : 0; - value.len = v & 0x7f; - - if (value.len == 0) { - value.data = NULL; - - if (ngx_http_v3_ref_insert(c, dynamic, index, &value) != NGX_OK) - { - goto failed; - } - - state = sw_start; - break; - } - - if (value.len != 0x7f) { - value.data = ngx_pnalloc(pc->pool, value.len); - if (value.data == NULL) { - goto failed; - } - - state = sw_inr_value; - offset = 0; - break; - } + st->type = ch; - value.len = 0; - state = sw_inr_read_value_length; - break; - - case sw_inr_read_value_length: + switch (st->type) { - value.len = (value.len << 7) + (v & 0x7f); - if (v & 0x80) { - break; - } + case NGX_HTTP_V3_STREAM_ENCODER: - value.len += 0x7f; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 encoder stream"); - value.data = ngx_pnalloc(pc->pool, value.len); - if (value.data == NULL) { + if (h3c->client_encoder) { goto failed; } - state = sw_inr_value; - offset = 0; - break; - - case sw_inr_value: - - value.data[offset++] = v; - if (offset != value.len) { - break; - } - - if (huffman) { - if (ngx_http_v3_decode_huffman(pc, &value) != NGX_OK) { - goto failed; - } - } - - if (ngx_http_v3_ref_insert(c, dynamic, index, &value) != NGX_OK) { - goto failed; - } + h3c->client_encoder = c; + st->handler = ngx_http_v3_parse_encoder; + n = sizeof(ngx_http_v3_parse_encoder_t); - state = sw_start; break; - case sw_iwnr_name_length: + case NGX_HTTP_V3_STREAM_DECODER: - name.len = (name.len << 7) + (v & 0x7f); - if (v & 0x80) { - break; - } - - name.len += 0x1f; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 decoder stream"); - name.data = ngx_pnalloc(pc->pool, name.len); - if (name.data == NULL) { + if (h3c->client_decoder) { goto failed; } - offset = 0; - state = sw_iwnr_name; - break; - - case sw_iwnr_name: - - name.data[offset++] = v; - if (offset != name.len) { - break; - } - - if (huffman) { - if (ngx_http_v3_decode_huffman(pc, &name) != NGX_OK) { - goto failed; - } - } - - state = sw_iwnr_value_length; - break; - - case sw_iwnr_value_length: - - huffman = (v & 0x80) ? 1 : 0; - value.len = v & 0x7f; - - if (value.len == 0) { - value.data = NULL; - - if (ngx_http_v3_insert(c, &name, &value) != NGX_OK) { - goto failed; - } - - state = sw_start; - break; - } - - if (value.len != 0x7f) { - value.data = ngx_pnalloc(pc->pool, value.len); - if (value.data == NULL) { - goto failed; - } - - offset = 0; - state = sw_iwnr_value; - break; - } - - state = sw_iwnr_read_value_length; - break; - - case sw_iwnr_read_value_length: - - value.len = (value.len << 7) + (v & 0x7f); - if (v & 0x80) { - break; - } - - value.data = ngx_pnalloc(pc->pool, value.len); - if (value.data == NULL) { - goto failed; - } + h3c->client_decoder = c; + st->handler = ngx_http_v3_parse_decoder; + n = sizeof(ngx_http_v3_parse_decoder_t); - offset = 0; - state = sw_iwnr_value; break; - case sw_iwnr_value: - - value.data[offset++] = v; - if (offset != value.len) { - break; - } + case NGX_HTTP_V3_STREAM_CONTROL: - if (huffman) { - if (ngx_http_v3_decode_huffman(pc, &value) != NGX_OK) { - goto failed; - } - } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 control stream"); - if (ngx_http_v3_insert(c, &name, &value) != NGX_OK) { + if (h3c->client_control) { goto failed; } - state = sw_start; - break; - - - case sw_capacity: - - index = (index << 7) + (v & 0x7f); - if (v & 0x80) { - break; - } - - index += 0x1f; + h3c->client_control = c; + st->handler = ngx_http_v3_parse_control; + n = sizeof(ngx_http_v3_parse_control_t); - if (ngx_http_v3_set_capacity(c, index) != NGX_OK) { - goto failed; - } - - state = sw_start; break; - case sw_duplicate: - - index = (index << 7) + (v & 0x7f); - if (v & 0x80) { - break; - } + default: - index += 0x1f; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 stream 0x%02xi", st->type); + n = 0; + } - if (ngx_http_v3_duplicate(c, index) != NGX_OK) { + if (n) { + st->data = ngx_pcalloc(c->pool, n); + if (st->data == NULL) { goto failed; } - - state = sw_start; - break; } - } - st->state = state; - st->dynamic = dynamic; - st->huffman = huffman; - st->index = index; - st->offset = offset; - st->name = name; - st->value = value; + rev->handler = ngx_http_v3_uni_read_handler; + ngx_http_v3_uni_read_handler(rev); + return; + } if (ngx_handle_read_event(rev, 0) != NGX_OK) { goto failed; @@ -624,158 +249,61 @@ failed: static void -ngx_http_v3_client_decoder_handler(ngx_event_t *rev) +ngx_http_v3_uni_read_handler(ngx_event_t *rev) { - u_char v; + u_char buf[128]; ssize_t n; - ngx_uint_t index; + ngx_int_t rc, i; ngx_connection_t *c; ngx_http_v3_uni_stream_t *st; - enum { - sw_start = 0, - sw_ack_header, - sw_cancel_stream, - sw_inc_insert_count - } state; c = rev->data; st = c->data; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client decoder"); - - state = st->state; - index = st->index; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler"); while (rev->ready) { - /* XXX limit checks */ - /* XXX buffer input */ - - n = c->recv(c, &v, 1); + n = c->recv(c, buf, sizeof(buf)); if (n == NGX_ERROR || n == 0) { goto failed; } - if (n != 1) { + if (n == NGX_AGAIN) { break; } - switch (state) { - - case sw_start: - - if (v & 0x80) { - /* Header Acknowledgement */ - - index = v & 0x7f; - - if (index != 0x7f) { - if (ngx_http_v3_ack_header(c, index) != NGX_OK) { - goto failed; - } - - break; - } - - index = 0; - state = sw_ack_header; - break; - } - - if (v & 0x40) { - /* Stream Cancellation */ - - index = v & 0x3f; - - if (index != 0x3f) { - if (ngx_http_v3_cancel_stream(c, index) != NGX_OK) { - goto failed; - } - - break; - } - - index = 0; - state = sw_cancel_stream; - break; - } - - /* Insert Count Increment */ - - index = v & 0x3f; - - if (index != 0x3f) { - if (ngx_http_v3_inc_insert_count(c, index) != NGX_OK) { - goto failed; - } - - break; - } - - index = 0; - state = sw_inc_insert_count; - break; - - case sw_ack_header: - - index = (index << 7) + (v & 0x7f); - if (v & 0x80) { - break; - } - - index += 0x7f; - - if (ngx_http_v3_ack_header(c, index) != NGX_OK) { - goto failed; - } - - state = sw_start; - break; - - case sw_cancel_stream: + if (st->handler == NULL) { + continue; + } - index = (index << 7) + (v & 0x7f); - if (v & 0x80) { - break; - } + for (i = 0; i < n; i++) { - index += 0x3f; + rc = st->handler(c, st->data, buf[i]); - if (ngx_http_v3_cancel_stream(c, index) != NGX_OK) { + if (rc == NGX_ERROR) { goto failed; } - state = sw_start; - break; - - case sw_inc_insert_count: - - index = (index << 7) + (v & 0x7f); - if (v & 0x80) { - break; - } - - index += 0x3f; - - if (ngx_http_v3_inc_insert_count(c, index) != NGX_OK) { - goto failed; + if (rc == NGX_DONE) { + goto done; } - state = sw_start; - break; + /* rc == NGX_AGAIN */ } } - st->state = state; - st->index = index; - if (ngx_handle_read_event(rev, 0) != NGX_OK) { goto failed; } return; +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read done"); + failed: ngx_http_v3_close_uni_stream(c); @@ -835,7 +363,7 @@ failed: static ngx_connection_t * -ngx_http_v3_get_server_encoder(ngx_connection_t *c) +ngx_http_v3_get_control(ngx_connection_t *c) { ngx_http_v3_connection_t *h3c; @@ -843,7 +371,7 @@ ngx_http_v3_get_server_encoder(ngx_connection_t *c) if (h3c->server_encoder == NULL) { h3c->server_encoder = ngx_http_v3_create_uni_stream(c, - NGX_HTTP_V3_ENCODER_STREAM); + NGX_HTTP_V3_STREAM_CONTROL); } return h3c->server_encoder; @@ -851,18 +379,34 @@ ngx_http_v3_get_server_encoder(ngx_connection_t *c) static ngx_connection_t * -ngx_http_v3_get_server_decoder(ngx_connection_t *c) +ngx_http_v3_get_encoder(ngx_connection_t *c) { ngx_http_v3_connection_t *h3c; h3c = c->qs->parent->data; - if (h3c->server_decoder == NULL) { - h3c->server_decoder = ngx_http_v3_create_uni_stream(c, - NGX_HTTP_V3_DECODER_STREAM); + if (h3c->server_encoder == NULL) { + h3c->server_encoder = ngx_http_v3_create_uni_stream(c, + NGX_HTTP_V3_STREAM_ENCODER); } - return h3c->server_decoder; + return h3c->server_encoder; +} + + +static ngx_connection_t * +ngx_http_v3_get_decoder(ngx_connection_t *c) +{ + ngx_http_v3_connection_t *h3c; + + h3c = c->qs->parent->data; + + if (h3c->server_encoder == NULL) { + h3c->server_encoder = ngx_http_v3_create_uni_stream(c, + NGX_HTTP_V3_STREAM_DECODER); + } + + return h3c->server_encoder; } @@ -878,7 +422,7 @@ ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, "http3 client ref insert, %s[%ui] \"%V\"", dynamic ? "dynamic" : "static", index, value); - ec = ngx_http_v3_get_server_encoder(c); + ec = ngx_http_v3_get_encoder(c); if (ec == NULL) { return NGX_ERROR; } @@ -923,7 +467,7 @@ ngx_http_v3_client_insert(ngx_connection_t *c, ngx_str_t *name, ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client insert \"%V\":\"%V\"", name, value); - ec = ngx_http_v3_get_server_encoder(c); + ec = ngx_http_v3_get_encoder(c); if (ec == NULL) { return NGX_ERROR; } @@ -972,7 +516,7 @@ ngx_http_v3_client_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client set capacity %ui", capacity); - ec = ngx_http_v3_get_server_encoder(c); + ec = ngx_http_v3_get_encoder(c); if (ec == NULL) { return NGX_ERROR; } @@ -999,7 +543,7 @@ ngx_http_v3_client_duplicate(ngx_connection_t *c, ngx_uint_t index) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client duplicate %ui", index); - ec = ngx_http_v3_get_server_encoder(c); + ec = ngx_http_v3_get_encoder(c); if (ec == NULL) { return NGX_ERROR; } @@ -1026,7 +570,7 @@ ngx_http_v3_client_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client ack header %ui", stream_id); - dc = ngx_http_v3_get_server_decoder(c); + dc = ngx_http_v3_get_decoder(c); if (dc == NULL) { return NGX_ERROR; } @@ -1053,7 +597,7 @@ ngx_http_v3_client_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client cancel stream %ui", stream_id); - dc = ngx_http_v3_get_server_decoder(c); + dc = ngx_http_v3_get_decoder(c); if (dc == NULL) { return NGX_ERROR; } @@ -1080,7 +624,7 @@ ngx_http_v3_client_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client increment insert count %ui", inc); - dc = ngx_http_v3_get_server_decoder(c); + dc = ngx_http_v3_get_decoder(c); if (dc == NULL) { return NGX_ERROR; } diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index 1c1d8c051..b4fc90b38 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -383,3 +383,33 @@ ngx_http_v3_new_header(ngx_connection_t *c) return NGX_OK; } + + +ngx_int_t +ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value) +{ + switch (id) { + + case NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param QPACK_MAX_TABLE_CAPACITY:%uL", value); + break; + + case NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param SETTINGS_MAX_HEADER_LIST_SIZE:%uL", value); + break; + + case NGX_HTTP_V3_PARAM_BLOCKED_STREAMS: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param QPACK_BLOCKED_STREAMS:%uL", value); + break; + + default: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param #%uL:%uL", id, value); + } + + return NGX_OK; +} -- cgit From 85430505feb676dd45ca65ea75a0ccd0ed32ea3a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 18 Mar 2020 14:09:50 +0300 Subject: Removed comment. --- src/http/v3/ngx_http_v3_parse.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index e0b28a6c3..0fd44bc40 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -208,7 +208,6 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, if (--st->length == 0) { if (rc != NGX_DONE) { -ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "XXX len"); return NGX_ERROR; } -- cgit From d36684447c0c9f2e2324f9dc43e534ccc7edec18 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 18 Mar 2020 14:10:44 +0300 Subject: Fixed HTTP/3 server stream creation. --- src/http/v3/ngx_http_v3_streams.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 18e38a68d..6a5610b9a 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -321,7 +321,7 @@ ngx_http_v3_create_uni_stream(ngx_connection_t *c, ngx_uint_t type) ngx_pool_cleanup_t *cln; ngx_http_v3_uni_stream_t *us; - sc = ngx_quic_create_uni_stream(c->qs->parent); + sc = ngx_quic_create_uni_stream(c); if (sc == NULL) { return NULL; } @@ -369,8 +369,8 @@ ngx_http_v3_get_control(ngx_connection_t *c) h3c = c->qs->parent->data; - if (h3c->server_encoder == NULL) { - h3c->server_encoder = ngx_http_v3_create_uni_stream(c, + if (h3c->server_control == NULL) { + h3c->server_control = ngx_http_v3_create_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); } @@ -401,8 +401,8 @@ ngx_http_v3_get_decoder(ngx_connection_t *c) h3c = c->qs->parent->data; - if (h3c->server_encoder == NULL) { - h3c->server_encoder = ngx_http_v3_create_uni_stream(c, + if (h3c->server_decoder == NULL) { + h3c->server_decoder = ngx_http_v3_create_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); } -- cgit From 04d037b239440bf7a2f422eb4b7c4e4e7652939e Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 18 Mar 2020 15:28:20 +0300 Subject: Fixed pointer increment while parsing HTTP/3 header. --- src/http/v3/ngx_http_v3_request.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index e6cd27183..a9adcf8c2 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -83,9 +83,8 @@ ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b) break; } - for ( /* void */ ; b->pos < b->last; b->pos++) { - - rc = ngx_http_v3_parse_headers(c, st, *b->pos); + while (b->pos < b->last) { + rc = ngx_http_v3_parse_headers(c, st, *b->pos++); if (rc == NGX_ERROR) { goto failed; -- cgit From 5aa8e519c9fecc00b3a74781716ceb66609c5661 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 18 Mar 2020 16:37:16 +0300 Subject: Moved setting QUIC methods to runtime. This allows listening to both https and http3 in the same server. Also, the change eliminates the ssl_quic directive. --- src/http/modules/ngx_http_ssl_module.c | 14 -------------- src/http/modules/ngx_http_ssl_module.h | 1 - 2 files changed, 15 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 8640c2211..4b480a006 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -249,13 +249,6 @@ static ngx_command_t ngx_http_ssl_commands[] = { offsetof(ngx_http_ssl_srv_conf_t, early_data), NULL }, - { ngx_string("ssl_quic"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_ssl_srv_conf_t, quic), - NULL }, - ngx_null_command }; @@ -575,7 +568,6 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) sscf->enable = NGX_CONF_UNSET; sscf->prefer_server_ciphers = NGX_CONF_UNSET; sscf->early_data = NGX_CONF_UNSET; - sscf->quic = NGX_CONF_UNSET; sscf->buffer_size = NGX_CONF_UNSET_SIZE; sscf->verify = NGX_CONF_UNSET_UINT; sscf->verify_depth = NGX_CONF_UNSET_UINT; @@ -620,8 +612,6 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->early_data, prev->early_data, 0); - ngx_conf_merge_value(conf->quic, prev->quic, 0); - ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); @@ -867,10 +857,6 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - if (ngx_ssl_quic(cf, &conf->ssl, conf->quic) != NGX_OK) { - return NGX_CONF_ERROR; - } - return NGX_CONF_OK; } diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h index 310d7c737..26fdccfe4 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -21,7 +21,6 @@ typedef struct { ngx_flag_t prefer_server_ciphers; ngx_flag_t early_data; - ngx_flag_t quic; ngx_uint_t protocols; -- cgit From e63accd7bd222aebd7cf4f90aeb8cca617d01b94 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 18 Mar 2020 20:22:16 +0300 Subject: HTTP/3 $request_line variable. --- src/http/ngx_http_request.c | 4 +- src/http/v3/ngx_http_v3_request.c | 573 +++----------------------------------- 2 files changed, 37 insertions(+), 540 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 166d8ffba..687de931c 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1177,7 +1177,7 @@ ngx_http_process_request_line(ngx_event_t *rev) r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; - r->request_length = r->header_in->pos - r->request_start; + r->request_length = r->header_in->pos - r->request_start; /* XXX */ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http request line: \"%V\"", &r->request_line); @@ -1593,7 +1593,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header done"); - r->request_length += r->header_in->pos - r->header_name_start; + r->request_length += r->header_in->pos - r->header_name_start; /* XXX */ r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index a9adcf8c2..75488db1d 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -40,6 +40,8 @@ struct { ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b) { + size_t n; + u_char *p; ngx_int_t rc; ngx_str_t *name, *value; ngx_connection_t *c; @@ -97,23 +99,45 @@ ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b) name = &st->header_rep.header.name; value = &st->header_rep.header.value; - if (r->state == sw_start - && ngx_http_v3_process_pseudo_header(r, name, value) != NGX_OK) - { - if (rc == NGX_DONE) { - r->state = sw_last; - } else { + if (r->state == sw_start) { + + if (ngx_http_v3_process_pseudo_header(r, name, value) == NGX_OK) { + if (rc == NGX_OK) { + continue; + } + + r->state = sw_done; + + } else if (rc == NGX_OK) { r->state = sw_prev; + + } else { + r->state = sw_last; + } + + n = (r->method_end - r->method_start) + 1 + + (r->uri_end - r->uri_start) + 1 + + sizeof("HTTP/3") - 1; + + p = ngx_pnalloc(c->pool, n); + if (p == NULL) { + goto failed; } + r->request_start = p; + + p = ngx_cpymem(p, r->method_start, r->method_end - r->method_start); + *p++ = ' '; + p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start); + *p++ = ' '; + p = ngx_cpymem(p, "HTTP/3", sizeof("HTTP/3") - 1); + + r->request_end = p; + } else if (rc == NGX_DONE) { r->state = sw_done; } - if (r->state == sw_start) { - continue; - } - r->header_name_start = name->data; r->header_name_end = name->data + name->len; r->header_start = value->data; @@ -139,533 +163,6 @@ done: return NGX_HTTP_PARSE_HEADER_DONE; } -#if 0 -ngx_int_t -ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t pseudo) -{ - u_char *p, ch; - ngx_str_t name, value; - ngx_int_t rc; - ngx_uint_t length, index, insert_count, sign, base, delta_base, - huffman, dynamic, offset; - ngx_connection_t *c; - ngx_http_v3_header_t *h; - enum { - sw_start = 0, - sw_length, - sw_length_1, - sw_length_2, - sw_length_3, - sw_header_block, - sw_req_insert_count, - sw_delta_base, - sw_read_delta_base, - sw_header, - sw_old_header, - sw_header_ri, - sw_header_pbi, - sw_header_lri, - sw_header_lpbi, - sw_header_l_name_len, - sw_header_l_name, - sw_header_value_len, - sw_header_read_value_len, - sw_header_value - } state; - - c = r->connection; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header, pseudo:%ui", pseudo); - - if (r->state == sw_old_header) { - r->state = sw_header; - return NGX_OK; - } - - length = r->h3_length; - index = r->h3_index; - insert_count = r->h3_insert_count; - sign = r->h3_sign; - delta_base = r->h3_delta_base; - huffman = r->h3_huffman; - dynamic = r->h3_dynamic; - offset = r->h3_offset; - - name.data = r->header_name_start; - name.len = r->header_name_end - r->header_name_start; - value.data = r->header_start; - value.len = r->header_end - r->header_start; - - if (r->state == sw_start) { - length = 1; - } - -again: - - state = r->state; - - if (state == sw_header && length == 0) { - r->state = sw_start; - return NGX_HTTP_PARSE_HEADER_DONE; - } - - for (p = b->pos; p < b->last; p++) { - - if (state >= sw_header_block && length-- == 0) { - goto failed; - } - - ch = *p; - - switch (state) { - - case sw_start: - - if (ch != NGX_HTTP_V3_FRAME_HEADERS) { - goto failed; - } - - r->request_start = p; - state = sw_length; - break; - - case sw_length: - - length = ch; - if (length & 0xc0) { - state = sw_length_1; - break; - } - - state = sw_header_block; - break; - - case sw_length_1: - - length = (length << 8) + ch; - if ((length & 0xc000) != 0x4000) { - state = sw_length_2; - break; - } - - length &= 0x3fff; - state = sw_header_block; - break; - - case sw_length_2: - - length = (length << 8) + ch; - if ((length & 0xc00000) != 0x800000) { - state = sw_length_3; - break; - } - - length &= 0x3fffff; - state = sw_header_block; - break; - - case sw_length_3: - - length = (length << 8) + ch; - length &= 0x3fffffff; - state = sw_header_block; - break; - - case sw_header_block: - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 header block length:%ui", length); - - if (ch != 0xff) { - insert_count = ch; - state = sw_delta_base; - break; - } - - insert_count = 0; - state = sw_req_insert_count; - break; - - case sw_req_insert_count: - - insert_count = (insert_count << 7) + (ch & 0x7f); - if (ch & 0x80) { - break; - } - - insert_count += 0xff; - state = sw_delta_base; - break; - - case sw_delta_base: - - sign = (ch & 0x80) ? 1 : 0; - delta_base = ch & 0x7f; - - if (delta_base != 0x7f) { - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 header block " - "insert_count:%ui, sign:%ui, delta_base:%ui", - insert_count, sign, delta_base); - goto done; - } - - delta_base = 0; - state = sw_read_delta_base; - break; - - case sw_read_delta_base: - - delta_base = (delta_base << 7) + (ch & 0x7f); - if (ch & 0x80) { - break; - } - - delta_base += 0x7f; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 header block " - "insert_count:%ui, sign:%ui, delta_base:%ui", - insert_count, sign, delta_base); - goto done; - - case sw_header: - - index = 0; - huffman = 0; - ngx_str_null(&name); - ngx_str_null(&value); - - if (ch & 0x80) { - /* Indexed Header Field */ - - dynamic = (ch & 0x40) ? 0 : 1; - index = ch & 0x3f; - - if (index != 0x3f) { - goto done; - } - - index = 0; - state = sw_header_ri; - break; - } - - if (ch & 0x40) { - /* Literal Header Field With Name Reference */ - - dynamic = (ch & 0x10) ? 0 : 1; - index = ch & 0x0f; - - if (index != 0x0f) { - state = sw_header_value_len; - break; - } - - index = 0; - state = sw_header_lri; - break; - } - - if (ch & 0x20) { - /* Literal Header Field Without Name Reference */ - - huffman = (ch & 0x08) ? 1 : 0; - name.len = ch & 0x07; - - if (name.len == 0) { - goto failed; - } - - if (name.len != 0x07) { - offset = 0; - state = sw_header_l_name; - break; - } - - name.len = 0; - state = sw_header_l_name_len; - break; - } - - if (ch & 10) { - /* Indexed Header Field With Post-Base Index */ - - dynamic = 2; - index = ch & 0x0f; - - if (index != 0x0f) { - goto done; - } - - index = 0; - state = sw_header_pbi; - break; - } - - /* Literal Header Field With Post-Base Name Reference */ - - dynamic = 2; - index = ch & 0x07; - - if (index != 0x07) { - state = sw_header_value_len; - break; - } - - index = 0; - state = sw_header_lpbi; - break; - - case sw_header_ri: - - index = (index << 7) + (ch & 0x7f); - if (ch & 0x80) { - break; - } - - index += 0x3f; - goto done; - - case sw_header_pbi: - - index = (index << 7) + (ch & 0x7f); - if (ch & 0x80) { - break; - } - - index += 0x0f; - goto done; - - case sw_header_lri: - - index = (index << 7) + (ch & 0x7f); - if (ch & 0x80) { - break; - } - - index += 0x0f; - state = sw_header_value_len; - break; - - case sw_header_lpbi: - - index = (index << 7) + (ch & 0x7f); - if (ch & 0x80) { - break; - } - - index += 0x07; - state = sw_header_value_len; - break; - - - case sw_header_l_name_len: - - name.len = (name.len << 7) + (ch & 0x7f); - if (ch & 0x80) { - break; - } - - name.len += 0x07; - offset = 0; - state = sw_header_l_name; - break; - - case sw_header_l_name: - if (offset++ == 0) { - name.data = p; - } - - if (offset != name.len) { - break; - } - - if (huffman) { - if (ngx_http_v3_decode_huffman(c, &name) != NGX_OK) { - goto failed; - } - } - - state = sw_header_value_len; - break; - - case sw_header_value_len: - - huffman = (ch & 0x80) ? 1 : 0; - value.len = ch & 0x7f; - - if (value.len == 0) { - value.data = p; - goto done; - } - - if (value.len != 0x7f) { - offset = 0; - state = sw_header_value; - break; - } - - value.len = 0; - state = sw_header_read_value_len; - break; - - case sw_header_read_value_len: - - value.len = (value.len << 7) + (ch & 0x7f); - if (ch & 0x80) { - break; - } - - value.len += 0x7f; - offset = 0; - state = sw_header_value; - break; - - case sw_header_value: - - if (offset++ == 0) { - value.data = p; - } - - if (offset != value.len) { - break; - } - - if (huffman) { - if (ngx_http_v3_decode_huffman(c, &value) != NGX_OK) { - goto failed; - } - } - - goto done; - - case sw_old_header: - - break; - } - } - - b->pos = p; - r->state = state; - r->h3_length = length; - r->h3_index = index; - r->h3_insert_count = insert_count; - r->h3_sign = sign; - r->h3_delta_base = delta_base; - r->h3_huffman = huffman; - r->h3_dynamic = dynamic; - r->h3_offset = offset; - - /* XXX fix large reallocations */ - r->header_name_start = name.data; - r->header_name_end = name.data + name.len; - r->header_start = value.data; - r->header_end = value.data + value.len; - - /* XXX r->lowcase_index = i; */ - - return NGX_AGAIN; - -done: - - b->pos = p + 1; - r->state = sw_header; - r->h3_length = length; - r->h3_insert_count = insert_count; - r->h3_sign = sign; - r->h3_delta_base = delta_base; - - if (state < sw_header) { - if (ngx_http_v3_check_insert_count(c, insert_count) != NGX_OK) { - return NGX_DONE; - } - - goto again; - } - - if (sign == 0) { - base = insert_count + delta_base; - } else { - base = insert_count - delta_base - 1; - } - - ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 header %s[%ui], base:%ui, \"%V\":\"%V\"", - dynamic ? "dynamic" : "static", index, base, &name, &value); - - if (name.data == NULL) { - - if (dynamic == 2) { - index = base - index - 1; - } else if (dynamic == 1) { - index += base; - } - - h = ngx_http_v3_lookup_table(c, dynamic, index); - if (h == NULL) { - goto failed; - } - - name = h->name; - - if (value.data == NULL) { - value = h->value; - } - } - - /* XXX ugly reallocation for the trailing '\0' */ - - p = ngx_pnalloc(c->pool, name.len + value.len + 2); - if (p == NULL) { - return NGX_ERROR; - } - - ngx_memcpy(p, name.data, name.len); - name.data = p; - ngx_memcpy(p + name.len + 1, value.data, value.len); - value.data = p + name.len + 1; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 header \"%V\":\"%V\"", &name, &value); - - if (pseudo) { - rc = ngx_http_v3_process_pseudo_header(r, &name, &value); - - if (rc == NGX_ERROR) { - goto failed; - } - - if (rc == NGX_OK) { - r->request_end = p + 1; - goto again; - } - - /* rc == NGX_DONE */ - - r->state = sw_old_header; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 header left:%ui", length); - - r->header_name_start = name.data; - r->header_name_end = name.data + name.len; - r->header_start = value.data; - r->header_end = value.data + value.len; - r->header_hash = ngx_hash_key(name.data, name.len); /* XXX */ - - /* XXX r->lowcase_index = i; */ - - return NGX_OK; - -failed: - - return NGX_HTTP_PARSE_INVALID_REQUEST; -} -#endif - static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, @@ -675,7 +172,7 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_connection_t *c; if (name->len == 0 || name->data[0] != ':') { - return NGX_DECLINED; + return NGX_DONE; } c = r->connection; -- cgit From 8ad2707d4f5c007ec308262ea03481d95eee7e02 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 19 Mar 2020 15:03:09 +0300 Subject: Fixed header creation for header_only responses in HTTP/3. --- src/http/ngx_http_header_filter_module.c | 29 ++++++++++++++++------------- src/http/v3/ngx_http_v3_request.c | 26 +++++++++++++++----------- 2 files changed, 31 insertions(+), 24 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c index 02e251f79..85edf3657 100644 --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -179,6 +179,22 @@ ngx_http_header_filter(ngx_http_request_t *r) return NGX_OK; } + if (r->http_version < NGX_HTTP_VERSION_10) { + return NGX_OK; + } + + if (r->method == NGX_HTTP_HEAD) { + r->header_only = 1; + } + + if (r->headers_out.status_line.len == 0) { + if (r->headers_out.status == NGX_HTTP_NO_CONTENT + || r->headers_out.status == NGX_HTTP_NOT_MODIFIED) + { + r->header_only = 1; + } + } + #if (NGX_HTTP_V3) if (r->http_version == NGX_HTTP_VERSION_30) { @@ -194,14 +210,6 @@ ngx_http_header_filter(ngx_http_request_t *r) #endif - if (r->http_version < NGX_HTTP_VERSION_10) { - return NGX_OK; - } - - if (r->method == NGX_HTTP_HEAD) { - r->header_only = 1; - } - if (r->headers_out.last_modified_time != -1) { if (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT @@ -235,7 +243,6 @@ ngx_http_header_filter(ngx_http_request_t *r) /* 2XX */ if (status == NGX_HTTP_NO_CONTENT) { - r->header_only = 1; ngx_str_null(&r->headers_out.content_type); r->headers_out.last_modified_time = -1; r->headers_out.last_modified = NULL; @@ -252,10 +259,6 @@ ngx_http_header_filter(ngx_http_request_t *r) { /* 3XX */ - if (status == NGX_HTTP_NOT_MODIFIED) { - r->header_only = 1; - } - status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX; status_line = &ngx_http_status_lines[status]; len += ngx_http_status_lines[status].len; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 75488db1d..9a2654fd4 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -259,7 +259,7 @@ ngx_http_v3_create_header(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create header"); /* XXX support chunked body in the chunked filter */ - if (r->headers_out.content_length_n == -1) { + if (!r->header_only && r->headers_out.content_length_n == -1) { return NULL; } @@ -310,11 +310,11 @@ ngx_http_v3_create_header(ngx_http_request_t *r) + ngx_http_v3_encode_prefix_int(NULL, n, 7) + n; } - if (r->headers_out.content_length_n == 0) { - len += ngx_http_v3_encode_prefix_int(NULL, 4, 6); - - } else { + if (r->headers_out.content_length_n > 0) { len += ngx_http_v3_encode_prefix_int(NULL, 4, 4) + 1 + NGX_OFF_T_LEN; + + } else if (r->headers_out.content_length_n == 0) { + len += ngx_http_v3_encode_prefix_int(NULL, 4, 6); } if (r->headers_out.last_modified == NULL @@ -454,18 +454,18 @@ ngx_http_v3_create_header(ngx_http_request_t *r) } } - if (r->headers_out.content_length_n == 0) { - /* content-length: 0 */ - *b->last = 0xc0; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 6); - - } else if (r->headers_out.content_length_n > 0) { + if (r->headers_out.content_length_n > 0) { /* content-length: 0 */ *b->last = 0x70; b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 4); p = b->last++; b->last = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); *p = b->last - p - 1; + + } else if (r->headers_out.content_length_n == 0) { + /* content-length: 0 */ + *b->last = 0xc0; + b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 6); } if (r->headers_out.last_modified == NULL @@ -521,6 +521,10 @@ ngx_http_v3_create_header(ngx_http_request_t *r) b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); } + if (r->header_only) { + b->last_buf = 1; + } + cl = ngx_alloc_chain_link(c->pool); if (cl == NULL) { return NULL; -- cgit From 30de0ca52dc7d460a184781e82288b0e2723ebce Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Fri, 20 Mar 2020 13:47:44 +0300 Subject: Configurable transport parameters. - integer parameters can be configured using the following directives: quic_max_idle_timeout quic_max_ack_delay quic_max_packet_size quic_initial_max_data quic_initial_max_stream_data_bidi_local quic_initial_max_stream_data_bidi_remote quic_initial_max_stream_data_uni quic_initial_max_streams_bidi quic_initial_max_streams_uni quic_ack_delay_exponent quic_active_migration quic_active_connection_id_limit - only following parameters are actually sent: active_connection_id_limit initial_max_streams_uni initial_max_streams_bidi initial_max_stream_data_bidi_local initial_max_stream_data_bidi_remote initial_max_stream_data_uni (other parameters are to be added into ngx_quic_create_transport_params() function as needed, should be easy now) - draft 24 and draft 27 are now supported (at compile-time using quic_version macro) --- src/http/ngx_http_request.c | 5 +- src/http/v3/ngx_http_v3.h | 5 ++ src/http/v3/ngx_http_v3_module.c | 182 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 189 insertions(+), 3 deletions(-) (limited to 'src/http') 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 @@ -39,6 +39,11 @@ #define NGX_HTTP_V3_PARAM_BLOCKED_STREAMS 0x07 +typedef struct { + ngx_quic_tp_t quic; +} ngx_http_v3_srv_conf_t; + + typedef struct { ngx_http_connection_t hc; 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; +} + -- cgit From 4096676897ba547079e96230a16587150835d356 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Fri, 20 Mar 2020 12:44:45 +0300 Subject: Adedd the http "quic" variable. The value is literal "quic" for requests passed over HTTP/3, and empty string otherwise. --- src/http/v3/ngx_http_v3_module.c | 53 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 6d3f34d41..20137b377 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -100,13 +100,16 @@ static ngx_command_t ngx_http_v3_commands[] = { }; +static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); 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 */ + ngx_http_v3_add_variables, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ @@ -136,6 +139,54 @@ ngx_module_t ngx_http_v3_module = { }; +static ngx_http_variable_t ngx_http_v3_vars[] = { + { ngx_string("quic"), NULL, ngx_http_variable_quic, + 0, 0, 0 }, + + ngx_http_null_variable +}; + + +static ngx_int_t +ngx_http_variable_quic(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->connection->qs) { + + v->len = 4; + v->valid = 1; + v->no_cacheable = 1; + v->not_found = 0; + v->data = (u_char *) "quic"; + return NGX_OK; + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_v3_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + + static void * ngx_http_v3_create_srv_conf(ngx_conf_t *cf) { -- cgit From 0f77eac8af88e461c1b695094e63db249841ce0e Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 20 Mar 2020 23:49:42 +0300 Subject: Removed unused variable. --- src/http/v3/ngx_http_v3_request.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 9a2654fd4..542fc387d 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -245,7 +245,7 @@ ngx_chain_t * ngx_http_v3_create_header(ngx_http_request_t *r) { u_char *p; - size_t len, hlen, n; + size_t len, n; ngx_buf_t *b; ngx_uint_t i, j; ngx_chain_t *hl, *cl, *bl; @@ -553,8 +553,6 @@ ngx_http_v3_create_header(ngx_http_request_t *r) hl->buf = b; hl->next = cl; - hlen = 1 + ngx_http_v3_encode_varlen_int(NULL, len); - if (r->headers_out.content_length_n >= 0) { len = 1 + ngx_http_v3_encode_varlen_int(NULL, r->headers_out.content_length_n); -- cgit From 72b0a1b32a8a9527fccf1d71ee1fc54859cffd4c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 23 Mar 2020 18:47:17 +0300 Subject: Limit output QUIC packets with client max_packet_size. Additionally, receive larger packets than 512 bytes. --- src/http/v3/ngx_http_v3_module.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 20137b377..385838df4 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -233,11 +233,13 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) // > 2 ^ 14 is invalid ngx_conf_merge_msec_value(conf->quic.max_ack_delay, - prev->quic.max_ack_delay, 25); + prev->quic.max_ack_delay, + NGX_QUIC_DEFAULT_MAX_ACK_DELAY); // < 1200 is invalid ngx_conf_merge_uint_value(conf->quic.max_packet_size, - prev->quic.max_packet_size, 65527); + prev->quic.max_packet_size, + NGX_QUIC_DEFAULT_MAX_PACKET_SIZE); ngx_conf_merge_uint_value(conf->quic.initial_max_data, prev->quic.initial_max_data, 10000000); @@ -261,7 +263,8 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) // > 20 is invalid ngx_conf_merge_uint_value(conf->quic.ack_delay_exponent, - prev->quic.ack_delay_exponent, 3); + prev->quic.ack_delay_exponent, + NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); ngx_conf_merge_uint_value(conf->quic.disable_active_migration, prev->quic.disable_active_migration, 1); -- cgit From ede2656c6016d240b804bbcb28b6ecd391cca5de Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 23 Mar 2020 19:26:24 +0300 Subject: Support for HTTP/3 ALPN. This is required by Chrome. --- src/http/modules/ngx_http_ssl_module.c | 12 ++++++++++-- src/http/v3/ngx_http_v3.h | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 4b480a006..a48d3b924 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -371,7 +371,7 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, #if (NGX_DEBUG) unsigned int i; #endif -#if (NGX_HTTP_V2) +#if (NGX_HTTP_V2 || NGX_HTTP_V3) ngx_http_connection_t *hc; #endif #if (NGX_HTTP_V2 || NGX_DEBUG) @@ -388,15 +388,23 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, } #endif -#if (NGX_HTTP_V2) +#if (NGX_HTTP_V2 || NGX_HTTP_V3) hc = c->data; +#endif +#if (NGX_HTTP_V2) if (hc->addr_conf->http2) { srv = (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; srvlen = sizeof(NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1; } else +#endif +#if (NGX_HTTP_V3) + if (hc->addr_conf->http3) { + srv = (unsigned char *) NGX_HTTP_V3_ALPN_ADVERTISE; + srvlen = sizeof(NGX_HTTP_V3_ALPN_ADVERTISE) - 1; + } else #endif { srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 2c63f544e..81e57dc36 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -17,6 +17,9 @@ #define NGX_HTTP_V3_STREAM 0x48335354 /* "H3ST" */ +#define NGX_HTTP_V3_ALPN(s) NGX_HTTP_V3_ALPN_DRAFT(s) +#define NGX_HTTP_V3_ALPN_DRAFT(s) "\x05h3-" #s +#define NGX_HTTP_V3_ALPN_ADVERTISE NGX_HTTP_V3_ALPN(NGX_QUIC_DRAFT_VERSION) #define NGX_HTTP_V3_VARLEN_INT_LEN 4 #define NGX_HTTP_V3_PREFIX_INT_LEN 11 -- cgit From 5ac5e51fdfe68e8b11f8c7abd2ce361062f68e54 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 23 Mar 2020 21:20:20 +0300 Subject: Respect QUIC max_idle_timeout. --- src/http/ngx_http_request.c | 4 +--- src/http/v3/ngx_http_v3_module.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 6c9fdc543..acd708cf6 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -347,9 +347,7 @@ ngx_http_init_connection(ngx_connection_t *c) 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, &v3cf->quic, - c->listening->post_accept_timeout, - ngx_http_quic_stream_handler); + ngx_quic_run(c, &sscf->ssl, &v3cf->quic, ngx_http_quic_stream_handler); return; } #endif diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 385838df4..45bfc5b1c 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -229,7 +229,7 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_v3_srv_conf_t *conf = child; ngx_conf_merge_msec_value(conf->quic.max_idle_timeout, - prev->quic.max_idle_timeout, 10000); + prev->quic.max_idle_timeout, 60000); // > 2 ^ 14 is invalid ngx_conf_merge_msec_value(conf->quic.max_ack_delay, -- cgit From f20af3dabca75b1f57f5c72a0e45e7251762b43c Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 23 Mar 2020 20:48:34 +0300 Subject: Fixed client certificate verification. For ngx_http_process_request() part to work, this required to set both r->http_connection->ssl and c->ssl on a QUIC stream. To avoid damaging global SSL object, ngx_ssl_shutdown() is managed to ignore QUIC streams. --- src/http/ngx_http_request.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index acd708cf6..890e5374b 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -225,6 +225,7 @@ ngx_http_init_connection(ngx_connection_t *c) if (c->type == SOCK_DGRAM) { hc = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t)); hc->quic = 1; + hc->ssl = 1; } else #endif -- cgit From d8d42e29e7ff43025ba0f56262993cd37c4a5b10 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 24 Mar 2020 19:17:57 +0300 Subject: QUIC streams don't need filter_need_in_memory after 7f0981be07c4. Now they inherit c->ssl always enabled from the main connection, which makes r->main_filter_need_in_memory set for them. --- src/http/ngx_http_request.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 890e5374b..c5165f2dc 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -709,7 +709,6 @@ ngx_http_alloc_request(ngx_connection_t *c) #if (NGX_HTTP_V3) if (hc->quic) { r->http_version = NGX_HTTP_VERSION_30; - r->filter_need_in_memory = 1; } #endif -- cgit From f75e4e3fef6e270555afefbb15b4741c694596f3 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 24 Mar 2020 16:38:03 +0300 Subject: Removed ngx_quic_stream_node_t. Now ngx_quic_stream_t is directly inserted into the tree. --- src/http/ngx_http_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index c5165f2dc..6f168c8bd 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -414,7 +414,7 @@ ngx_http_quic_stream_handler(ngx_connection_t *c) pc = c->qs->parent; h3c = pc->data; - if (c->qs->unidirectional) { + if (c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { ngx_http_v3_handle_client_uni_stream(c); return; } -- cgit From a0a2e0de1d256dc6ebcaf9102682132c01510601 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 24 Mar 2020 18:05:45 +0300 Subject: When closing a QUIC connection, wait for all streams to finish. Additionally, streams are now removed from the tree in cleanup handler. --- src/http/v3/ngx_http_v3_streams.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 6a5610b9a..fa1c48a8b 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -29,6 +29,7 @@ static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); static void ngx_http_v3_uni_stream_cleanup(void *data); static void ngx_http_v3_read_uni_stream_type(ngx_event_t *rev); static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); +static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); static ngx_connection_t *ngx_http_v3_create_uni_stream(ngx_connection_t *c, ngx_uint_t type); static ngx_connection_t *ngx_http_v3_get_control(ngx_connection_t *c); @@ -74,6 +75,8 @@ ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) cln->data = c; c->read->handler = ngx_http_v3_read_uni_stream_type; + c->write->handler = ngx_http_v3_dummy_write_handler; + ngx_http_v3_read_uni_stream_type(c->read); } @@ -310,6 +313,21 @@ failed: } +static void +ngx_http_v3_dummy_write_handler(ngx_event_t *wev) +{ + ngx_connection_t *c; + + c = wev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy write handler"); + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_v3_close_uni_stream(c); + } +} + + /* XXX async & buffered stream writes */ static ngx_connection_t * @@ -338,6 +356,9 @@ ngx_http_v3_create_uni_stream(ngx_connection_t *c, ngx_uint_t type) us->type = type; sc->data = us; + sc->read->handler = ngx_http_v3_uni_read_handler; + sc->write->handler = ngx_http_v3_dummy_write_handler; + cln = ngx_pool_cleanup_add(sc->pool, 0); if (cln == NULL) { goto failed; -- cgit From dbf1b41cfbbe884413c750bf33f62f6c989cecd8 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 25 Mar 2020 12:14:24 +0300 Subject: Simplifed handling HTTP/3 streams. --- src/http/v3/ngx_http_v3.h | 19 ++- src/http/v3/ngx_http_v3_streams.c | 245 ++++++++++++-------------------------- 2 files changed, 86 insertions(+), 178 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 81e57dc36..80bdaebb5 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -15,8 +15,6 @@ #include -#define NGX_HTTP_V3_STREAM 0x48335354 /* "H3ST" */ - #define NGX_HTTP_V3_ALPN(s) NGX_HTTP_V3_ALPN_DRAFT(s) #define NGX_HTTP_V3_ALPN_DRAFT(s) "\x05h3-" #s #define NGX_HTTP_V3_ALPN_ADVERTISE NGX_HTTP_V3_ALPN(NGX_QUIC_DRAFT_VERSION) @@ -41,6 +39,14 @@ #define NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE 0x06 #define NGX_HTTP_V3_PARAM_BLOCKED_STREAMS 0x07 +#define NGX_HTTP_V3_STREAM_CLIENT_CONTROL 0 +#define NGX_HTTP_V3_STREAM_SERVER_CONTROL 1 +#define NGX_HTTP_V3_STREAM_CLIENT_ENCODER 2 +#define NGX_HTTP_V3_STREAM_SERVER_ENCODER 3 +#define NGX_HTTP_V3_STREAM_CLIENT_DECODER 4 +#define NGX_HTTP_V3_STREAM_SERVER_DECODER 5 +#define NGX_HTTP_V3_MAX_KNOWN_STREAM 6 + typedef struct { ngx_quic_tp_t quic; @@ -51,14 +57,7 @@ typedef struct { ngx_http_connection_t hc; ngx_array_t *dynamic; - - ngx_connection_t *client_encoder; - ngx_connection_t *client_decoder; - ngx_connection_t *client_control; - - ngx_connection_t *server_encoder; - ngx_connection_t *server_decoder; - ngx_connection_t *server_control; + ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; } ngx_http_v3_connection_t; diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index fa1c48a8b..6078725d7 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -15,40 +15,30 @@ typedef ngx_int_t (*ngx_http_v3_handler_pt)(ngx_connection_t *c, void *data, typedef struct { - uint32_t signature; /* QSTR */ - ngx_http_v3_handler_pt handler; void *data; - - ngx_uint_t type; - ngx_uint_t client; /* unsigned client:1; */ + ngx_int_t index; } ngx_http_v3_uni_stream_t; static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); -static void ngx_http_v3_uni_stream_cleanup(void *data); static void ngx_http_v3_read_uni_stream_type(ngx_event_t *rev); static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); -static ngx_connection_t *ngx_http_v3_create_uni_stream(ngx_connection_t *c, +static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type); -static ngx_connection_t *ngx_http_v3_get_control(ngx_connection_t *c); -static ngx_connection_t *ngx_http_v3_get_encoder(ngx_connection_t *c); -static ngx_connection_t *ngx_http_v3_get_decoder(ngx_connection_t *c); void ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) { - ngx_pool_cleanup_t *cln; ngx_http_v3_uni_stream_t *us; c->log->connection = c->number; - /* XXX */ - (void) ngx_http_v3_get_control(c); - (void) ngx_http_v3_get_encoder(c); - (void) ngx_http_v3_get_decoder(c); + ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); + ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); + ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 new uni stream id:0x%uxL", c->qs->id); @@ -59,21 +49,10 @@ ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) return; } - us->signature = NGX_HTTP_V3_STREAM; - us->client = 1; - us->type = (ngx_uint_t) -1; + us->index = -1; c->data = us; - cln = ngx_pool_cleanup_add(c->pool, 0); - if (cln == NULL) { - ngx_http_v3_close_uni_stream(c); - return; - } - - cln->handler = ngx_http_v3_uni_stream_cleanup; - cln->data = c; - c->read->handler = ngx_http_v3_read_uni_stream_type; c->write->handler = ngx_http_v3_dummy_write_handler; @@ -84,23 +63,7 @@ ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) static void ngx_http_v3_close_uni_stream(ngx_connection_t *c) { - ngx_pool_t *pool; - - c->destroyed = 1; - - pool = c->pool; - - ngx_close_connection(c); - - ngx_destroy_pool(pool); -} - - -static void -ngx_http_v3_uni_stream_cleanup(void *data) -{ - ngx_connection_t *c = data; - + ngx_pool_t *pool; ngx_http_v3_connection_t *h3c; ngx_http_v3_uni_stream_t *us; @@ -109,38 +72,17 @@ ngx_http_v3_uni_stream_cleanup(void *data) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 close stream"); - switch (us->type) { - - case NGX_HTTP_V3_STREAM_ENCODER: - - if (us->client) { - h3c->client_encoder = NULL; - } else { - h3c->server_encoder = NULL; - } - - break; - - case NGX_HTTP_V3_STREAM_DECODER: - - if (us->client) { - h3c->client_decoder = NULL; - } else { - h3c->server_decoder = NULL; - } + if (us->index >= 0) { + h3c->known_streams[us->index] = NULL; + } - break; + c->destroyed = 1; - case NGX_HTTP_V3_STREAM_CONTROL: + pool = c->pool; - if (us->client) { - h3c->client_control = NULL; - } else { - h3c->server_control = NULL; - } + ngx_close_connection(c); - break; - } + ngx_destroy_pool(pool); } @@ -149,12 +91,13 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) { u_char ch; ssize_t n; + ngx_int_t index; ngx_connection_t *c; ngx_http_v3_connection_t *h3c; - ngx_http_v3_uni_stream_t *st; + ngx_http_v3_uni_stream_t *us; c = rev->data; - st = c->data; + us = c->data; h3c = c->qs->parent->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read stream type"); @@ -171,21 +114,15 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) break; } - st->type = ch; - - switch (st->type) { + switch (ch) { case NGX_HTTP_V3_STREAM_ENCODER: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 encoder stream"); - if (h3c->client_encoder) { - goto failed; - } - - h3c->client_encoder = c; - st->handler = ngx_http_v3_parse_encoder; + index = NGX_HTTP_V3_STREAM_CLIENT_ENCODER; + us->handler = ngx_http_v3_parse_encoder; n = sizeof(ngx_http_v3_parse_encoder_t); break; @@ -195,12 +132,8 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 decoder stream"); - if (h3c->client_decoder) { - goto failed; - } - - h3c->client_decoder = c; - st->handler = ngx_http_v3_parse_decoder; + index = NGX_HTTP_V3_STREAM_CLIENT_DECODER; + us->handler = ngx_http_v3_parse_decoder; n = sizeof(ngx_http_v3_parse_decoder_t); break; @@ -210,12 +143,8 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 control stream"); - if (h3c->client_control) { - goto failed; - } - - h3c->client_control = c; - st->handler = ngx_http_v3_parse_control; + index = NGX_HTTP_V3_STREAM_CLIENT_CONTROL; + us->handler = ngx_http_v3_parse_control; n = sizeof(ngx_http_v3_parse_control_t); break; @@ -223,13 +152,24 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) default: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 stream 0x%02xi", st->type); + "http3 stream 0x%02xi", (ngx_int_t) ch); + index = -1; n = 0; } + if (index >= 0) { + if (h3c->known_streams[index]) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); + goto failed; + } + + us->index = index; + h3c->known_streams[index] = c; + } + if (n) { - st->data = ngx_pcalloc(c->pool, n); - if (st->data == NULL) { + us->data = ngx_pcalloc(c->pool, n); + if (us->data == NULL) { goto failed; } } @@ -258,10 +198,10 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) ssize_t n; ngx_int_t rc, i; ngx_connection_t *c; - ngx_http_v3_uni_stream_t *st; + ngx_http_v3_uni_stream_t *us; c = rev->data; - st = c->data; + us = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler"); @@ -277,13 +217,13 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) break; } - if (st->handler == NULL) { + if (us->handler == NULL) { continue; } for (i = 0; i < n; i++) { - rc = st->handler(c, st->data, buf[i]); + rc = us->handler(c, us->data, buf[i]); if (rc == NGX_ERROR) { goto failed; @@ -331,14 +271,37 @@ ngx_http_v3_dummy_write_handler(ngx_event_t *wev) /* XXX async & buffered stream writes */ static ngx_connection_t * -ngx_http_v3_create_uni_stream(ngx_connection_t *c, ngx_uint_t type) +ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) { u_char buf[NGX_HTTP_V3_VARLEN_INT_LEN]; size_t n; + ngx_int_t index; ngx_connection_t *sc; - ngx_pool_cleanup_t *cln; + ngx_http_v3_connection_t *h3c; ngx_http_v3_uni_stream_t *us; + switch (type) { + case NGX_HTTP_V3_STREAM_ENCODER: + index = NGX_HTTP_V3_STREAM_SERVER_ENCODER; + break; + case NGX_HTTP_V3_STREAM_DECODER: + index = NGX_HTTP_V3_STREAM_SERVER_DECODER; + break; + case NGX_HTTP_V3_STREAM_CONTROL: + index = NGX_HTTP_V3_STREAM_SERVER_CONTROL; + break; + default: + index = -1; + } + + h3c = c->qs->parent->data; + + if (index >= 0) { + if (h3c->known_streams[index]) { + return h3c->known_streams[index]; + } + } + sc = ngx_quic_create_uni_stream(c); if (sc == NULL) { return NULL; @@ -352,20 +315,14 @@ ngx_http_v3_create_uni_stream(ngx_connection_t *c, ngx_uint_t type) goto failed; } - us->signature = NGX_HTTP_V3_STREAM; - us->type = type; + us->index = index; + sc->data = us; sc->read->handler = ngx_http_v3_uni_read_handler; sc->write->handler = ngx_http_v3_dummy_write_handler; - cln = ngx_pool_cleanup_add(sc->pool, 0); - if (cln == NULL) { - goto failed; - } - - cln->handler = ngx_http_v3_uni_stream_cleanup; - cln->data = sc; + h3c->known_streams[index] = sc; n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf; @@ -383,54 +340,6 @@ failed: } -static ngx_connection_t * -ngx_http_v3_get_control(ngx_connection_t *c) -{ - ngx_http_v3_connection_t *h3c; - - h3c = c->qs->parent->data; - - if (h3c->server_control == NULL) { - h3c->server_control = ngx_http_v3_create_uni_stream(c, - NGX_HTTP_V3_STREAM_CONTROL); - } - - return h3c->server_encoder; -} - - -static ngx_connection_t * -ngx_http_v3_get_encoder(ngx_connection_t *c) -{ - ngx_http_v3_connection_t *h3c; - - h3c = c->qs->parent->data; - - if (h3c->server_encoder == NULL) { - h3c->server_encoder = ngx_http_v3_create_uni_stream(c, - NGX_HTTP_V3_STREAM_ENCODER); - } - - return h3c->server_encoder; -} - - -static ngx_connection_t * -ngx_http_v3_get_decoder(ngx_connection_t *c) -{ - ngx_http_v3_connection_t *h3c; - - h3c = c->qs->parent->data; - - if (h3c->server_decoder == NULL) { - h3c->server_decoder = ngx_http_v3_create_uni_stream(c, - NGX_HTTP_V3_STREAM_DECODER); - } - - return h3c->server_encoder; -} - - ngx_int_t ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value) @@ -443,7 +352,7 @@ ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, "http3 client ref insert, %s[%ui] \"%V\"", dynamic ? "dynamic" : "static", index, value); - ec = ngx_http_v3_get_encoder(c); + ec = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); if (ec == NULL) { return NGX_ERROR; } @@ -488,7 +397,7 @@ ngx_http_v3_client_insert(ngx_connection_t *c, ngx_str_t *name, ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client insert \"%V\":\"%V\"", name, value); - ec = ngx_http_v3_get_encoder(c); + ec = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); if (ec == NULL) { return NGX_ERROR; } @@ -537,7 +446,7 @@ ngx_http_v3_client_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client set capacity %ui", capacity); - ec = ngx_http_v3_get_encoder(c); + ec = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); if (ec == NULL) { return NGX_ERROR; } @@ -564,7 +473,7 @@ ngx_http_v3_client_duplicate(ngx_connection_t *c, ngx_uint_t index) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client duplicate %ui", index); - ec = ngx_http_v3_get_encoder(c); + ec = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); if (ec == NULL) { return NGX_ERROR; } @@ -591,7 +500,7 @@ ngx_http_v3_client_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client ack header %ui", stream_id); - dc = ngx_http_v3_get_decoder(c); + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); if (dc == NULL) { return NGX_ERROR; } @@ -618,7 +527,7 @@ ngx_http_v3_client_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client cancel stream %ui", stream_id); - dc = ngx_http_v3_get_decoder(c); + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); if (dc == NULL) { return NGX_ERROR; } @@ -645,7 +554,7 @@ ngx_http_v3_client_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client increment insert count %ui", inc); - dc = ngx_http_v3_get_decoder(c); + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); if (dc == NULL) { return NGX_ERROR; } -- cgit From 81f7cff632d7db267be42f5451c6e1f29d021685 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 27 Mar 2020 15:50:42 +0300 Subject: Fixed buffer overflow. --- src/http/v3/ngx_http_v3_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 542fc387d..b00b93ce2 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -263,7 +263,7 @@ ngx_http_v3_create_header(ngx_http_request_t *r) return NULL; } - len = 0; + len = 2; if (r->headers_out.status == NGX_HTTP_OK) { len += ngx_http_v3_encode_prefix_int(NULL, 25, 6); -- cgit From 80a38580bd04f499d72ab3b6f7776e275e47a2b3 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 27 Mar 2020 19:46:54 +0300 Subject: Chunked response body in HTTP/3. --- src/http/modules/ngx_http_chunked_filter_module.c | 58 ++++++++++++++++++----- src/http/v3/ngx_http_v3.h | 1 + src/http/v3/ngx_http_v3_request.c | 35 ++++++++++++-- 3 files changed, 76 insertions(+), 18 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c index 4d6fd3eed..87b032496 100644 --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -18,7 +18,7 @@ typedef struct { static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r, - ngx_http_chunked_filter_ctx_t *ctx); + ngx_http_chunked_filter_ctx_t *ctx, size_t size); static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { @@ -106,6 +106,7 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { u_char *chunk; off_t size; + size_t n; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *out, *cl, *tl, **ll; @@ -161,29 +162,50 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) chunk = b->start; if (chunk == NULL) { - /* the "0000000000000000" is 64-bit hexadecimal string */ - chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1); +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + n = NGX_HTTP_V3_VARLEN_INT_LEN * 2; + + } else +#endif + { + /* the "0000000000000000" is 64-bit hexadecimal string */ + n = sizeof("0000000000000000" CRLF) - 1; + } + + chunk = ngx_palloc(r->pool, n); if (chunk == NULL) { return NGX_ERROR; } b->start = chunk; - b->end = chunk + sizeof("0000000000000000" CRLF) - 1; + b->end = chunk + n; } b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; b->memory = 0; b->temporary = 1; b->pos = chunk; - b->last = ngx_sprintf(chunk, "%xO" CRLF, size); + +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + b->last = (u_char *) ngx_http_v3_encode_varlen_int(chunk, + NGX_HTTP_V3_FRAME_DATA); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, size); + + } else +#endif + { + b->last = ngx_sprintf(chunk, "%xO" CRLF, size); + } tl->next = out; out = tl; } if (cl->buf->last_buf) { - tl = ngx_http_chunked_create_trailers(r, ctx); + tl = ngx_http_chunked_create_trailers(r, ctx, size); if (tl == NULL) { return NGX_ERROR; } @@ -192,11 +214,12 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) *ll = tl; - if (size == 0) { - tl->buf->pos += 2; - } - - } else if (size > 0) { + } else if (size > 0 +#if (NGX_HTTP_V3) + && r->http_version != NGX_HTTP_VERSION_30 +#endif + ) + { tl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (tl == NULL) { return NGX_ERROR; @@ -227,7 +250,7 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) static ngx_chain_t * ngx_http_chunked_create_trailers(ngx_http_request_t *r, - ngx_http_chunked_filter_ctx_t *ctx) + ngx_http_chunked_filter_ctx_t *ctx, size_t size) { size_t len; ngx_buf_t *b; @@ -236,6 +259,12 @@ ngx_http_chunked_create_trailers(ngx_http_request_t *r, ngx_list_part_t *part; ngx_table_elt_t *header; +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + return ngx_http_v3_create_trailers(r); + } +#endif + len = 0; part = &r->headers_out.trailers.part; @@ -288,7 +317,10 @@ ngx_http_chunked_create_trailers(ngx_http_request_t *r, b->last = b->pos; - *b->last++ = CR; *b->last++ = LF; + if (size > 0) { + *b->last++ = CR; *b->last++ = LF; + } + *b->last++ = '0'; *b->last++ = CR; *b->last++ = LF; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 80bdaebb5..d28074d37 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -69,6 +69,7 @@ typedef struct { ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b); ngx_chain_t *ngx_http_v3_create_header(ngx_http_request_t *r); +ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r); uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index b00b93ce2..756a6f90d 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -258,11 +258,6 @@ ngx_http_v3_create_header(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create header"); - /* XXX support chunked body in the chunked filter */ - if (!r->header_only && r->headers_out.content_length_n == -1) { - return NULL; - } - len = 2; if (r->headers_out.status == NGX_HTTP_OK) { @@ -578,3 +573,33 @@ ngx_http_v3_create_header(ngx_http_request_t *r) return hl; } + + +ngx_chain_t * +ngx_http_v3_create_trailers(ngx_http_request_t *r) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 create trailers"); + + /* XXX */ + + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NULL; + } + + b->last_buf = 1; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = b; + cl->next = NULL; + + return cl; +} -- cgit From fa1e1beadca8b1ea900ec654919aea58762ab746 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 27 Mar 2020 19:41:06 +0300 Subject: Parsing HTTP/3 request body. --- src/http/ngx_http.h | 3 ++ src/http/ngx_http_parse.c | 11 +++++++ src/http/ngx_http_request.c | 1 + src/http/ngx_http_request_body.c | 29 +++++++++++------- src/http/v3/ngx_http_v3.h | 2 ++ src/http/v3/ngx_http_v3_parse.c | 58 +++++++++++++++++++++++++++++++++++ src/http/v3/ngx_http_v3_parse.h | 10 +++++++ src/http/v3/ngx_http_v3_request.c | 63 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 167 insertions(+), 10 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 8772001c0..a0946c95a 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -63,6 +63,9 @@ struct ngx_http_chunked_s { ngx_uint_t state; off_t size; off_t length; +#if (NGX_HTTP_V3) + void *h3_parse; +#endif }; diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 28aa8b0dd..92bcf12ad 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -2185,6 +2185,12 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, sw_trailer_header_almost_done } state; +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + return ngx_http_v3_parse_request_body(r, b, ctx); + } +#endif + state = ctx->state; if (state == sw_chunk_data && ctx->size == 0) { @@ -2371,6 +2377,11 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, } } + if (b->last_buf) { + /* XXX client prematurely closed connection */ + return NGX_ERROR; + } + data: ctx->state = state; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 6f168c8bd..4368e79c0 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -709,6 +709,7 @@ ngx_http_alloc_request(ngx_connection_t *c) #if (NGX_HTTP_V3) if (hc->quic) { r->http_version = NGX_HTTP_VERSION_30; + r->headers_in.chunked = 1; } #endif diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index c4f092e59..b07d8562f 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -343,11 +343,10 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) } if (n == 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client prematurely closed connection"); + rb->buf->last_buf = 1; } - if (n == 0 || n == NGX_ERROR) { + if (n == NGX_ERROR) { c->error = 1; return NGX_HTTP_BAD_REQUEST; } @@ -355,7 +354,7 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) rb->buf->last += n; r->request_length += n; - if (n == rest) { + if (n == rest || n == 0) { /* pass buffer to request body filter chain */ out.buf = rb->buf; @@ -805,11 +804,7 @@ ngx_http_test_expect(ngx_http_request_t *r) if (r->expect_tested || r->headers_in.expect == NULL - || r->http_version < NGX_HTTP_VERSION_11 -#if (NGX_HTTP_V2) - || r->stream != NULL -#endif - ) + || r->http_version != NGX_HTTP_VERSION_11) { return NGX_OK; } @@ -914,6 +909,11 @@ ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) b->last_buf = 1; } + if (cl->buf->last_buf && rb->rest > 0) { + /* XXX client prematurely closed connection */ + return NGX_ERROR; + } + *ll = tl; ll = &tl->next; } @@ -950,7 +950,16 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) } r->headers_in.content_length_n = 0; - rb->rest = 3; + +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + rb->rest = 1; + + } else +#endif + { + rb->rest = 3; + } } out = NULL; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index d28074d37..3f35e985e 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -68,6 +68,8 @@ typedef struct { ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b); +ngx_int_t ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, + ngx_http_chunked_t *ctx); ngx_chain_t *ngx_http_v3_create_header(ngx_http_request_t *r); ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r); diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 0fd44bc40..3be3802ed 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -1421,3 +1421,61 @@ done: st->state = sw_start; return NGX_DONE; } + + +ngx_int_t +ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, + u_char ch) +{ + enum { + sw_start = 0, + sw_type, + sw_length + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data"); + + st->state = sw_type; + + /* fall through */ + + case sw_type: + + if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { + break; + } + + if (st->vlint.value != NGX_HTTP_V3_FRAME_DATA) { + return NGX_ERROR; + } + + st->state = sw_length; + break; + + case sw_length: + + if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { + break; + } + + st->length = st->vlint.value; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse data frame len:%ui", st->length); + + goto done; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data done"); + + st->state = sw_start; + return NGX_DONE; +} diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h index 959da7941..ec78c7c35 100644 --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -105,6 +105,13 @@ typedef struct { } ngx_http_v3_parse_control_t; +typedef struct { + ngx_uint_t state; + ngx_uint_t length; + ngx_http_v3_parse_varlen_int_t vlint; +} ngx_http_v3_parse_data_t; + + ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, ngx_http_v3_parse_varlen_int_t *st, u_char ch); ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, @@ -141,5 +148,8 @@ ngx_int_t ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch); +ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c, + ngx_http_v3_parse_data_t *st, u_char ch); + #endif /* _NGX_HTTP_V3_PARSE_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 756a6f90d..911dbab36 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -241,6 +241,69 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, } +ngx_int_t +ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, + ngx_http_chunked_t *ctx) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_v3_parse_data_t *st; + + c = r->connection; + st = ctx->h3_parse; + + if (st == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse request body"); + + st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_data_t)); + if (st == NULL) { + goto failed; + } + + r->h3_parse = st; + } + + if (ctx->size) { + return NGX_OK; + } + + while (b->pos < b->last) { + rc = ngx_http_v3_parse_data(c, st, *b->pos++); + + if (rc == NGX_ERROR) { + goto failed; + } + + if (rc == NGX_AGAIN) { + continue; + } + + /* rc == NGX_DONE */ + + ctx->size = st->length; + return NGX_OK; + } + + if (!b->last_buf) { + ctx->length = 1; + return NGX_AGAIN; + } + + if (st->state) { + goto failed; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header done"); + + return NGX_DONE; + +failed: + + return NGX_ERROR; +} + + ngx_chain_t * ngx_http_v3_create_header(ngx_http_request_t *r) { -- cgit From 7a0b840c51f7ed60453d7fb06731ae024295327b Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Sat, 28 Mar 2020 18:02:20 +0300 Subject: HTTP/3: static table cleanup. --- src/http/v3/ngx_http_v3_tables.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index b4fc90b38..f58f190f1 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -17,7 +17,7 @@ static ngx_int_t ngx_http_v3_new_header(ngx_connection_t *c); static ngx_http_v3_header_t ngx_http_v3_static_table[] = { { ngx_string(":authority"), ngx_string("") }, - { ngx_string(":path"), ngx_string("/") }, + { ngx_string(":path"), ngx_string("/") }, { ngx_string("age"), ngx_string("0") }, { ngx_string("content-disposition"), ngx_string("") }, { ngx_string("content-length"), ngx_string("0") }, @@ -47,8 +47,8 @@ static ngx_http_v3_header_t ngx_http_v3_static_table[] = { { ngx_string(":status"), ngx_string("503") }, { ngx_string("accept"), ngx_string("*/*") }, { ngx_string("accept"), - ngx_string("application/dns-message ") }, - { ngx_string("accept-encoding"), ngx_string("gzip,") }, + ngx_string("application/dns-message") }, + { ngx_string("accept-encoding"), ngx_string("gzip, deflate, br") }, { ngx_string("accept-ranges"), ngx_string("bytes") }, { ngx_string("access-control-allow-headers"), ngx_string("cache-control") }, @@ -62,7 +62,7 @@ static ngx_http_v3_header_t ngx_http_v3_static_table[] = { { ngx_string("cache-control"), ngx_string("no-cache") }, { ngx_string("cache-control"), ngx_string("no-store") }, { ngx_string("cache-control"), - ngx_string("public, max-age=31536000 ") }, + ngx_string("public, max-age=31536000") }, { ngx_string("content-encoding"), ngx_string("br") }, { ngx_string("content-encoding"), ngx_string("gzip") }, { ngx_string("content-type"), @@ -90,7 +90,8 @@ static ngx_http_v3_header_t ngx_http_v3_static_table[] = { ngx_string("max-age=31536000;includesubdomains;preload") }, { ngx_string("vary"), ngx_string("accept-encoding") }, { ngx_string("vary"), ngx_string("origin") }, - { ngx_string("x-content-type-options"),ngx_string("nosniff") }, + { ngx_string("x-content-type-options"), + ngx_string("nosniff") }, { ngx_string("x-xss-protection"), ngx_string("1;mode=block") }, { ngx_string(":status"), ngx_string("100") }, { ngx_string(":status"), ngx_string("204") }, @@ -123,9 +124,9 @@ static ngx_http_v3_header_t ngx_http_v3_static_table[] = { { ngx_string("access-control-request-method"), ngx_string("post") }, { ngx_string("alt-svc"), ngx_string("clear") }, - { ngx_string("horization"), ngx_string("") }, + { ngx_string("authorization"), ngx_string("") }, { ngx_string("content-security-policy"), - ngx_string("script-src") }, + ngx_string("script-src 'none';object-src 'none';base-uri 'none'") }, { ngx_string("early-data"), ngx_string("1") }, { ngx_string("expect-ct"), ngx_string("") }, { ngx_string("forwarded"), ngx_string("") }, -- cgit From 4502e5b1e98412ae261d3440e2962519e3e71d5d Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Sat, 28 Mar 2020 18:41:31 +0300 Subject: HTTP/3: http3 variable. --- src/http/v3/ngx_http_v3_module.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 45bfc5b1c..279210337 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -102,6 +102,8 @@ static ngx_command_t ngx_http_v3_commands[] = { static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_http3(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); static void *ngx_http_v3_create_srv_conf(ngx_conf_t *cf); static char *ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, @@ -143,6 +145,9 @@ static ngx_http_variable_t ngx_http_v3_vars[] = { { ngx_string("quic"), NULL, ngx_http_variable_quic, 0, 0, 0 }, + { ngx_string("http3"), NULL, ngx_http_variable_http3, + 0, 0, 0 }, + ngx_http_null_variable }; @@ -167,6 +172,25 @@ ngx_http_variable_quic(ngx_http_request_t *r, } +static ngx_int_t +ngx_http_variable_http3(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + v->valid = 1; + v->no_cacheable = 1; + v->not_found = 0; + + v->data = ngx_pnalloc(r->pool, sizeof("h3-xx") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(v->data, "h3-%d", NGX_QUIC_DRAFT_VERSION) - v->data; + + return NGX_OK; +} + + static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf) { -- cgit From b77fd3dc58b8398bf85d7c11901f5497f1abdf9e Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 13 Apr 2020 17:54:23 +0300 Subject: HTTP/3: fixed reading request body. --- src/http/v3/ngx_http_v3_request.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 911dbab36..e0ee0c882 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -265,7 +265,8 @@ ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, } if (ctx->size) { - return NGX_OK; + ctx->length = ctx->size + 1; + return (b->pos == b->last) ? NGX_AGAIN : NGX_OK; } while (b->pos < b->last) { -- cgit From 29b6ad00a2166e3b6e9fd159af18c11467e5ea08 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Wed, 15 Apr 2020 18:54:03 +0300 Subject: Added primitive flow control mechanisms. + MAX_STREAM_DATA frame is sent when recv() is performed on stream The new value is a sum of total bytes received by stream + free space in a buffer; The sending of MAX_STREM_DATA frame in response to STREAM_DATA_BLOCKED frame is adjusted to follow the same logic as above. + MAX_DATA frame is sent when total amount of received data is 2x of current limit. The limit is doubled. + Default values of transport parameters are adjusted to more meaningful values: initial stream limits are set to quic buffer size instead of unrealistically small 255. initial max data is decreased to 16 buffer sizes, in an assumption that this is enough for a relatively short connection, instead of randomly chosen big number. All this allows to initiate a stable flow of streams that does not block on stream/connection limits (tested with FF 77.0a1 and 100K requests) --- src/http/v3/ngx_http_v3_module.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 279210337..4306206c9 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -266,18 +266,20 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) NGX_QUIC_DEFAULT_MAX_PACKET_SIZE); ngx_conf_merge_uint_value(conf->quic.initial_max_data, - prev->quic.initial_max_data, 10000000); + prev->quic.initial_max_data, + 16 * NGX_QUIC_STREAM_BUFSIZE); ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_bidi_local, prev->quic.initial_max_stream_data_bidi_local, - 255); + NGX_QUIC_STREAM_BUFSIZE); ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_bidi_remote, prev->quic.initial_max_stream_data_bidi_remote, - 255); + NGX_QUIC_STREAM_BUFSIZE); ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_uni, - prev->quic.initial_max_stream_data_uni, 255); + prev->quic.initial_max_stream_data_uni, + NGX_QUIC_STREAM_BUFSIZE); ngx_conf_merge_uint_value(conf->quic.initial_max_streams_bidi, prev->quic.initial_max_streams_bidi, 16); -- cgit From f901747f29e0c9c65db06ae522469ad5d916611e Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Thu, 16 Apr 2020 12:17:41 +0300 Subject: Added handling of incorrect values in TP configuration. Some parameters have minimal/maximum values defined by standard. --- src/http/v3/ngx_http_v3_module.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 4306206c9..4b5f2f370 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -255,16 +255,30 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_msec_value(conf->quic.max_idle_timeout, prev->quic.max_idle_timeout, 60000); - // > 2 ^ 14 is invalid ngx_conf_merge_msec_value(conf->quic.max_ack_delay, prev->quic.max_ack_delay, NGX_QUIC_DEFAULT_MAX_ACK_DELAY); - // < 1200 is invalid + if (conf->quic.max_ack_delay > 16384) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"quic_max_ack_delay\" greater than" + " 16384 is invalid"); + return NGX_CONF_ERROR; + } + ngx_conf_merge_uint_value(conf->quic.max_packet_size, prev->quic.max_packet_size, NGX_QUIC_DEFAULT_MAX_PACKET_SIZE); + if (conf->quic.max_packet_size < 1200 + || conf->quic.max_packet_size > 65527) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"quic_max_packet_size\" less than" + " 1200 or greater than 65527 is invalid"); + return NGX_CONF_ERROR; + } + ngx_conf_merge_uint_value(conf->quic.initial_max_data, prev->quic.initial_max_data, 16 * NGX_QUIC_STREAM_BUFSIZE); @@ -287,18 +301,30 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) 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, NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); + if (conf->quic.ack_delay_exponent > 20) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"quic_ack_delay_exponent\" greater than" + " 20 is invalid"); + return NGX_CONF_ERROR; + } + 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); + if (conf->quic.active_connection_id_limit < 2) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"quic_active_connection_id_limit\" less than" + " 2 is invalid"); + return NGX_CONF_ERROR; + } + return NGX_CONF_OK; } -- cgit From 400eb1b628ce60625df5f6844899ca4cd6d8cbed Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 21 Apr 2020 17:11:49 +0300 Subject: HTTP/3: fixed encoding variable-length integers. --- src/http/v3/ngx_http_v3.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index cca84dbc1..f7f79b9f4 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -34,21 +34,25 @@ ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value) if (value <= 1073741823) { if (p == NULL) { - return 3; + return 4; } - *p++ = 0x80 | (value >> 16); + *p++ = 0x80 | (value >> 24); + *p++ = (value >> 16); *p++ = (value >> 8); *p++ = value; return (uintptr_t) p; - } if (p == NULL) { - return 4; + return 8; } - *p++ = 0xc0 | (value >> 24); + *p++ = 0xc0 | (value >> 56); + *p++ = (value >> 48); + *p++ = (value >> 40); + *p++ = (value >> 32); + *p++ = (value >> 24); *p++ = (value >> 16); *p++ = (value >> 8); *p++ = value; -- cgit From 89bba9bf7ddf0cfe448e817f9215686fbfb1ae94 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 22 Apr 2020 15:48:39 +0300 Subject: HTTP/3: bytes holding directives changed to ngx_conf_set_size_slot. This allows to specify directive values with measurement units. --- src/http/v3/ngx_http_v3_module.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 4b5f2f370..1566706fc 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -28,35 +28,35 @@ static ngx_command_t ngx_http_v3_commands[] = { { ngx_string("quic_max_packet_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_num_slot, + ngx_conf_set_size_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_conf_set_size_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_conf_set_size_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_conf_set_size_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_conf_set_size_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_stream_data_uni), NULL }, @@ -231,11 +231,11 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) 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.max_packet_size = NGX_CONF_UNSET_SIZE; + v3cf->quic.initial_max_data = NGX_CONF_UNSET_SIZE; + v3cf->quic.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_SIZE; + v3cf->quic.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_SIZE; + v3cf->quic.initial_max_stream_data_uni = NGX_CONF_UNSET_SIZE; 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; @@ -266,7 +266,7 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - ngx_conf_merge_uint_value(conf->quic.max_packet_size, + ngx_conf_merge_size_value(conf->quic.max_packet_size, prev->quic.max_packet_size, NGX_QUIC_DEFAULT_MAX_PACKET_SIZE); @@ -279,19 +279,19 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - ngx_conf_merge_uint_value(conf->quic.initial_max_data, + ngx_conf_merge_size_value(conf->quic.initial_max_data, prev->quic.initial_max_data, 16 * NGX_QUIC_STREAM_BUFSIZE); - ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_bidi_local, + ngx_conf_merge_size_value(conf->quic.initial_max_stream_data_bidi_local, prev->quic.initial_max_stream_data_bidi_local, NGX_QUIC_STREAM_BUFSIZE); - ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_bidi_remote, + ngx_conf_merge_size_value(conf->quic.initial_max_stream_data_bidi_remote, prev->quic.initial_max_stream_data_bidi_remote, NGX_QUIC_STREAM_BUFSIZE); - ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_uni, + ngx_conf_merge_size_value(conf->quic.initial_max_stream_data_uni, prev->quic.initial_max_stream_data_uni, NGX_QUIC_STREAM_BUFSIZE); -- cgit From 37b95d545c396df2c4c7ef61a31d18e62288a019 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 22 Apr 2020 15:59:19 +0300 Subject: HTTP/3: directives with limited values converted to post handler. The purpose is to show a precise line number with an invalid value. --- src/http/v3/ngx_http_v3_module.c | 80 +++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 30 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 1566706fc..9daaedb3e 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -10,6 +10,21 @@ #include +static char *ngx_http_v3_max_ack_delay(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_v3_max_packet_size(ngx_conf_t *cf, void *post, + void *data); + + +static ngx_conf_post_t ngx_http_v3_max_ack_delay_post = + { ngx_http_v3_max_ack_delay }; +static ngx_conf_post_t ngx_http_v3_max_packet_size_post = + { ngx_http_v3_max_packet_size }; +static ngx_conf_num_bounds_t ngx_http_v3_ack_delay_exponent_bounds = + { ngx_conf_check_num_bounds, 0, 20 }; +static ngx_conf_num_bounds_t ngx_http_v3_active_connection_id_limit_bounds = + { ngx_conf_check_num_bounds, 2, -1 }; + + static ngx_command_t ngx_http_v3_commands[] = { { ngx_string("quic_max_idle_timeout"), @@ -24,14 +39,14 @@ static ngx_command_t ngx_http_v3_commands[] = { ngx_conf_set_msec_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_v3_srv_conf_t, quic.max_ack_delay), - NULL }, + &ngx_http_v3_max_ack_delay_post }, { ngx_string("quic_max_packet_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_v3_srv_conf_t, quic.max_packet_size), - NULL }, + &ngx_http_v3_max_packet_size_post }, { ngx_string("quic_initial_max_data"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, @@ -80,7 +95,7 @@ static ngx_command_t ngx_http_v3_commands[] = { ngx_conf_set_num_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_v3_srv_conf_t, quic.ack_delay_exponent), - NULL }, + &ngx_http_v3_ack_delay_exponent_bounds }, { ngx_string("quic_active_migration"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, @@ -94,7 +109,7 @@ static ngx_command_t ngx_http_v3_commands[] = { ngx_conf_set_num_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_v3_srv_conf_t, quic.active_connection_id_limit), - NULL }, + &ngx_http_v3_active_connection_id_limit_bounds }, ngx_null_command }; @@ -259,26 +274,10 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) prev->quic.max_ack_delay, NGX_QUIC_DEFAULT_MAX_ACK_DELAY); - if (conf->quic.max_ack_delay > 16384) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_max_ack_delay\" greater than" - " 16384 is invalid"); - return NGX_CONF_ERROR; - } - ngx_conf_merge_size_value(conf->quic.max_packet_size, prev->quic.max_packet_size, NGX_QUIC_DEFAULT_MAX_PACKET_SIZE); - if (conf->quic.max_packet_size < 1200 - || conf->quic.max_packet_size > 65527) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_max_packet_size\" less than" - " 1200 or greater than 65527 is invalid"); - return NGX_CONF_ERROR; - } - ngx_conf_merge_size_value(conf->quic.initial_max_data, prev->quic.initial_max_data, 16 * NGX_QUIC_STREAM_BUFSIZE); @@ -305,26 +304,47 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) prev->quic.ack_delay_exponent, NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); - if (conf->quic.ack_delay_exponent > 20) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_ack_delay_exponent\" greater than" - " 20 is invalid"); - return NGX_CONF_ERROR; - } - ngx_conf_merge_uint_value(conf->quic.disable_active_migration, prev->quic.disable_active_migration, 1); ngx_conf_merge_uint_value(conf->quic.active_connection_id_limit, prev->quic.active_connection_id_limit, 2); - if (conf->quic.active_connection_id_limit < 2) { + return NGX_CONF_OK; +} + + +static char * +ngx_http_v3_max_ack_delay(ngx_conf_t *cf, void *post, void *data) +{ + ngx_msec_t *sp = data; + + if (*sp > 16384) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_active_connection_id_limit\" less than" - " 2 is invalid"); + "\"quic_max_ack_delay\" must be less than 16384"); + return NGX_CONF_ERROR; } return NGX_CONF_OK; } + +static char * +ngx_http_v3_max_packet_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp < NGX_QUIC_MIN_INITIAL_SIZE + || *sp > NGX_QUIC_DEFAULT_MAX_PACKET_SIZE) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"quic_max_packet_size\" must be between %d and %d", + NGX_QUIC_MIN_INITIAL_SIZE, + NGX_QUIC_DEFAULT_MAX_PACKET_SIZE); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} -- cgit From 9c375910161cdcac5bb616f6afb8de030849f2ca Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 23 Apr 2020 18:05:05 +0300 Subject: Assign connection number to every QUIC stream log. --- src/http/ngx_http_request.c | 1 - src/http/v3/ngx_http_v3_streams.c | 2 -- 2 files changed, 3 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 082938e00..b0cc9a543 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -438,7 +438,6 @@ ngx_http_quic_stream_handler(ngx_connection_t *c) ctx->request = NULL; ctx->current_request = NULL; - c->log->connection = c->number; c->log->handler = ngx_http_log_error; c->log->data = ctx; c->log->action = "waiting for request"; diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 6078725d7..165f21fdc 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -34,8 +34,6 @@ ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) { ngx_http_v3_uni_stream_t *us; - c->log->connection = c->number; - ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); -- cgit From ad2289e70ed3b3c0d0fb75b554f493a60db99307 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 14 May 2020 15:47:18 +0300 Subject: Address validation using Retry packets. The behaviour is toggled with the new directive "quic_retry on|off". QUIC token construction is made suitable for issuing with NEW_TOKEN. --- src/http/v3/ngx_http_v3_module.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 9daaedb3e..efad51c71 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -111,6 +111,13 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, quic.active_connection_id_limit), &ngx_http_v3_active_connection_id_limit_bounds }, + { ngx_string("quic_retry"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.retry), + NULL }, + ngx_null_command }; @@ -257,6 +264,8 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) v3cf->quic.disable_active_migration = NGX_CONF_UNSET_UINT; v3cf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT; + v3cf->quic.retry = NGX_CONF_UNSET; + return v3cf; } @@ -310,6 +319,15 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->quic.active_connection_id_limit, prev->quic.active_connection_id_limit, 2); + ngx_conf_merge_value(conf->quic.retry, prev->quic.retry, 0); + + if (conf->quic.retry) { + if (RAND_bytes(conf->quic.token_key, sizeof(conf->quic.token_key)) <= 0) { + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; } -- cgit From 51e4e31a8dbfd856dbc1875208fa37eeb56ddacc Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 20 May 2020 15:36:24 +0300 Subject: Assorted fixes. Found by Clang Static Analyzer. --- src/http/v3/ngx_http_v3_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index e0ee0c882..c4b313d31 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -618,7 +618,7 @@ ngx_http_v3_create_header(ngx_http_request_t *r) b = ngx_create_temp_buf(c->pool, len); if (b == NULL) { - NULL; + return NULL; } *b->last++ = NGX_HTTP_V3_FRAME_DATA; -- cgit From 1c54340e0aa65781f6cd4cc643f704826c47459a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 19 May 2020 15:41:41 +0300 Subject: HTTP/3: prevent array access by negative index for unknown streams. Currently there are no such streams, but the function ngx_http_v3_get_uni_stream() supports them. --- src/http/v3/ngx_http_v3_streams.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 165f21fdc..bd4583998 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -320,7 +320,9 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) sc->read->handler = ngx_http_v3_uni_read_handler; sc->write->handler = ngx_http_v3_dummy_write_handler; - h3c->known_streams[index] = sc; + if (index >= 0) { + h3c->known_streams[index] = sc; + } n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf; -- cgit From d69f678e9c788e2c2424eb6dc2fbacf8366213f6 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 14 May 2020 14:49:53 +0300 Subject: HTTP/3: move body parser call out of ngx_http_parse_chunked(). The function ngx_http_parse_chunked() is also called from the proxy module to parse the upstream response. It should always parse HTTP/1 body in this case. --- src/http/ngx_http_parse.c | 6 ------ src/http/ngx_http_request_body.c | 22 ++++++++++++++++++++-- 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 92bcf12ad..f66593443 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -2185,12 +2185,6 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, sw_trailer_header_almost_done } state; -#if (NGX_HTTP_V3) - if (r->http_version == NGX_HTTP_VERSION_30) { - return ngx_http_v3_parse_request_body(r, b, ctx); - } -#endif - state = ctx->state; if (state == sw_chunk_data && ctx->size == 0) { diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index b07d8562f..568f11f02 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -735,7 +735,16 @@ ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b) for ( ;; ) { - rc = ngx_http_parse_chunked(r, b, rb->chunked); + switch (r->http_version) { +#if (NGX_HTTP_V3) + case NGX_HTTP_VERSION_30: + rc = ngx_http_v3_parse_request_body(r, b, rb->chunked); + break; +#endif + + default: /* HTTP/1.x */ + rc = ngx_http_parse_chunked(r, b, rb->chunked); + } if (rc == NGX_OK) { @@ -978,7 +987,16 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); - rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked); + switch (r->http_version) { +#if (NGX_HTTP_V3) + case NGX_HTTP_VERSION_30: + rc = ngx_http_v3_parse_request_body(r, cl->buf, rb->chunked); + break; +#endif + + default: /* HTTP/1.x */ + rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked); + } if (rc == NGX_OK) { -- cgit From 6abb50658fcdf6ab92a4bc9042cd7dd9bb413850 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 19 May 2020 15:29:10 +0300 Subject: HTTP/3: split header parser in two functions. The first one parses pseudo-headers and is analagous to the request line parser in HTTP/1. The second one parses regular headers and is analogous to the header parser in HTTP/1. Additionally, error handling of client passing malformed uri is now fixed. --- src/http/ngx_http_request.c | 2 +- src/http/v3/ngx_http_v3.h | 1 + src/http/v3/ngx_http_v3_request.c | 142 ++++++++++++++++++++------------------ 3 files changed, 77 insertions(+), 68 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index b0cc9a543..e77c4bc35 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1164,7 +1164,7 @@ ngx_http_process_request_line(ngx_event_t *rev) switch (r->http_version) { #if (NGX_HTTP_V3) case NGX_HTTP_VERSION_30: - rc = ngx_http_v3_parse_header(r, r->header_in); + rc = ngx_http_v3_parse_request(r, r->header_in); break; #endif diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 3f35e985e..f80b74f3a 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -67,6 +67,7 @@ typedef struct { } ngx_http_v3_header_t; +ngx_int_t ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b); ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b); ngx_int_t ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, ngx_http_chunked_t *ctx); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index c4b313d31..2bb627489 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -38,21 +38,14 @@ struct { ngx_int_t -ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b) +ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) { - size_t n; + size_t len; u_char *p; - ngx_int_t rc; + ngx_int_t rc, n; ngx_str_t *name, *value; ngx_connection_t *c; ngx_http_v3_parse_headers_t *st; - enum { - sw_start = 0, - sw_prev, - sw_headers, - sw_last, - sw_done - }; c = r->connection; st = r->h3_parse; @@ -68,23 +61,6 @@ ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b) r->h3_parse = st; } - switch (r->state) { - - case sw_prev: - r->state = sw_headers; - return NGX_OK; - - case sw_done: - goto done; - - case sw_last: - r->state = sw_done; - return NGX_OK; - - default: - break; - } - while (b->pos < b->last) { rc = ngx_http_v3_parse_headers(c, st, *b->pos++); @@ -99,68 +75,100 @@ ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b) name = &st->header_rep.header.name; value = &st->header_rep.header.value; - if (r->state == sw_start) { + n = ngx_http_v3_process_pseudo_header(r, name, value); - if (ngx_http_v3_process_pseudo_header(r, name, value) == NGX_OK) { - if (rc == NGX_OK) { - continue; - } + if (n == NGX_ERROR) { + goto failed; + } - r->state = sw_done; + if (n == NGX_OK && rc == NGX_OK) { + continue; + } - } else if (rc == NGX_OK) { - r->state = sw_prev; + len = (r->method_end - r->method_start) + 1 + + (r->uri_end - r->uri_start) + 1 + + sizeof("HTTP/3") - 1; - } else { - r->state = sw_last; - } + p = ngx_pnalloc(c->pool, len); + if (p == NULL) { + goto failed; + } - n = (r->method_end - r->method_start) + 1 - + (r->uri_end - r->uri_start) + 1 - + sizeof("HTTP/3") - 1; + r->request_start = p; + + p = ngx_cpymem(p, r->method_start, r->method_end - r->method_start); + *p++ = ' '; + p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start); + *p++ = ' '; + p = ngx_cpymem(p, "HTTP/3", sizeof("HTTP/3") - 1); + + r->request_end = p; + + return NGX_OK; + } + + return NGX_AGAIN; + +failed: + + return NGX_HTTP_PARSE_INVALID_REQUEST; +} - p = ngx_pnalloc(c->pool, n); - if (p == NULL) { - goto failed; - } - r->request_start = p; +ngx_int_t +ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b) +{ + ngx_int_t rc; + ngx_str_t *name, *value; + ngx_connection_t *c; + ngx_http_v3_parse_headers_t *st; - p = ngx_cpymem(p, r->method_start, r->method_end - r->method_start); - *p++ = ' '; - p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start); - *p++ = ' '; - p = ngx_cpymem(p, "HTTP/3", sizeof("HTTP/3") - 1); + c = r->connection; + st = r->h3_parse; - r->request_end = p; + if (st->state == 0) { + if (r->header_name_start == NULL) { + name = &st->header_rep.header.name; - } else if (rc == NGX_DONE) { - r->state = sw_done; + if (name->len && name->data[0] != ':') { + goto done; + } } - r->header_name_start = name->data; - r->header_name_end = name->data + name->len; - r->header_start = value->data; - r->header_end = value->data + value->len; - r->header_hash = ngx_hash_key(name->data, name->len); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse header done"); - /* XXX r->lowcase_index = i; */ + return NGX_HTTP_PARSE_HEADER_DONE; + } - return NGX_OK; + while (b->pos < b->last) { + rc = ngx_http_v3_parse_headers(c, st, *b->pos++); + + if (rc == NGX_ERROR) { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + if (rc != NGX_AGAIN) { + goto done; + } } return NGX_AGAIN; -failed: +done: - return r->state == sw_start ? NGX_HTTP_PARSE_INVALID_REQUEST - : NGX_HTTP_PARSE_INVALID_HEADER; + name = &st->header_rep.header.name; + value = &st->header_rep.header.value; -done: + r->header_name_start = name->data; + r->header_name_end = name->data + name->len; + r->header_start = value->data; + r->header_end = value->data + value->len; + r->header_hash = ngx_hash_key(name->data, name->len); - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header done"); + /* XXX r->lowcase_index = i; */ - return NGX_HTTP_PARSE_HEADER_DONE; + return NGX_OK; } -- cgit From d25937c2b596013874fcb049f10b28270ee49e29 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 19 May 2020 15:34:00 +0300 Subject: HTTP/3: restricted symbols in header names. As per HTTP/3 draft 27, a request or response containing uppercase header field names MUST be treated as malformed. Also, existing rules applied when parsing HTTP/1 header names are also applied to HTTP/3 header names: - null character is not allowed - underscore character may or may not be treated as invalid depending on the value of "underscores_in_headers" - all non-alphanumeric characters with the exception of '-' are treated as invalid Also, the r->locase_header field is now filled while parsing an HTTP/3 header. Error logging for invalid headers is fixed as well. --- src/http/ngx_http_request.c | 10 ++++---- src/http/v3/ngx_http_v3.h | 3 ++- src/http/v3/ngx_http_v3_request.c | 49 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 8 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index e77c4bc35..e3d217f79 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1511,7 +1511,8 @@ ngx_http_process_request_headers(ngx_event_t *rev) switch (r->http_version) { #if (NGX_HTTP_V3) case NGX_HTTP_VERSION_30: - rc = ngx_http_v3_parse_header(r, r->header_in); + rc = ngx_http_v3_parse_header(r, r->header_in, + cscf->underscores_in_headers); break; #endif @@ -1530,9 +1531,10 @@ ngx_http_process_request_headers(ngx_event_t *rev) /* there was error while a header line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid header line: \"%*s\"", - r->header_end - r->header_name_start, - r->header_name_start); + "client sent invalid header line: \"%*s: %*s\"", + r->header_name_end - r->header_name_start, + r->header_name_start, + r->header_end - r->header_start, r->header_start); continue; } diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index f80b74f3a..29cc06ee9 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -68,7 +68,8 @@ typedef struct { ngx_int_t ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b); -ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b); +ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, + ngx_uint_t allow_underscores); ngx_int_t ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, ngx_http_chunked_t *ctx); ngx_chain_t *ngx_http_v3_create_header(ngx_http_request_t *r); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 2bb627489..59b8ce5b8 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -116,16 +116,23 @@ failed: ngx_int_t -ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b) +ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, + ngx_uint_t allow_underscores) { + u_char ch; ngx_int_t rc; ngx_str_t *name, *value; + ngx_uint_t hash, i, n; ngx_connection_t *c; ngx_http_v3_parse_headers_t *st; c = r->connection; st = r->h3_parse; + if (st->header_rep.state == 0) { + r->invalid_header = 0; + } + if (st->state == 0) { if (r->header_name_start == NULL) { name = &st->header_rep.header.name; @@ -164,9 +171,45 @@ done: r->header_name_end = name->data + name->len; r->header_start = value->data; r->header_end = value->data + value->len; - r->header_hash = ngx_hash_key(name->data, name->len); - /* XXX r->lowcase_index = i; */ + hash = 0; + i = 0; + + for (n = 0; n < name->len; n++) { + ch = name->data[n]; + + if (ch >= 'A' && ch <= 'Z') { + /* + * A request or response containing uppercase + * header field names MUST be treated as malformed + */ + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + if (ch == '\0') { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + if (ch == '_' && !allow_underscores) { + r->invalid_header = 1; + continue; + } + + if ((ch < 'a' || ch > 'z') + && (ch < '0' || ch > '9') + && ch != '-' && ch != '_') + { + r->invalid_header = 1; + continue; + } + + hash = ngx_hash(hash, ch); + r->lowcase_header[i++] = ch; + i &= (NGX_HTTP_LC_HEADER_LEN - 1); + } + + r->header_hash = hash; + r->lowcase_index = i; return NGX_OK; } -- cgit From d6b6b6dfc57c916dc7990504fffe71248421456d Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 19 May 2020 15:47:37 +0300 Subject: Fixed $request_length for HTTP/3. New field r->parse_start is introduced to substitute r->request_start and r->header_name_start for request length accounting. These fields only work for this purpose in HTTP/1 because HTTP/1 request line and header line start with these values. Also, error logging is now fixed to output the right part of the request. --- src/http/ngx_http_parse.c | 2 ++ src/http/ngx_http_request.c | 27 ++++++++++++++------------- src/http/ngx_http_request.h | 1 + src/http/v3/ngx_http_v3_request.c | 2 ++ 4 files changed, 19 insertions(+), 13 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index f66593443..b59a6ce4c 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -143,6 +143,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) /* HTTP methods: GET, HEAD, POST */ case sw_start: + r->parse_start = p; r->request_start = p; r->method_start = p; @@ -883,6 +884,7 @@ ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b, /* first char */ case sw_start: + r->parse_start = p; r->header_name_start = p; r->invalid_header = 0; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index e3d217f79..baef5f444 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1178,7 +1178,7 @@ ngx_http_process_request_line(ngx_event_t *rev) r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; - r->request_length = r->header_in->pos - r->request_start; /* XXX */ + r->request_length = r->header_in->pos - r->parse_start; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http request line: \"%V\"", &r->request_line); @@ -1293,8 +1293,8 @@ ngx_http_process_request_line(ngx_event_t *rev) } if (rv == NGX_DECLINED) { - r->request_line.len = r->header_in->end - r->request_start; - r->request_line.data = r->request_start; + r->request_line.len = r->header_in->end - r->parse_start; + r->request_line.data = r->parse_start; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long URI"); @@ -1470,7 +1470,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) } if (rv == NGX_DECLINED) { - p = r->header_name_start; + p = r->parse_start; r->lingering_close = 1; @@ -1490,7 +1490,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long header line: \"%*s...\"", - len, r->header_name_start); + len, r->parse_start); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); @@ -1523,8 +1523,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) if (rc == NGX_OK) { - /* XXX */ - r->request_length += r->header_in->pos - r->header_name_start; + r->request_length += r->header_in->pos - r->parse_start; if (r->invalid_header && cscf->ignore_invalid_headers) { @@ -1596,7 +1595,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header done"); - r->request_length += r->header_in->pos - r->header_name_start; /* XXX */ + r->request_length += r->header_in->pos - r->parse_start; r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; @@ -1711,7 +1710,7 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, return NGX_OK; } - old = request_line ? r->request_start : r->header_name_start; /* XXX */ + old = r->parse_start; cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); @@ -1783,6 +1782,8 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, b->pos = new + (r->header_in->pos - old); b->last = new + (r->header_in->pos - old); + r->parse_start = new; + if (request_line) { r->request_start = new; @@ -3892,15 +3893,15 @@ ngx_http_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr, len -= p - buf; buf = p; - if (r->request_line.data == NULL && r->request_start) { - for (p = r->request_start; p < r->header_in->last; p++) { + if (r->request_line.data == NULL && r->parse_start) { + for (p = r->parse_start; p < r->header_in->last; p++) { if (*p == CR || *p == LF) { break; } } - r->request_line.len = p - r->request_start; - r->request_line.data = r->request_start; + r->request_line.len = p - r->parse_start; + r->request_line.data = r->parse_start; } if (r->request_line.len) { diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 772d53b2d..1994000a4 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -579,6 +579,7 @@ struct ngx_http_request_s { * via ngx_http_ephemeral_t */ + u_char *parse_start; u_char *uri_start; u_char *uri_end; u_char *uri_ext; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 59b8ce5b8..432bc8711 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -59,6 +59,7 @@ ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) } r->h3_parse = st; + r->parse_start = b->pos; } while (b->pos < b->last) { @@ -130,6 +131,7 @@ ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, st = r->h3_parse; if (st->header_rep.state == 0) { + r->parse_start = b->pos; r->invalid_header = 0; } -- cgit From 94764fda6efa48fc18b2fc0a2cd82d451e947094 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 19 May 2020 16:20:33 +0300 Subject: Fixed client buffer reallocation for HTTP/3. Preserving pointers within the client buffer is not needed for HTTP/3 because all data is either allocated from pool or static. Unlike with HTTP/1, data typically cannot be referenced directly within the client buffer. Trying to preserve NULLs or external pointers lead to broken pointers. Also, reverted changes in ngx_http_alloc_large_header_buffer() not relevant for HTTP/3 to minimize diff to mainstream. --- src/http/ngx_http_request.c | 48 ++++++++++++++++----------------------- src/http/v3/ngx_http_v3_request.c | 1 + 2 files changed, 20 insertions(+), 29 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index baef5f444..1b3573598 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1784,6 +1784,12 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, r->parse_start = new; + r->header_in = b; + + if (r->http_version > NGX_HTTP_VERSION_11) { + return NGX_OK; + } + if (request_line) { r->request_start = new; @@ -1791,63 +1797,47 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, r->request_end = new + (r->request_end - old); } - if (r->method_start >= old && r->method_start < r->header_in->pos) { - r->method_start = new + (r->method_start - old); - r->method_end = new + (r->method_end - old); - } + r->method_end = new + (r->method_end - old); - if (r->uri_start >= old && r->uri_start < r->header_in->pos) { - r->uri_start = new + (r->uri_start - old); - r->uri_end = new + (r->uri_end - old); - } + r->uri_start = new + (r->uri_start - old); + r->uri_end = new + (r->uri_end - old); - if (r->schema_start >= old && r->schema_start < r->header_in->pos) { + if (r->schema_start) { r->schema_start = new + (r->schema_start - old); r->schema_end = new + (r->schema_end - old); } - if (r->host_start >= old && r->host_start < r->header_in->pos) { + if (r->host_start) { r->host_start = new + (r->host_start - old); if (r->host_end) { r->host_end = new + (r->host_end - old); } } - if (r->port_start >= old && r->port_start < r->header_in->pos) { + if (r->port_start) { r->port_start = new + (r->port_start - old); r->port_end = new + (r->port_end - old); } - if (r->uri_ext >= old && r->uri_ext < r->header_in->pos) { + if (r->uri_ext) { r->uri_ext = new + (r->uri_ext - old); } - if (r->args_start >= old && r->args_start < r->header_in->pos) { + if (r->args_start) { r->args_start = new + (r->args_start - old); } - if (r->http_protocol.data >= old - && r->http_protocol.data < r->header_in->pos) - { + if (r->http_protocol.data) { r->http_protocol.data = new + (r->http_protocol.data - old); } } else { - if (r->header_name_start >= old - && r->header_name_start < r->header_in->pos) - { - r->header_name_start = new; - r->header_name_end = new + (r->header_name_end - old); - } - - if (r->header_start >= old && r->header_start < r->header_in->pos) { - r->header_start = new + (r->header_start - old); - r->header_end = new + (r->header_end - old); - } + r->header_name_start = new; + r->header_name_end = new + (r->header_name_end - old); + r->header_start = new + (r->header_start - old); + r->header_end = new + (r->header_end - old); } - r->header_in = b; - return NGX_OK; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 432bc8711..7fb297728 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -60,6 +60,7 @@ ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) r->h3_parse = st; r->parse_start = b->pos; + r->state = 1; } while (b->pos < b->last) { -- cgit From 097d8a87b3f8c0d4f12cc468d35a590ae18e81ed Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 14 May 2020 16:02:32 +0300 Subject: HTTP/3: reallocate strings inserted into the dynamic table. They should always be allocated from the main QUIC connection pool. --- src/http/v3/ngx_http_v3_tables.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index f58f190f1..ecebe943c 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -149,12 +149,15 @@ ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value) { ngx_array_t *dt; + ngx_connection_t *pc; ngx_http_v3_header_t *ref, *h; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 ref insert %s[$ui] \"%V\"", dynamic ? "dynamic" : "static", index, value); + pc = c->qs->parent; + ref = ngx_http_v3_lookup_table(c, dynamic, index); if (ref == NULL) { return NGX_ERROR; @@ -171,7 +174,14 @@ ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, } h->name = ref->name; - h->value = *value; + + h->value.data = ngx_pstrdup(pc->pool, value); + if (h->value.data == NULL) { + h->value.len = 0; + return NGX_ERROR; + } + + h->value.len = value->len; if (ngx_http_v3_new_header(c) != NGX_OK) { return NGX_ERROR; @@ -186,11 +196,14 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) { ngx_array_t *dt; + ngx_connection_t *pc; ngx_http_v3_header_t *h; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 insert \"%V\":\"%V\"", name, value); + pc = c->qs->parent; + dt = ngx_http_v3_get_dynamic_table(c); if (dt == NULL) { return NGX_ERROR; @@ -201,8 +214,22 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, return NGX_ERROR; } - h->name = *name; - h->value = *value; + h->name.data = ngx_pstrdup(pc->pool, name); + if (h->name.data == NULL) { + h->name.len = 0; + h->value.len = 0; + return NGX_ERROR; + } + + h->name.len = name->len; + + h->value.data = ngx_pstrdup(pc->pool, value); + if (h->value.data == NULL) { + h->value.len = 0; + return NGX_ERROR; + } + + h->value.len = value->len; if (ngx_http_v3_new_header(c) != NGX_OK) { return NGX_ERROR; -- cgit From 76bbe70406fe9541c6a6b07e68c833cfb15a46aa Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 29 May 2020 12:56:08 +0300 Subject: Renamed max_packet_size to max_udp_payload_size, from draft-28. No functional changes. --- src/http/v3/ngx_http_v3_module.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index efad51c71..434b7e73d 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -11,14 +11,14 @@ static char *ngx_http_v3_max_ack_delay(ngx_conf_t *cf, void *post, void *data); -static char *ngx_http_v3_max_packet_size(ngx_conf_t *cf, void *post, +static char *ngx_http_v3_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data); static ngx_conf_post_t ngx_http_v3_max_ack_delay_post = { ngx_http_v3_max_ack_delay }; -static ngx_conf_post_t ngx_http_v3_max_packet_size_post = - { ngx_http_v3_max_packet_size }; +static ngx_conf_post_t ngx_http_v3_max_udp_payload_size_post = + { ngx_http_v3_max_udp_payload_size }; static ngx_conf_num_bounds_t ngx_http_v3_ack_delay_exponent_bounds = { ngx_conf_check_num_bounds, 0, 20 }; static ngx_conf_num_bounds_t ngx_http_v3_active_connection_id_limit_bounds = @@ -41,12 +41,12 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, quic.max_ack_delay), &ngx_http_v3_max_ack_delay_post }, - { ngx_string("quic_max_packet_size"), + { ngx_string("quic_max_udp_payload_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, quic.max_packet_size), - &ngx_http_v3_max_packet_size_post }, + offsetof(ngx_http_v3_srv_conf_t, quic.max_udp_payload_size), + &ngx_http_v3_max_udp_payload_size_post }, { ngx_string("quic_initial_max_data"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, @@ -253,7 +253,7 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) 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_SIZE; + v3cf->quic.max_udp_payload_size = NGX_CONF_UNSET_SIZE; v3cf->quic.initial_max_data = NGX_CONF_UNSET_SIZE; v3cf->quic.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_SIZE; v3cf->quic.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_SIZE; @@ -283,9 +283,9 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) prev->quic.max_ack_delay, NGX_QUIC_DEFAULT_MAX_ACK_DELAY); - ngx_conf_merge_size_value(conf->quic.max_packet_size, - prev->quic.max_packet_size, - NGX_QUIC_DEFAULT_MAX_PACKET_SIZE); + ngx_conf_merge_size_value(conf->quic.max_udp_payload_size, + prev->quic.max_udp_payload_size, + NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); ngx_conf_merge_size_value(conf->quic.initial_max_data, prev->quic.initial_max_data, @@ -349,17 +349,18 @@ ngx_http_v3_max_ack_delay(ngx_conf_t *cf, void *post, void *data) static char * -ngx_http_v3_max_packet_size(ngx_conf_t *cf, void *post, void *data) +ngx_http_v3_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data) { size_t *sp = data; if (*sp < NGX_QUIC_MIN_INITIAL_SIZE - || *sp > NGX_QUIC_DEFAULT_MAX_PACKET_SIZE) + || *sp > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_max_packet_size\" must be between %d and %d", + "\"quic_max_udp_payload_size\" must be between " + "%d and %d", NGX_QUIC_MIN_INITIAL_SIZE, - NGX_QUIC_DEFAULT_MAX_PACKET_SIZE); + NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); return NGX_CONF_ERROR; } -- cgit From 7d41fd85b8d6679335798740ce9bbae7586ae649 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 29 May 2020 15:06:33 +0300 Subject: QUIC draft-28 transport parameters support. Draft-27 and draft-28 support can now be enabled interchangeably, it's based on the compile-time macro NGX_QUIC_DRAFT_VERSION. --- src/http/v3/ngx_http_v3_module.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 434b7e73d..550b706da 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -245,7 +245,9 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) /* * set by ngx_pcalloc(): - * v3cf->quic.original_connection_id = 0; + * v3cf->quic.original_dcid = { 0, NULL }; + * v3cf->quic.initial_scid = { 0, NULL }; + * v3cf->quic.retry_scid = { 0, NULL }; * v3cf->quic.stateless_reset_token = { 0 } * conf->quic.preferred_address = NULL */ -- cgit From 101113a98f92b023c7d6586e45767ba3b886abd7 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Fri, 29 May 2020 13:29:24 +0300 Subject: Added propagation of the "wildcard" flag to c->listening. The flags was originally added by 8f038068f4bc, and is propagated correctly in the stream module. With QUIC introduction, http module now uses datagram sockets as well, thus the fix. --- src/http/ngx_http.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 10f88fabb..1b5e387db 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1795,6 +1795,8 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) ls->reuseport = addr->opt.reuseport; #endif + ls->wildcard = addr->opt.wildcard; + return ls; } -- cgit From 22297afd7924d00440105bc440aa4f67fde380fe Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 29 May 2020 12:42:23 +0300 Subject: Require ":authority" or "Host" in HTTP/3 and HTTP/2 requests. Also, if both are present, require that they have the same value. These requirements are specified in HTTP/3 draft 28. Current implementation of HTTP/2 treats ":authority" and "Host" interchangeably. New checks only make sure at least one of these values is present in the request. A similar check existed earlier and was limited only to HTTP/1.1 in 38c0898b6df7. --- src/http/ngx_http_request.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 3e6fce676..23b28c243 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2065,6 +2065,31 @@ ngx_http_process_request_header(ngx_http_request_t *r) return NGX_ERROR; } + if (r->http_version >= NGX_HTTP_VERSION_20) { + if (r->headers_in.server.len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent HTTP request without " + "\":authority\" or \"Host\" header"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + + if (r->headers_in.host) { + if (r->headers_in.host->value.len != r->headers_in.server.len + || ngx_memcmp(r->headers_in.host->value.data, + r->headers_in.server.data, + r->headers_in.server.len) + != 0) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent HTTP request with different " + "values of \":authority\" and \"Host\" headers"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + } + } + if (r->headers_in.content_length) { r->headers_in.content_length_n = ngx_atoof(r->headers_in.content_length->value.data, -- cgit From c0003539ac767ec9d16e54d26b5296a6669d0089 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 2 Jun 2020 15:59:14 +0300 Subject: Decoupled validation of Host and :authority for HTTP/2 and HTTP/3. Previously an error was triggered for HTTP/2 when host with port was passed by client. --- src/http/ngx_http_request.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 23b28c243..ac5937347 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2065,10 +2065,18 @@ ngx_http_process_request_header(ngx_http_request_t *r) return NGX_ERROR; } - if (r->http_version >= NGX_HTTP_VERSION_20) { + if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_20) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent HTTP/2 request without " + "\":authority\" or \"Host\" header"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + + if (r->http_version == NGX_HTTP_VERSION_30) { if (r->headers_in.server.len == 0) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent HTTP request without " + "client sent HTTP/3 request without " "\":authority\" or \"Host\" header"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return NGX_ERROR; @@ -2082,7 +2090,7 @@ ngx_http_process_request_header(ngx_http_request_t *r) != 0) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent HTTP request with different " + "client sent HTTP/3 request with different " "values of \":authority\" and \"Host\" headers"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return NGX_ERROR; -- cgit From 9d465009149c67325d9ac49503891cc84e5eaef8 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 23 Jun 2020 11:57:00 +0300 Subject: Do not close QUIC sockets in ngx_close_listening_sockets(). This breaks graceful shutdown of QUIC connections in terms of quic-transport. --- src/http/ngx_http.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 1b5e387db..0a645722c 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1797,6 +1797,10 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) ls->wildcard = addr->opt.wildcard; +#if (NGX_HTTP_SSL) + ls->quic = addr->opt.http3; +#endif + return ls; } -- cgit From 3168f58306820428fec2c2871fd03a325aa0c65c Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 25 Jun 2020 20:31:13 +0300 Subject: HTTP/3: do not emit a DATA frame header for header_only responses. This resulted in the frame error due to the invalid DATA frame length. --- src/http/v3/ngx_http_v3_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 7fb297728..d9ea99d2c 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -666,7 +666,7 @@ ngx_http_v3_create_header(ngx_http_request_t *r) hl->buf = b; hl->next = cl; - if (r->headers_out.content_length_n >= 0) { + if (r->headers_out.content_length_n >= 0 && !r->header_only) { len = 1 + ngx_http_v3_encode_varlen_int(NULL, r->headers_out.content_length_n); -- cgit From 690e1bc50cf47257ce76eeed2c0392a64bee24b0 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 26 Jun 2020 10:05:28 +0300 Subject: HTTP/3: fixed dropping first non-pseudo header. --- src/http/v3/ngx_http_v3_request.c | 49 ++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index d9ea99d2c..5747c8ee6 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -105,6 +105,7 @@ ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) p = ngx_cpymem(p, "HTTP/3", sizeof("HTTP/3") - 1); r->request_end = p; + r->state = 0; return NGX_OK; } @@ -127,28 +128,47 @@ ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t hash, i, n; ngx_connection_t *c; ngx_http_v3_parse_headers_t *st; + enum { + sw_start = 0, + sw_done, + sw_next, + sw_header + }; c = r->connection; st = r->h3_parse; - if (st->header_rep.state == 0) { + switch (r->state) { + + case sw_start: r->parse_start = b->pos; - r->invalid_header = 0; - } - if (st->state == 0) { - if (r->header_name_start == NULL) { - name = &st->header_rep.header.name; + if (st->state) { + r->state = sw_next; + goto done; + } - if (name->len && name->data[0] != ':') { - goto done; - } + name = &st->header_rep.header.name; + + if (name->len && name->data[0] != ':') { + r->state = sw_done; + goto done; } + /* fall through */ + + case sw_done: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header done"); - return NGX_HTTP_PARSE_HEADER_DONE; + + case sw_next: + r->parse_start = b->pos; + r->invalid_header = 0; + break; + + case sw_header: + break; } while (b->pos < b->last) { @@ -158,11 +178,18 @@ ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, return NGX_HTTP_PARSE_INVALID_HEADER; } - if (rc != NGX_AGAIN) { + if (rc == NGX_DONE) { + r->state = sw_done; + goto done; + } + + if (rc == NGX_OK) { + r->state = sw_next; goto done; } } + r->state = sw_header; return NGX_AGAIN; done: -- cgit From b7662c0e80003a676375be7d46071231321260e6 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 26 Jun 2020 11:58:00 +0300 Subject: HTTP/3: introduced ngx_http_v3_get_module_srv_conf() macro. The macro helps to access a module's server configuration from a QUIC stream context. --- src/http/v3/ngx_http_v3.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 29cc06ee9..3c5bb721c 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -61,6 +61,12 @@ typedef struct { } ngx_http_v3_connection_t; +#define ngx_http_v3_get_module_srv_conf(c, module) \ + ngx_http_get_module_srv_conf( \ + ((ngx_http_v3_connection_t *) c->qs->parent->data)->hc.conf_ctx, \ + module) + + typedef struct { ngx_str_t name; ngx_str_t value; -- cgit From 8d41d17a12509d6f80f51a27046b7a10882892ef Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 29 Jun 2020 15:56:14 +0300 Subject: HTTP/3: http3_max_field_size directive to limit string size. Client streams may send literal strings which are now limited in size by the new directive. The default value is 4096. The directive is similar to HTTP/2 directive http2_max_field_size. --- src/http/v3/ngx_http_v3.h | 3 +++ src/http/v3/ngx_http_v3_module.c | 12 ++++++++++++ src/http/v3/ngx_http_v3_parse.c | 11 ++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 3c5bb721c..d48f13922 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -47,9 +47,12 @@ #define NGX_HTTP_V3_STREAM_SERVER_DECODER 5 #define NGX_HTTP_V3_MAX_KNOWN_STREAM 6 +#define NGX_HTTP_V3_DEFAULT_MAX_FIELD_SIZE 4096 + typedef struct { ngx_quic_tp_t quic; + size_t max_field_size; } ngx_http_v3_srv_conf_t; diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 550b706da..17029934e 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -118,6 +118,13 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, quic.retry), NULL }, + { ngx_string("http3_max_field_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, max_field_size), + NULL }, + ngx_null_command }; @@ -268,6 +275,8 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) v3cf->quic.retry = NGX_CONF_UNSET; + v3cf->max_field_size = NGX_CONF_UNSET_SIZE; + return v3cf; } @@ -329,6 +338,9 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } } + ngx_conf_merge_size_value(conf->max_field_size, + prev->max_field_size, + NGX_HTTP_V3_DEFAULT_MAX_FIELD_SIZE); return NGX_CONF_OK; } diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 3be3802ed..71a643d70 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -399,7 +399,8 @@ ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, u_char ch) { - ngx_uint_t n; + ngx_uint_t n; + ngx_http_v3_srv_conf_t *v3cf; enum { sw_start = 0, sw_value @@ -415,6 +416,14 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, n = st->length; + v3cf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + if (n > v3cf->max_field_size) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "client exceeded http3_max_field_size limit"); + return NGX_ERROR; + } + if (st->huffman) { n = n * 8 / 5; st->huffstate = 0; -- cgit From a7ef0da3c8b64f2b5f4d8a7e73e724a74611284c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 2 Jul 2020 15:15:55 +0300 Subject: HTTP/3: fixed prefixed integer encoding and decoding. Previously bytes were ordered from MSB to LSB, but the right order is the reverse. --- src/http/v3/ngx_http_v3.c | 17 ++++++++--------- src/http/v3/ngx_http_v3_parse.c | 13 +++++++------ src/http/v3/ngx_http_v3_parse.h | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index f7f79b9f4..e796da772 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -78,23 +78,22 @@ ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix) value -= thresh; - for (n = 10; n > 1; n--) { - if (value >> (7 * (n - 1))) { - break; + if (p == NULL) { + for (n = 2; value >= 128; n++) { + value >>= 7; } - } - if (p == NULL) { - return n + 1; + return n; } *p++ |= thresh; - for ( /* void */ ; n > 1; n--) { - *p++ = 0x80 | (value >> 7 * (n - 1)); + while (value >= 128) { + *p++ = 0x80 | value; + value >>= 7; } - *p++ = value & 0x7f; + *p++ = value; return (uintptr_t) p; } diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 71a643d70..85491082e 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -91,6 +91,7 @@ ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch) { + ngx_uint_t mask; enum { sw_start = 0, sw_value @@ -100,25 +101,25 @@ ngx_http_v3_parse_prefix_int(ngx_connection_t *c, case sw_start: - st->mask = (1 << prefix) - 1; - st->value = (ch & st->mask); + mask = (1 << prefix) - 1; + st->value = ch & mask; - if (st->value != st->mask) { + if (st->value != mask) { goto done; } - st->value = 0; + st->shift = 0; st->state = sw_value; break; case sw_value: - st->value = (st->value << 7) + (ch & 0x7f); + st->value += (ch & 0x7f) << st->shift; if (ch & 0x80) { + st->shift += 7; break; } - st->value += st->mask; goto done; } diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h index ec78c7c35..17ff6c777 100644 --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -22,7 +22,7 @@ typedef struct { typedef struct { ngx_uint_t state; - ngx_uint_t mask; + ngx_uint_t shift; uint64_t value; } ngx_http_v3_parse_prefix_int_t; -- cgit From a687d08062d8cb029ab82249aa55833cf44be3ce Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 2 Jul 2020 15:34:05 +0300 Subject: HTTP/3: refactored dynamic table implementation. Previously dynamic table was not functional because of zero limit on its size set by default. Now the following changes enable it: - new directives to set SETTINGS_QPACK_MAX_TABLE_CAPACITY and SETTINGS_QPACK_BLOCKED_STREAMS - send settings with SETTINGS_QPACK_MAX_TABLE_CAPACITY and SETTINGS_QPACK_BLOCKED_STREAMS to the client - send Insert Count Increment to the client - send Header Acknowledgement to the client - evict old dynamic table entries on overflow - decode Required Insert Count from client - block stream if Required Insert Count is not reached --- src/http/ngx_http_request.c | 21 +- src/http/v3/ngx_http_v3.h | 53 +++-- src/http/v3/ngx_http_v3_module.c | 24 ++ src/http/v3/ngx_http_v3_parse.c | 111 +++++++-- src/http/v3/ngx_http_v3_request.c | 8 +- src/http/v3/ngx_http_v3_streams.c | 54 ++++- src/http/v3/ngx_http_v3_tables.c | 479 ++++++++++++++++++++++++++++---------- 7 files changed, 584 insertions(+), 166 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index ac5937347..89e554bf2 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -223,7 +223,17 @@ ngx_http_init_connection(ngx_connection_t *c) #if (NGX_HTTP_V3) if (c->type == SOCK_DGRAM) { - hc = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t)); + ngx_http_v3_connection_t *h3c; + + h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t)); + if (h3c == NULL) { + ngx_http_close_connection(c); + return; + } + + ngx_queue_init(&h3c->blocked); + + hc = &h3c->hc; hc->quic = 1; hc->ssl = 1; @@ -414,6 +424,13 @@ ngx_http_quic_stream_handler(ngx_connection_t *c) pc = c->qs->parent; h3c = pc->data; + if (!h3c->settings_sent) { + h3c->settings_sent = 1; + + /* TODO close QUIC connection on error */ + (void) ngx_http_v3_send_settings(c); + } + if (c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { ngx_http_v3_handle_client_uni_stream(c); return; @@ -1255,7 +1272,7 @@ ngx_http_process_request_line(ngx_event_t *rev) break; } - if (rc == NGX_DONE) { + if (rc == NGX_BUSY) { if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index d48f13922..b51febcdd 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -48,32 +48,48 @@ #define NGX_HTTP_V3_MAX_KNOWN_STREAM 6 #define NGX_HTTP_V3_DEFAULT_MAX_FIELD_SIZE 4096 +#define NGX_HTTP_V3_DEFAULT_MAX_TABLE_CAPACITY 16384 +#define NGX_HTTP_V3_DEFAULT_MAX_BLOCKED_STREAMS 16 + + +#define ngx_http_v3_get_module_srv_conf(c, module) \ + ngx_http_get_module_srv_conf( \ + ((ngx_http_v3_connection_t *) c->qs->parent->data)->hc.conf_ctx, \ + module) typedef struct { - ngx_quic_tp_t quic; - size_t max_field_size; + ngx_quic_tp_t quic; + size_t max_field_size; + size_t max_table_capacity; + ngx_uint_t max_blocked_streams; } ngx_http_v3_srv_conf_t; typedef struct { - ngx_http_connection_t hc; - - ngx_array_t *dynamic; - ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; -} ngx_http_v3_connection_t; + ngx_str_t name; + ngx_str_t value; +} ngx_http_v3_header_t; -#define ngx_http_v3_get_module_srv_conf(c, module) \ - ngx_http_get_module_srv_conf( \ - ((ngx_http_v3_connection_t *) c->qs->parent->data)->hc.conf_ctx, \ - module) +typedef struct { + ngx_http_v3_header_t **elts; + ngx_uint_t nelts; + ngx_uint_t base; + size_t size; + size_t capacity; +} ngx_http_v3_dynamic_table_t; typedef struct { - ngx_str_t name; - ngx_str_t value; -} ngx_http_v3_header_t; + ngx_http_connection_t hc; + ngx_http_v3_dynamic_table_t table; + ngx_queue_t blocked; + ngx_uint_t nblocked; + ngx_uint_t settings_sent; + /* unsigned settings_sent:1; */ + ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; +} ngx_http_v3_connection_t; ngx_int_t ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b); @@ -88,6 +104,7 @@ uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix); +ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); void ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c); ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, @@ -99,8 +116,12 @@ ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index); ngx_int_t ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id); ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); -ngx_http_v3_header_t *ngx_http_v3_lookup_table(ngx_connection_t *c, - ngx_uint_t dynamic, ngx_uint_t index); +ngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_v3_decode_insert_count(ngx_connection_t *c, + ngx_uint_t *insert_count); ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count); ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 17029934e..386209cb7 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -125,6 +125,20 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, max_field_size), NULL }, + { ngx_string("http3_max_table_capacity"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, max_table_capacity), + NULL }, + + { ngx_string("http3_max_blocked_streams"), + 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, max_blocked_streams), + NULL }, + ngx_null_command }; @@ -276,6 +290,8 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) v3cf->quic.retry = NGX_CONF_UNSET; v3cf->max_field_size = NGX_CONF_UNSET_SIZE; + v3cf->max_table_capacity = NGX_CONF_UNSET_SIZE; + v3cf->max_blocked_streams = NGX_CONF_UNSET_UINT; return v3cf; } @@ -342,6 +358,14 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) prev->max_field_size, NGX_HTTP_V3_DEFAULT_MAX_FIELD_SIZE); + ngx_conf_merge_size_value(conf->max_table_capacity, + prev->max_table_capacity, + NGX_HTTP_V3_DEFAULT_MAX_TABLE_CAPACITY); + + ngx_conf_merge_uint_value(conf->max_blocked_streams, + prev->max_blocked_streams, + NGX_HTTP_V3_DEFAULT_MAX_BLOCKED_STREAMS); + return NGX_CONF_OK; } diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 85491082e..bd91c5b02 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -10,6 +10,10 @@ #include +static ngx_int_t ngx_http_v3_parse_lookup(ngx_connection_t *c, + ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value); + + ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, ngx_http_v3_parse_varlen_int_t *st, u_char ch) @@ -144,6 +148,7 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, sw_start = 0, sw_length, sw_prefix, + sw_verify, sw_header_rep, sw_done }; @@ -195,9 +200,20 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, return NGX_ERROR; } - st->state = sw_header_rep; + st->state = sw_verify; break; + case sw_verify: + + rc = ngx_http_v3_check_insert_count(c, st->prefix.insert_count); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_header_rep; + + /* fall through */ + case sw_header_rep: rc = ngx_http_v3_parse_header_rep(c, &st->header_rep, st->prefix.base, @@ -228,6 +244,12 @@ done: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers done"); + if (st->prefix.insert_count > 0) { + if (ngx_http_v3_client_ack_header(c, c->qs->id) != NGX_OK) { + return NGX_ERROR; + } + } + st->state = sw_start; return NGX_DONE; } @@ -286,6 +308,10 @@ ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, done: + if (ngx_http_v3_decode_insert_count(c, &st->insert_count) != NGX_OK) { + return NGX_ERROR; + } + if (st->sign) { st->base = st->insert_count - st->delta_base - 1; } else { @@ -294,7 +320,7 @@ done: ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header block prefix done " - "i:%ui, s:%ui, d:%ui, base:%uL", + "insert_count:%ui, sign:%ui, delta_base:%ui, base:%uL", st->insert_count, st->sign, st->delta_base, st->base); st->state = sw_start; @@ -479,7 +505,6 @@ ngx_int_t ngx_http_v3_parse_header_ri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { - ngx_http_v3_header_t *h; enum { sw_start = 0, sw_index @@ -518,15 +543,14 @@ done: st->index = st->base - st->index - 1; } - h = ngx_http_v3_lookup_table(c, st->dynamic, st->index); - if (h == NULL) { + if (ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, + &st->value) + != NGX_OK) + { return NGX_ERROR; } - st->name = h->name; - st->value = h->value; st->state = sw_start; - return NGX_DONE; } @@ -535,8 +559,7 @@ ngx_int_t ngx_http_v3_parse_header_lri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { - ngx_int_t rc; - ngx_http_v3_header_t *h; + ngx_int_t rc; enum { sw_start = 0, sw_index, @@ -616,12 +639,12 @@ done: st->index = st->base - st->index - 1; } - h = ngx_http_v3_lookup_table(c, st->dynamic, st->index); - if (h == NULL) { + if (ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, NULL) + != NGX_OK) + { return NGX_ERROR; } - st->name = h->name; st->state = sw_start; return NGX_DONE; } @@ -735,7 +758,6 @@ ngx_int_t ngx_http_v3_parse_header_pbi(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { - ngx_http_v3_header_t *h; enum { sw_start = 0, sw_index @@ -768,13 +790,13 @@ done: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header pbi done dynamic[+%ui]", st->index); - h = ngx_http_v3_lookup_table(c, 1, st->base + st->index); - if (h == NULL) { + if (ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, + &st->value) + != NGX_OK) + { return NGX_ERROR; } - st->name = h->name; - st->value = h->value; st->state = sw_start; return NGX_DONE; } @@ -784,8 +806,7 @@ ngx_int_t ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { - ngx_int_t rc; - ngx_http_v3_header_t *h; + ngx_int_t rc; enum { sw_start = 0, sw_index, @@ -860,17 +881,57 @@ done: "http3 parse header lpbi done dynamic[+%ui] \"%V\"", st->index, &st->value); - h = ngx_http_v3_lookup_table(c, 1, st->base + st->index); - if (h == NULL) { + if (ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, NULL) + != NGX_OK) + { return NGX_ERROR; } - st->name = h->name; st->state = sw_start; return NGX_DONE; } +static ngx_int_t +ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *name, ngx_str_t *value) +{ + u_char *p; + + if (!dynamic) { + return ngx_http_v3_lookup_static(c, index, name, value); + } + + if (ngx_http_v3_lookup(c, index, name, value) != NGX_OK) { + return NGX_ERROR; + } + + if (name) { + p = ngx_pnalloc(c->pool, name->len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, name->data, name->len); + p[name->len] = '\0'; + name->data = p; + } + + if (value) { + p = ngx_pnalloc(c->pool, value->len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, value->data, value->len); + p[value->len] = '\0'; + value->data = p; + } + + return NGX_OK; +} + + ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) { @@ -1145,7 +1206,7 @@ done: "http3 parse encoder instruction done"); st->state = sw_start; - return NGX_DONE; + return NGX_AGAIN; } @@ -1429,7 +1490,7 @@ done: "http3 parse decoder instruction done"); st->state = sw_start; - return NGX_DONE; + return NGX_AGAIN; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 5747c8ee6..ae65ba9ea 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -64,12 +64,18 @@ ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) } while (b->pos < b->last) { - rc = ngx_http_v3_parse_headers(c, st, *b->pos++); + rc = ngx_http_v3_parse_headers(c, st, *b->pos); if (rc == NGX_ERROR) { goto failed; } + if (rc == NGX_BUSY) { + return NGX_BUSY; + } + + b->pos++; + if (rc == NGX_AGAIN) { continue; } diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index bd4583998..229ce5bf4 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -34,10 +34,6 @@ ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) { ngx_http_v3_uni_stream_t *us; - ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); - ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); - ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 new uni stream id:0x%uxL", c->qs->id); @@ -340,6 +336,56 @@ failed: } +ngx_int_t +ngx_http_v3_send_settings(ngx_connection_t *c) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; + size_t n; + ngx_connection_t *cc; + ngx_http_v3_srv_conf_t *v3cf; + ngx_http_v3_connection_t *h3c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); + + cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); + if (cc == NULL) { + return NGX_ERROR; + } + + h3c = c->qs->parent->data; + v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); + + n = ngx_http_v3_encode_varlen_int(NULL, + NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); + n += ngx_http_v3_encode_varlen_int(NULL, v3cf->max_table_capacity); + n += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); + n += ngx_http_v3_encode_varlen_int(NULL, v3cf->max_blocked_streams); + + p = (u_char *) ngx_http_v3_encode_varlen_int(buf, + NGX_HTTP_V3_FRAME_SETTINGS); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, + NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, v3cf->max_table_capacity); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, + NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, v3cf->max_blocked_streams); + n = p - buf; + + if (cc->send(cc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_http_v3_close_uni_stream(cc); + + return NGX_ERROR; +} + + ngx_int_t ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value) diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index ecebe943c..a57eccb9f 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -10,10 +10,22 @@ #include -static ngx_array_t *ngx_http_v3_get_dynamic_table(ngx_connection_t *c); +#define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) + + +static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); +static void ngx_http_v3_cleanup_table(void *data); +static void ngx_http_v3_unblock(void *data); static ngx_int_t ngx_http_v3_new_header(ngx_connection_t *c); +typedef struct { + ngx_queue_t queue; + ngx_connection_t *connection; + ngx_uint_t *nblocked; +} ngx_http_v3_block_t; + + static ngx_http_v3_header_t ngx_http_v3_static_table[] = { { ngx_string(":authority"), ngx_string("") }, @@ -148,40 +160,73 @@ ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value) { - ngx_array_t *dt; - ngx_connection_t *pc; - ngx_http_v3_header_t *ref, *h; + ngx_str_t name; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 ref insert %s[$ui] \"%V\"", - dynamic ? "dynamic" : "static", index, value); + if (dynamic) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ref insert dynamic[%ui] \"%V\"", index, value); - pc = c->qs->parent; + if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) { + return NGX_ERROR; + } - ref = ngx_http_v3_lookup_table(c, dynamic, index); - if (ref == NULL) { - return NGX_ERROR; - } + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ref insert static[%ui] \"%V\"", index, value); - dt = ngx_http_v3_get_dynamic_table(c); - if (dt == NULL) { - return NGX_ERROR; + if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) { + return NGX_ERROR; + } } - h = ngx_array_push(dt); - if (h == NULL) { + return ngx_http_v3_insert(c, &name, value); +} + + +ngx_int_t +ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) +{ + u_char *p; + size_t size; + ngx_http_v3_header_t *h; + ngx_http_v3_connection_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + size = ngx_http_v3_table_entry_size(name, value); + + if (ngx_http_v3_evict(c, size) != NGX_OK) { return NGX_ERROR; } - h->name = ref->name; + h3c = c->qs->parent->data; + dt = &h3c->table; - h->value.data = ngx_pstrdup(pc->pool, value); - if (h->value.data == NULL) { - h->value.len = 0; + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 insert [%ui] \"%V\":\"%V\", size:%uz", + dt->base + dt->nelts, name, value, size); + + p = ngx_alloc(sizeof(ngx_http_v3_header_t) + name->len + value->len, + c->log); + if (p == NULL) { return NGX_ERROR; } + h = (ngx_http_v3_header_t *) p; + + h->name.data = p + sizeof(ngx_http_v3_header_t); + h->name.len = name->len; + h->value.data = ngx_cpymem(h->name.data, name->data, name->len); h->value.len = value->len; + ngx_memcpy(h->value.data, value->data, value->len); + + dt->elts[dt->nelts++] = h; + dt->size += size; + + /* TODO increment can be sent less often */ + + if (ngx_http_v3_client_inc_insert_count(c, 1) != NGX_OK) { + return NGX_ERROR; + } if (ngx_http_v3_new_header(c) != NGX_OK) { return NGX_ERROR; @@ -192,95 +237,150 @@ ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_int_t -ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, - ngx_str_t *value) +ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) { - ngx_array_t *dt; - ngx_connection_t *pc; - ngx_http_v3_header_t *h; + ngx_uint_t max, prev_max; + ngx_connection_t *pc; + ngx_pool_cleanup_t *cln; + ngx_http_v3_header_t **elts; + ngx_http_v3_srv_conf_t *v3cf; + ngx_http_v3_connection_t *h3c; + ngx_http_v3_dynamic_table_t *dt; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 insert \"%V\":\"%V\"", name, value); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 set capacity %ui", capacity); pc = c->qs->parent; + h3c = pc->data; + v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); - dt = ngx_http_v3_get_dynamic_table(c); - if (dt == NULL) { + if (capacity > v3cf->max_table_capacity) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded http3_max_table_capacity limit"); return NGX_ERROR; } - h = ngx_array_push(dt); - if (h == NULL) { - return NGX_ERROR; - } + dt = &h3c->table; - h->name.data = ngx_pstrdup(pc->pool, name); - if (h->name.data == NULL) { - h->name.len = 0; - h->value.len = 0; - return NGX_ERROR; + if (dt->size > capacity) { + if (ngx_http_v3_evict(c, dt->size - capacity) != NGX_OK) { + return NGX_ERROR; + } } - h->name.len = name->len; + max = capacity / 32; + prev_max = dt->capacity / 32; - h->value.data = ngx_pstrdup(pc->pool, value); - if (h->value.data == NULL) { - h->value.len = 0; - return NGX_ERROR; - } + if (max > prev_max) { + elts = ngx_alloc(max * sizeof(void *), c->log); + if (elts == NULL) { + return NGX_ERROR; + } - h->value.len = value->len; + if (dt->elts == NULL) { + cln = ngx_pool_cleanup_add(pc->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } - if (ngx_http_v3_new_header(c) != NGX_OK) { - return NGX_ERROR; + cln->handler = ngx_http_v3_cleanup_table; + cln->data = dt; + + } else { + ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *)); + ngx_free(dt->elts); + } + + dt->elts = elts; } + dt->capacity = capacity; + return NGX_OK; } -ngx_int_t -ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) +static void +ngx_http_v3_cleanup_table(void *data) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 set capacity %ui", capacity); + ngx_http_v3_dynamic_table_t *dt = data; - /* XXX ignore capacity */ + ngx_uint_t n; - return NGX_OK; + for (n = 0; n < dt->nelts; n++) { + ngx_free(dt->elts[n]); + } + + ngx_free(dt->elts); } -ngx_int_t -ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) +static ngx_int_t +ngx_http_v3_evict(ngx_connection_t *c, size_t need) { - ngx_array_t *dt; - ngx_http_v3_header_t *ref, *h; + size_t size, target; + ngx_uint_t n; + ngx_http_v3_header_t *h; + ngx_http_v3_connection_t *h3c; + ngx_http_v3_dynamic_table_t *dt; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); + h3c = c->qs->parent->data; + dt = &h3c->table; - ref = ngx_http_v3_lookup_table(c, 1, index); - if (ref == NULL) { + if (need > dt->capacity) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "not enough dynamic table capacity"); return NGX_ERROR; } - dt = ngx_http_v3_get_dynamic_table(c); - if (dt == NULL) { - return NGX_ERROR; + target = dt->capacity - need; + n = 0; + + while (dt->size > target) { + h = dt->elts[n++]; + size = ngx_http_v3_table_entry_size(&h->name, &h->value); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 evict [%ui] \"%V\":\"%V\" size:%uz", + dt->base, &h->name, &h->value, size); + + ngx_free(h); + dt->size -= size; + } + + if (n) { + dt->nelts -= n; + dt->base += n; + ngx_memmove(dt->elts, &dt->elts[n], dt->nelts * sizeof(void *)); } - h = ngx_array_push(dt); - if (h == NULL) { + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) +{ + ngx_str_t name, value; + ngx_http_v3_connection_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); + + h3c = c->qs->parent->data; + dt = &h3c->table; + + if (dt->base + dt->nelts <= index) { return NGX_ERROR; } - *h = *ref; + index = dt->base + dt->nelts - 1 - index; - if (ngx_http_v3_new_header(c) != NGX_OK) { + if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) { return NGX_ERROR; } - return NGX_OK; + return ngx_http_v3_insert(c, &name, &value); } @@ -320,94 +420,237 @@ ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) } -static ngx_array_t * -ngx_http_v3_get_dynamic_table(ngx_connection_t *c) +ngx_int_t +ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value) { - ngx_connection_t *pc; - ngx_http_v3_connection_t *h3c; + ngx_uint_t nelts; + ngx_http_v3_header_t *h; - pc = c->qs->parent; - h3c = pc->data; + nelts = sizeof(ngx_http_v3_static_table) + / sizeof(ngx_http_v3_static_table[0]); - if (h3c->dynamic) { - return h3c->dynamic; + if (index >= nelts) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 static[%ui] lookup out of bounds: %ui", + index, nelts); + return NGX_ERROR; } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create dynamic table"); + h = &ngx_http_v3_static_table[index]; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 static[%ui] lookup \"%V\":\"%V\"", + index, &h->name, &h->value); + + if (name) { + *name = h->name; + } - h3c->dynamic = ngx_array_create(pc->pool, 1, sizeof(ngx_http_v3_header_t)); + if (value) { + *value = h->value; + } - return h3c->dynamic; + return NGX_OK; } -ngx_http_v3_header_t * -ngx_http_v3_lookup_table(ngx_connection_t *c, ngx_uint_t dynamic, - ngx_uint_t index) +ngx_int_t +ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, + ngx_str_t *value) { - ngx_uint_t nelts; - ngx_array_t *dt; - ngx_http_v3_header_t *table; + ngx_http_v3_header_t *h; + ngx_http_v3_connection_t *h3c; + ngx_http_v3_dynamic_table_t *dt; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 lookup %s[%ui]", - dynamic ? "dynamic" : "static", index); + h3c = c->qs->parent->data; + dt = &h3c->table; - if (dynamic) { - dt = ngx_http_v3_get_dynamic_table(c); - if (dt == NULL) { - return NULL; - } + if (index < dt->base || index - dt->base >= dt->nelts) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 dynamic[%ui] lookup out of bounds: [%ui,%ui]", + index, dt->base, dt->base + dt->nelts); + return NGX_ERROR; + } - table = dt->elts; - nelts = dt->nelts; + h = dt->elts[index - dt->base]; - } else { - table = ngx_http_v3_static_table; - nelts = sizeof(ngx_http_v3_static_table) - / sizeof(ngx_http_v3_static_table[0]); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 dynamic[%ui] lookup \"%V\":\"%V\"", + index, &h->name, &h->value); + + if (name) { + *name = h->name; } - if (index >= nelts) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 lookup out of bounds: %ui", nelts); - return NULL; + if (value) { + *value = h->value; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) +{ + ngx_uint_t max_entries, full_range, max_value, + max_wrapped, req_insert_count; + ngx_http_v3_srv_conf_t *v3cf; + ngx_http_v3_connection_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + /* QPACK 4.5.1.1. Required Insert Count */ + + if (*insert_count == 0) { + return NGX_OK; } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 lookup \"%V\":\"%V\"", - &table[index].name, &table[index].value); + h3c = c->qs->parent->data; + dt = &h3c->table; + + v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); + + max_entries = v3cf->max_table_capacity / 32; + full_range = 2 * max_entries; + + if (*insert_count > full_range) { + return NGX_ERROR; + } + + max_value = dt->base + dt->nelts + max_entries; + max_wrapped = (max_value / full_range) * full_range; + req_insert_count = max_wrapped + *insert_count - 1; + + if (req_insert_count > max_value) { + if (req_insert_count <= full_range) { + return NGX_ERROR; + } + + req_insert_count -= full_range; + } - return &table[index]; + if (req_insert_count == 0) { + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 decode insert_count %ui -> %ui", + *insert_count, req_insert_count); + + *insert_count = req_insert_count; + + return NGX_OK; } ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) { - size_t n; - ngx_http_v3_connection_t *h3c; + size_t n; + ngx_connection_t *pc; + ngx_pool_cleanup_t *cln; + ngx_http_v3_block_t *block; + ngx_http_v3_srv_conf_t *v3cf; + ngx_http_v3_connection_t *h3c; + ngx_http_v3_dynamic_table_t *dt; - h3c = c->qs->parent->data; - n = h3c->dynamic ? h3c->dynamic->nelts : 0; + pc = c->qs->parent; + h3c = pc->data; + dt = &h3c->table; + + n = dt->base + dt->nelts; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 check insert count %ui/%ui", insert_count, n); + "http3 check insert count req:%ui, have:%ui", + insert_count, n); - if (n < insert_count) { - /* XXX how to get notified? */ - /* XXX wake all streams on any arrival to the encoder stream? */ - return NGX_AGAIN; + if (n >= insert_count) { + return NGX_OK; } - return NGX_OK; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 block stream"); + + block = NULL; + + for (cln = c->pool->cleanup; cln; cln = cln->next) { + if (cln->handler == ngx_http_v3_unblock) { + block = cln->data; + break; + } + } + + if (block == NULL) { + cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_v3_unblock; + + block = cln->data; + block->queue.prev = NULL; + block->connection = c; + block->nblocked = &h3c->nblocked; + } + + if (block->queue.prev == NULL) { + v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, + ngx_http_v3_module); + + if (h3c->nblocked == v3cf->max_blocked_streams) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded http3_max_blocked_streams limit"); + return NGX_ERROR; + } + + h3c->nblocked++; + ngx_queue_insert_tail(&h3c->blocked, &block->queue); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 blocked:%ui", h3c->nblocked); + + return NGX_BUSY; +} + + +static void +ngx_http_v3_unblock(void *data) +{ + ngx_http_v3_block_t *block = data; + + if (block->queue.prev) { + ngx_queue_remove(&block->queue); + block->queue.prev = NULL; + (*block->nblocked)--; + } } static ngx_int_t ngx_http_v3_new_header(ngx_connection_t *c) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 new dynamic header"); + ngx_queue_t *q; + ngx_connection_t *bc; + ngx_http_v3_block_t *block; + ngx_http_v3_connection_t *h3c; + + h3c = c->qs->parent->data; - /* XXX report all waiting streams of a new header */ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 new dynamic header, blocked:%ui", h3c->nblocked); + + while (!ngx_queue_empty(&h3c->blocked)) { + q = ngx_queue_head(&h3c->blocked); + block = (ngx_http_v3_block_t *) q; + bc = block->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, bc->log, 0, "http3 unblock stream"); + + ngx_http_v3_unblock(block); + ngx_post_event(bc->read, &ngx_posted_events); + } return NGX_OK; } -- cgit From 86e287a2de91dfc0c7f6acc4220c15debd40a74d Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 2 Jul 2020 16:33:36 +0300 Subject: HTTP/3: downgraded literal size error level to NGX_LOG_INFO. Now it's similar to HTTP/2. --- src/http/v3/ngx_http_v3_parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index bd91c5b02..b50b21618 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -446,7 +446,7 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, v3cf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); if (n > v3cf->max_field_size) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_field_size limit"); return NGX_ERROR; } -- cgit From fd35d92232ca2332125fe9b29e53aaec33aa2176 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 30 Jun 2020 12:30:57 +0300 Subject: HTTP/3: error code definitions for HTTP/3 and QPACK. --- src/http/v3/ngx_http_v3.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index b51febcdd..d74939fd3 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -51,6 +51,29 @@ #define NGX_HTTP_V3_DEFAULT_MAX_TABLE_CAPACITY 16384 #define NGX_HTTP_V3_DEFAULT_MAX_BLOCKED_STREAMS 16 +/* HTTP/3 errors */ +#define NGX_HTTP_V3_ERR_NO_ERROR 0x100 +#define NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR 0x101 +#define NGX_HTTP_V3_ERR_INTERNAL_ERROR 0x102 +#define NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR 0x103 +#define NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM 0x104 +#define NGX_HTTP_V3_ERR_FRAME_UNEXPECTED 0x105 +#define NGX_HTTP_V3_ERR_FRAME_ERROR 0x106 +#define NGX_HTTP_V3_ERR_EXCESSIVE_LOAD 0x107 +#define NGX_HTTP_V3_ERR_ID_ERROR 0x108 +#define NGX_HTTP_V3_ERR_SETTINGS_ERROR 0x109 +#define NGX_HTTP_V3_ERR_MISSING_SETTINGS 0x10a +#define NGX_HTTP_V3_ERR_REQUEST_REJECTED 0x10b +#define NGX_HTTP_V3_ERR_REQUEST_CANCELLED 0x10c +#define NGX_HTTP_V3_ERR_REQUEST_INCOMPLETE 0x10d +#define NGX_HTTP_V3_ERR_CONNECT_ERROR 0x10f +#define NGX_HTTP_V3_ERR_VERSION_FALLBACK 0x110 + +/* QPACK errors */ +#define NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED 0x200 +#define NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR 0x201 +#define NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR 0x202 + #define ngx_http_v3_get_module_srv_conf(c, module) \ ngx_http_get_module_srv_conf( \ -- cgit From 707117276ed252e39c75769a140cbac6e18eb74a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 2 Jul 2020 16:47:51 +0300 Subject: HTTP/3: close QUIC connection with HTTP/QPACK errors when needed. Previously errors led only to closing streams. To simplify closing QUIC connection from a QUIC stream context, new macro ngx_http_v3_finalize_connection() is introduced. It calls ngx_quic_finalize_connection() for the parent connection. --- src/http/ngx_http_request.c | 12 +- src/http/v3/ngx_http_v3.h | 3 + src/http/v3/ngx_http_v3_parse.c | 233 +++++++++++++++++++++----------------- src/http/v3/ngx_http_v3_parse.h | 10 ++ src/http/v3/ngx_http_v3_request.c | 18 +++ src/http/v3/ngx_http_v3_streams.c | 58 +++++++--- src/http/v3/ngx_http_v3_tables.c | 22 ++-- 7 files changed, 220 insertions(+), 136 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 89e554bf2..c953386d4 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -416,19 +416,21 @@ static void ngx_http_quic_stream_handler(ngx_connection_t *c) { ngx_event_t *rev; - ngx_connection_t *pc; ngx_http_log_ctx_t *ctx; ngx_http_connection_t *hc; ngx_http_v3_connection_t *h3c; - pc = c->qs->parent; - h3c = pc->data; + h3c = c->qs->parent->data; if (!h3c->settings_sent) { h3c->settings_sent = 1; - /* TODO close QUIC connection on error */ - (void) ngx_http_v3_send_settings(c); + if (ngx_http_v3_send_settings(c) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "could not send settings"); + ngx_http_close_connection(c); + return; + } } if (c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index d74939fd3..d4ea19aed 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -80,6 +80,9 @@ ((ngx_http_v3_connection_t *) c->qs->parent->data)->hc.conf_ctx, \ module) +#define ngx_http_v3_finalize_connection(c, code, reason) \ + ngx_quic_finalize_connection(c->qs->parent, code, reason) + typedef struct { ngx_quic_tp_t quic; diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index b50b21618..27484e92a 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -160,7 +160,7 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers"); if (ch != NGX_HTTP_V3_FRAME_HEADERS) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; } st->state = sw_length; @@ -183,21 +183,21 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, case sw_prefix: if (st->length-- == 0) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_FRAME_ERROR; } rc = ngx_http_v3_parse_header_block_prefix(c, &st->prefix, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; + if (rc == NGX_AGAIN) { + break; } if (rc != NGX_DONE) { - break; + return rc; } if (st->length == 0) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_FRAME_ERROR; } st->state = sw_verify; @@ -218,24 +218,25 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, rc = ngx_http_v3_parse_header_rep(c, &st->header_rep, st->prefix.base, ch); + st->length--; - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (--st->length == 0) { - if (rc != NGX_DONE) { - return NGX_ERROR; + if (rc == NGX_AGAIN) { + if (st->length == 0) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; } - goto done; + break; } - if (rc == NGX_DONE) { - return NGX_OK; + if (rc != NGX_DONE) { + return rc; } - break; + if (st->length == 0) { + goto done; + } + + return NGX_OK; } return NGX_AGAIN; @@ -259,6 +260,7 @@ ngx_int_t ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, ngx_http_v3_parse_header_block_prefix_t *st, u_char ch) { + ngx_int_t rc; enum { sw_start = 0, sw_req_insert_count, @@ -308,8 +310,9 @@ ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, done: - if (ngx_http_v3_decode_insert_count(c, &st->insert_count) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_decode_insert_count(c, &st->insert_count); + if (rc != NGX_OK) { + return rc; } if (st->sign) { @@ -404,16 +407,10 @@ ngx_http_v3_parse_header_rep(ngx_connection_t *c, rc = NGX_OK; } - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; + if (rc != NGX_DONE) { + return rc; } - /* rc == NGX_DONE */ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header representation done"); @@ -448,7 +445,7 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, if (n > v3cf->max_field_size) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_field_size limit"); - return NGX_ERROR; + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; } if (st->huffman) { @@ -505,6 +502,7 @@ ngx_int_t ngx_http_v3_parse_header_ri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { + ngx_int_t rc; enum { sw_start = 0, sw_index @@ -543,11 +541,10 @@ done: st->index = st->base - st->index - 1; } - if (ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, - &st->value) - != NGX_OK) - { - return NGX_ERROR; + rc = ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, + &st->value); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -614,15 +611,15 @@ ngx_http_v3_parse_header_lri(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->value = st->literal.value; goto done; } + if (rc != NGX_AGAIN) { + return rc; + } + break; } @@ -639,10 +636,9 @@ done: st->index = st->base - st->index - 1; } - if (ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, NULL) - != NGX_OK) - { - return NGX_ERROR; + rc = ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, NULL); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -693,13 +689,14 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->name = st->literal.value; st->state = sw_value_len; + break; + } + + if (rc != NGX_AGAIN) { + return rc; } break; @@ -729,15 +726,15 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->value = st->literal.value; goto done; } + if (rc != NGX_AGAIN) { + return rc; + } + break; } @@ -758,6 +755,7 @@ ngx_int_t ngx_http_v3_parse_header_pbi(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { + ngx_int_t rc; enum { sw_start = 0, sw_index @@ -790,11 +788,10 @@ done: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header pbi done dynamic[+%ui]", st->index); - if (ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, - &st->value) - != NGX_OK) - { - return NGX_ERROR; + rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, + &st->value); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -861,15 +858,15 @@ ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->value = st->literal.value; goto done; } + if (rc != NGX_AGAIN) { + return rc; + } + break; } @@ -881,10 +878,9 @@ done: "http3 parse header lpbi done dynamic[+%ui] \"%V\"", st->index, &st->value); - if (ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, NULL) - != NGX_OK) - { - return NGX_ERROR; + rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, NULL); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -899,11 +895,15 @@ ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, u_char *p; if (!dynamic) { - return ngx_http_v3_lookup_static(c, index, name, value); + if (ngx_http_v3_lookup_static(c, index, name, value) != NGX_OK) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + return NGX_OK; } if (ngx_http_v3_lookup(c, index, name, value) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } if (name) { @@ -940,6 +940,7 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) ngx_int_t rc; enum { sw_start = 0, + sw_first_type, sw_type, sw_length, sw_settings, @@ -953,10 +954,11 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse control"); - st->state = sw_type; + st->state = sw_first_type; /* fall through */ + case sw_first_type: case sw_type: if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { @@ -968,6 +970,12 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse frame type:%ui", st->type); + if (st->state == sw_first_type + && st->type != NGX_HTTP_V3_FRAME_SETTINGS) + { + return NGX_HTTP_V3_ERR_MISSING_SETTINGS; + } + st->state = sw_length; break; @@ -1008,19 +1016,24 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) rc = ngx_http_v3_parse_settings(c, &st->settings, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } + st->length--; + + if (rc == NGX_AGAIN) { + if (st->length == 0) { + return NGX_HTTP_V3_ERR_SETTINGS_ERROR; + } - if (--st->length > 0) { break; } if (rc != NGX_DONE) { - return NGX_ERROR; + return rc; + } + + if (st->length == 0) { + st->state = sw_type; } - st->state = sw_type; break; case sw_max_push_id: @@ -1085,7 +1098,7 @@ ngx_http_v3_parse_settings(ngx_connection_t *c, } if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_SETTINGS_ERROR; } goto done; @@ -1149,12 +1162,12 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) rc = ngx_http_v3_parse_header_inr(c, &st->header, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; + if (rc == NGX_AGAIN) { + break; } if (rc != NGX_DONE) { - break; + return rc; } goto done; @@ -1163,12 +1176,12 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) rc = ngx_http_v3_parse_header_iwnr(c, &st->header, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; + if (rc == NGX_AGAIN) { + break; } if (rc != NGX_DONE) { - break; + return rc; } goto done; @@ -1179,8 +1192,9 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) break; } - if (ngx_http_v3_set_capacity(c, st->pint.value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_set_capacity(c, st->pint.value); + if (rc != NGX_OK) { + return rc; } goto done; @@ -1191,8 +1205,9 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) break; } - if (ngx_http_v3_duplicate(c, st->pint.value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_duplicate(c, st->pint.value); + if (rc != NGX_OK) { + return rc; } goto done; @@ -1269,15 +1284,15 @@ ngx_http_v3_parse_header_inr(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->value = st->literal.value; goto done; } + if (rc != NGX_AGAIN) { + return rc; + } + break; } @@ -1290,9 +1305,9 @@ done: st->dynamic ? "dynamic" : "static", st->index, &st->value); - if (ngx_http_v3_ref_insert(c, st->dynamic, st->index, &st->value) != NGX_OK) - { - return NGX_ERROR; + rc = ngx_http_v3_ref_insert(c, st->dynamic, st->index, &st->value); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -1344,13 +1359,14 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->name = st->literal.value; st->state = sw_value_len; + break; + } + + if (rc != NGX_AGAIN) { + return rc; } break; @@ -1380,15 +1396,15 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->value = st->literal.value; goto done; } + if (rc != NGX_AGAIN) { + return rc; + } + break; } @@ -1400,8 +1416,9 @@ done: "http3 parse header iwnr done \"%V\":\"%V\"", &st->name, &st->value); - if (ngx_http_v3_insert(c, &st->name, &st->value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_insert(c, &st->name, &st->value); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -1414,6 +1431,7 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) { ngx_http_v3_parse_decoder_t *st = data; + ngx_int_t rc; enum { sw_start = 0, sw_ack_header, @@ -1451,8 +1469,9 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) break; } - if (ngx_http_v3_ack_header(c, st->pint.value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_ack_header(c, st->pint.value); + if (rc != NGX_OK) { + return rc; } goto done; @@ -1463,8 +1482,9 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) break; } - if (ngx_http_v3_cancel_stream(c, st->pint.value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_cancel_stream(c, st->pint.value); + if (rc != NGX_OK) { + return rc; } goto done; @@ -1475,8 +1495,9 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) break; } - if (ngx_http_v3_inc_insert_count(c, st->pint.value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_inc_insert_count(c, st->pint.value); + if (rc != NGX_OK) { + return rc; } goto done; @@ -1521,7 +1542,7 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, } if (st->vlint.value != NGX_HTTP_V3_FRAME_DATA) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; } st->state = sw_length; diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h index 17ff6c777..0c0af33b7 100644 --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -112,6 +112,16 @@ typedef struct { } ngx_http_v3_parse_data_t; +/* + * Parse functions return codes: + * NGX_DONE - parsing done + * NGX_OK - sub-element done + * NGX_AGAIN - more data expected + * NGX_BUSY - waiting for external event + * NGX_ERROR - internal error + * NGX_HTTP_V3_ERROR_XXX - HTTP/3 or QPACK error + */ + ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, ngx_http_v3_parse_varlen_int_t *st, u_char ch); ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index ae65ba9ea..0ffa8927d 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -66,6 +66,12 @@ ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) while (b->pos < b->last) { rc = ngx_http_v3_parse_headers(c, st, *b->pos); + if (rc > 0) { + ngx_http_v3_finalize_connection(c, rc, + "could not parse request headers"); + goto failed; + } + if (rc == NGX_ERROR) { goto failed; } @@ -180,6 +186,12 @@ ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, while (b->pos < b->last) { rc = ngx_http_v3_parse_headers(c, st, *b->pos++); + if (rc > 0) { + ngx_http_v3_finalize_connection(c, rc, + "could not parse request headers"); + return NGX_HTTP_PARSE_INVALID_HEADER; + } + if (rc == NGX_ERROR) { return NGX_HTTP_PARSE_INVALID_HEADER; } @@ -359,6 +371,12 @@ ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, while (b->pos < b->last) { rc = ngx_http_v3_parse_data(c, st, *b->pos++); + if (rc > 0) { + ngx_http_v3_finalize_connection(c, rc, + "could not parse request body"); + goto failed; + } + if (rc == NGX_ERROR) { goto failed; } diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 229ce5bf4..8eaa7fde6 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -39,6 +39,8 @@ ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + NULL); ngx_http_v3_close_uni_stream(c); return; } @@ -85,7 +87,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) { u_char ch; ssize_t n; - ngx_int_t index; + ngx_int_t index, rc; ngx_connection_t *c; ngx_http_v3_connection_t *h3c; ngx_http_v3_uni_stream_t *us; @@ -100,12 +102,18 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) n = c->recv(c, &ch, 1); - if (n == NGX_ERROR) { + if (n == NGX_AGAIN) { + break; + } + + if (n == 0) { + rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; goto failed; } - if (n == NGX_AGAIN || n != 1) { - break; + if (n != 1) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; + goto failed; } switch (ch) { @@ -154,6 +162,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) if (index >= 0) { if (h3c->known_streams[index]) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); + rc = NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; goto failed; } @@ -164,6 +173,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) if (n) { us->data = ngx_pcalloc(c->pool, n); if (us->data == NULL) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; goto failed; } } @@ -174,6 +184,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) } if (ngx_handle_read_event(rev, 0) != NGX_OK) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; goto failed; } @@ -181,6 +192,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) failed: + ngx_http_v3_finalize_connection(c, rc, "could not read stream type"); ngx_http_v3_close_uni_stream(c); } @@ -203,10 +215,22 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) n = c->recv(c, buf, sizeof(buf)); - if (n == NGX_ERROR || n == 0) { + if (n == NGX_ERROR) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; goto failed; } + if (n == 0) { + if (us->index >= 0) { + rc = NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM; + goto failed; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read eof"); + ngx_http_v3_close_uni_stream(c); + return; + } + if (n == NGX_AGAIN) { break; } @@ -219,30 +243,34 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) rc = us->handler(c, us->data, buf[i]); - if (rc == NGX_ERROR) { - goto failed; + if (rc == NGX_DONE) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 read done"); + ngx_http_v3_close_uni_stream(c); + return; } - if (rc == NGX_DONE) { - goto done; + if (rc > 0) { + goto failed; } - /* rc == NGX_AGAIN */ + if (rc != NGX_AGAIN) { + rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; + goto failed; + } } } if (ngx_handle_read_event(rev, 0) != NGX_OK) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; goto failed; } return; -done: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read done"); - failed: + ngx_http_v3_finalize_connection(c, rc, "stream error"); ngx_http_v3_close_uni_stream(c); } @@ -257,6 +285,8 @@ ngx_http_v3_dummy_write_handler(ngx_event_t *wev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy write handler"); if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + NULL); ngx_http_v3_close_uni_stream(c); } } diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index a57eccb9f..446601b01 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -167,7 +167,7 @@ ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, "http3 ref insert dynamic[%ui] \"%V\"", index, value); if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } } else { @@ -175,7 +175,7 @@ ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, "http3 ref insert static[%ui] \"%V\"", index, value); if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } } @@ -195,7 +195,7 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) size = ngx_http_v3_table_entry_size(name, value); if (ngx_http_v3_evict(c, size) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } h3c = c->qs->parent->data; @@ -257,14 +257,14 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) if (capacity > v3cf->max_table_capacity) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_table_capacity limit"); - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } dt = &h3c->table; if (dt->size > capacity) { if (ngx_http_v3_evict(c, dt->size - capacity) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } } @@ -371,13 +371,13 @@ ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) dt = &h3c->table; if (dt->base + dt->nelts <= index) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } index = dt->base + dt->nelts - 1 - index; if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } return ngx_http_v3_insert(c, &name, &value); @@ -515,7 +515,7 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) full_range = 2 * max_entries; if (*insert_count > full_range) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } max_value = dt->base + dt->nelts + max_entries; @@ -524,14 +524,14 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) if (req_insert_count > max_value) { if (req_insert_count <= full_range) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } req_insert_count -= full_range; } if (req_insert_count == 0) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -601,7 +601,7 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) if (h3c->nblocked == v3cf->max_blocked_streams) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_blocked_streams limit"); - return NGX_ERROR; + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } h3c->nblocked++; -- cgit From d839fee75f5247c160c0b7b927dc45a3122cb6a2 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 30 Jun 2020 15:32:09 +0300 Subject: HTTP/3: set r->headers_in.chunked flag after parsing headers. Previously it was set when creating the request object. The side-effect was trying to discard the request body in case of header parse error. --- src/http/ngx_http_request.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index c953386d4..8933bb3c3 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -727,7 +727,6 @@ ngx_http_alloc_request(ngx_connection_t *c) #if (NGX_HTTP_V3) if (hc->quic) { r->http_version = NGX_HTTP_VERSION_30; - r->headers_in.chunked = 1; } #endif @@ -2155,6 +2154,12 @@ ngx_http_process_request_header(ngx_http_request_t *r) } } +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + r->headers_in.chunked = 1; + } +#endif + if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) { if (r->headers_in.keep_alive) { r->headers_in.keep_alive_n = -- cgit From 0ebcffcf1409a03d2437ad18d65387448382620d Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 2 Jul 2020 17:35:57 +0300 Subject: HTTP/3: fixed prefix in decoding Section Acknowledgement. --- src/http/v3/ngx_http_v3_parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 27484e92a..da9826ced 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -1465,7 +1465,7 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) case sw_ack_header: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch) != NGX_DONE) { + if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { break; } -- cgit From 3b2eabde0bedc9a1a7d1b53bdbc28bdc14773dd1 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 3 Jul 2020 16:41:31 +0300 Subject: HTTP/3: fixed overflow in prefixed integer parser. Previously, the expression (ch & 0x7f) was promoted to a signed integer. Depending on the platform, the size of this integer could be less than 8 bytes, leading to overflow when handling the higher bits of the result. Also, sign bit of this integer could be replicated when adding to the 64-bit st->value. --- src/http/v3/ngx_http_v3_parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index da9826ced..bb8d73296 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -118,7 +118,7 @@ ngx_http_v3_parse_prefix_int(ngx_connection_t *c, case sw_value: - st->value += (ch & 0x7f) << st->shift; + st->value += (uint64_t) (ch & 0x7f) << st->shift; if (ch & 0x80) { st->shift += 7; break; -- cgit From 3a9874c4ab00d443c66e175502466b0105e8f3be Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 3 Jul 2020 09:26:12 +0300 Subject: HTTP/3: limited prefixed integer size by 62 bits. --- src/http/v3/ngx_http_v3_parse.c | 104 +++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 38 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index bb8d73296..8b1f745fe 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -119,6 +119,15 @@ ngx_http_v3_parse_prefix_int(ngx_connection_t *c, case sw_value: st->value += (uint64_t) (ch & 0x7f) << st->shift; + + if (st->shift == 56 + && ((ch & 0x80) || (st->value & 0xc000000000000000))) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded integer size limit"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + if (ch & 0x80) { st->shift += 7; break; @@ -281,8 +290,9 @@ ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, case sw_req_insert_count: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 8, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 8, ch); + if (rc != NGX_DONE) { + return rc; } st->insert_count = st->pint.value; @@ -298,8 +308,9 @@ ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, case sw_read_delta_base: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); + if (rc != NGX_DONE) { + return rc; } st->delta_base = st->pint.value; @@ -521,8 +532,9 @@ ngx_http_v3_parse_header_ri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, case sw_index: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch); + if (rc != NGX_DONE) { + return rc; } st->index = st->pint.value; @@ -578,8 +590,9 @@ ngx_http_v3_parse_header_lri(ngx_connection_t *c, case sw_index: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 4, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, ch); + if (rc != NGX_DONE) { + return rc; } st->index = st->pint.value; @@ -595,8 +608,9 @@ ngx_http_v3_parse_header_lri(ngx_connection_t *c, case sw_read_value_len: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); + if (rc != NGX_DONE) { + return rc; } st->literal.length = st->pint.value; @@ -673,8 +687,9 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, case sw_name_len: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 3, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, ch); + if (rc != NGX_DONE) { + return rc; } st->literal.length = st->pint.value; @@ -710,8 +725,9 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, case sw_read_value_len: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); + if (rc != NGX_DONE) { + return rc; } st->literal.length = st->pint.value; @@ -773,8 +789,9 @@ ngx_http_v3_parse_header_pbi(ngx_connection_t *c, case sw_index: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 4, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, ch); + if (rc != NGX_DONE) { + return rc; } st->index = st->pint.value; @@ -825,8 +842,9 @@ ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, case sw_index: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 3, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, ch); + if (rc != NGX_DONE) { + return rc; } st->index = st->pint.value; @@ -842,8 +860,9 @@ ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, case sw_read_value_len: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); + if (rc != NGX_DONE) { + return rc; } st->literal.length = st->pint.value; @@ -1188,8 +1207,9 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) case sw_capacity: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch); + if (rc != NGX_DONE) { + return rc; } rc = ngx_http_v3_set_capacity(c, st->pint.value); @@ -1201,8 +1221,9 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) case sw_duplicate: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch); + if (rc != NGX_DONE) { + return rc; } rc = ngx_http_v3_duplicate(c, st->pint.value); @@ -1251,8 +1272,9 @@ ngx_http_v3_parse_header_inr(ngx_connection_t *c, case sw_name_index: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch); + if (rc != NGX_DONE) { + return rc; } st->index = st->pint.value; @@ -1268,8 +1290,9 @@ ngx_http_v3_parse_header_inr(ngx_connection_t *c, case sw_read_value_len: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); + if (rc != NGX_DONE) { + return rc; } st->literal.length = st->pint.value; @@ -1343,8 +1366,9 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, case sw_name_len: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch); + if (rc != NGX_DONE) { + return rc; } st->literal.length = st->pint.value; @@ -1380,8 +1404,9 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, case sw_read_value_len: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); + if (rc != NGX_DONE) { + return rc; } st->literal.length = st->pint.value; @@ -1465,8 +1490,9 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) case sw_ack_header: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); + if (rc != NGX_DONE) { + return rc; } rc = ngx_http_v3_ack_header(c, st->pint.value); @@ -1478,8 +1504,9 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) case sw_cancel_stream: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch); + if (rc != NGX_DONE) { + return rc; } rc = ngx_http_v3_cancel_stream(c, st->pint.value); @@ -1491,8 +1518,9 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) case sw_inc_insert_count: - if (ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch); + if (rc != NGX_DONE) { + return rc; } rc = ngx_http_v3_inc_insert_count(c, st->pint.value); -- cgit From 8d1875d39ab60a6494a1d3b4be34fc35213d74df Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 3 Jul 2020 12:05:05 +0300 Subject: HTTP/3: simplifed handling ngx_http_v3_parse_literal() return code. --- src/http/v3/ngx_http_v3_parse.c | 77 +++++++++++------------------------------ 1 file changed, 21 insertions(+), 56 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 8b1f745fe..4e0532781 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -624,17 +624,12 @@ ngx_http_v3_parse_header_lri(ngx_connection_t *c, case sw_value: rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - - if (rc == NGX_DONE) { - st->value = st->literal.value; - goto done; - } - - if (rc != NGX_AGAIN) { + if (rc != NGX_DONE) { return rc; } - break; + st->value = st->literal.value; + goto done; } return NGX_AGAIN; @@ -703,17 +698,12 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, case sw_name: rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - - if (rc == NGX_DONE) { - st->name = st->literal.value; - st->state = sw_value_len; - break; - } - - if (rc != NGX_AGAIN) { + if (rc != NGX_DONE) { return rc; } + st->name = st->literal.value; + st->state = sw_value_len; break; case sw_value_len: @@ -741,17 +731,12 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, case sw_value: rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - - if (rc == NGX_DONE) { - st->value = st->literal.value; - goto done; - } - - if (rc != NGX_AGAIN) { + if (rc != NGX_DONE) { return rc; } - break; + st->value = st->literal.value; + goto done; } return NGX_AGAIN; @@ -876,17 +861,12 @@ ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, case sw_value: rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - - if (rc == NGX_DONE) { - st->value = st->literal.value; - goto done; - } - - if (rc != NGX_AGAIN) { + if (rc != NGX_DONE) { return rc; } - break; + st->value = st->literal.value; + goto done; } return NGX_AGAIN; @@ -1306,17 +1286,12 @@ ngx_http_v3_parse_header_inr(ngx_connection_t *c, case sw_value: rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - - if (rc == NGX_DONE) { - st->value = st->literal.value; - goto done; - } - - if (rc != NGX_AGAIN) { + if (rc != NGX_DONE) { return rc; } - break; + st->value = st->literal.value; + goto done; } return NGX_AGAIN; @@ -1382,17 +1357,12 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, case sw_name: rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - - if (rc == NGX_DONE) { - st->name = st->literal.value; - st->state = sw_value_len; - break; - } - - if (rc != NGX_AGAIN) { + if (rc != NGX_DONE) { return rc; } + st->name = st->literal.value; + st->state = sw_value_len; break; case sw_value_len: @@ -1420,17 +1390,12 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, case sw_value: rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - - if (rc == NGX_DONE) { - st->value = st->literal.value; - goto done; - } - - if (rc != NGX_AGAIN) { + if (rc != NGX_DONE) { return rc; } - break; + st->value = st->literal.value; + goto done; } return NGX_AGAIN; -- cgit From ac9c1dcad8becffd188321600ff4edd2d49dcca6 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 3 Jul 2020 12:07:43 +0300 Subject: HTTP/3: put ngx_http_v3_parse_varlen_int() return code in variable. This makes calling this function similar to other parse functions. --- src/http/v3/ngx_http_v3_parse.c | 42 +++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 4e0532781..e1e9444e1 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -177,8 +177,9 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, case sw_length: - if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + if (rc != NGX_DONE) { + return rc; } st->length = st->vlint.value; @@ -960,8 +961,9 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) case sw_first_type: case sw_type: - if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + if (rc != NGX_DONE) { + return rc; } st->type = st->vlint.value; @@ -980,8 +982,9 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) case sw_length: - if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + if (rc != NGX_DONE) { + return rc; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -1037,8 +1040,9 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) case sw_max_push_id: - if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + if (rc != NGX_DONE) { + return rc; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -1064,6 +1068,7 @@ ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, ngx_http_v3_parse_settings_t *st, u_char ch) { + ngx_int_t rc; enum { sw_start = 0, sw_id, @@ -1082,8 +1087,9 @@ ngx_http_v3_parse_settings(ngx_connection_t *c, case sw_id: - if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + if (rc != NGX_DONE) { + return rc; } st->id = st->vlint.value; @@ -1092,8 +1098,9 @@ ngx_http_v3_parse_settings(ngx_connection_t *c, case sw_value: - if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + if (rc != NGX_DONE) { + return rc; } if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) { @@ -1512,6 +1519,7 @@ ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, u_char ch) { + ngx_int_t rc; enum { sw_start = 0, sw_type, @@ -1530,8 +1538,9 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, case sw_type: - if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + if (rc != NGX_DONE) { + return rc; } if (st->vlint.value != NGX_HTTP_V3_FRAME_DATA) { @@ -1543,8 +1552,9 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, case sw_length: - if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + if (rc != NGX_DONE) { + return rc; } st->length = st->vlint.value; -- cgit From 3db00b4da74ddc0872be4cdb836aaff7fc21272a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 2 Jul 2020 20:07:24 +0300 Subject: HTTP/3: simplified handling return codes from parse functions. --- src/http/v3/ngx_http_v3_parse.c | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index e1e9444e1..bb52c9e94 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -197,11 +197,6 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, } rc = ngx_http_v3_parse_header_block_prefix(c, &st->prefix, ch); - - if (rc == NGX_AGAIN) { - break; - } - if (rc != NGX_DONE) { return rc; } @@ -228,14 +223,9 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, rc = ngx_http_v3_parse_header_rep(c, &st->header_rep, st->prefix.base, ch); - st->length--; - - if (rc == NGX_AGAIN) { - if (st->length == 0) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } - break; + if (--st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; } if (rc != NGX_DONE) { @@ -1018,14 +1008,8 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) rc = ngx_http_v3_parse_settings(c, &st->settings, ch); - st->length--; - - if (rc == NGX_AGAIN) { - if (st->length == 0) { - return NGX_HTTP_V3_ERR_SETTINGS_ERROR; - } - - break; + if (--st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_SETTINGS_ERROR; } if (rc != NGX_DONE) { @@ -1167,11 +1151,6 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) case sw_inr: rc = ngx_http_v3_parse_header_inr(c, &st->header, ch); - - if (rc == NGX_AGAIN) { - break; - } - if (rc != NGX_DONE) { return rc; } @@ -1181,11 +1160,6 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) case sw_iwnr: rc = ngx_http_v3_parse_header_iwnr(c, &st->header, ch); - - if (rc == NGX_AGAIN) { - break; - } - if (rc != NGX_DONE) { return rc; } -- cgit From 8d2d2c5f8fd579200d4d1f2abd7fa4af3ca258e6 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 20 Jul 2020 15:19:03 +0300 Subject: Fixed format specifiers. --- src/http/v3/ngx_http_v3_parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index bb52c9e94..f9e26a4f5 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -325,7 +325,7 @@ done: ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header block prefix done " - "insert_count:%ui, sign:%ui, delta_base:%ui, base:%uL", + "insert_count:%ui, sign:%ui, delta_base:%ui, base:%ui", st->insert_count, st->sign, st->delta_base, st->base); st->state = sw_start; -- cgit From b813b9ec358862a2a94868bc057420d6eca5c05d Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 21 Jul 2020 23:09:22 +0300 Subject: QUIC: added "quic" listen parameter. The parameter allows processing HTTP/0.9-2 over QUIC. Also, introduced ngx_http_quic_module and moved QUIC settings there --- src/http/modules/ngx_http_quic_module.c | 344 ++++++++++++++++++++++++++++++++ src/http/modules/ngx_http_quic_module.h | 25 +++ src/http/modules/ngx_http_ssl_module.c | 33 ++- src/http/ngx_http.c | 35 +++- src/http/ngx_http.h | 3 + src/http/ngx_http_core_module.c | 30 +++ src/http/ngx_http_core_module.h | 2 + src/http/ngx_http_request.c | 149 ++++---------- src/http/ngx_http_request.h | 1 - src/http/v3/ngx_http_v3.h | 5 +- src/http/v3/ngx_http_v3_module.c | 243 ---------------------- src/http/v3/ngx_http_v3_streams.c | 49 ++++- 12 files changed, 540 insertions(+), 379 deletions(-) create mode 100644 src/http/modules/ngx_http_quic_module.c create mode 100644 src/http/modules/ngx_http_quic_module.h (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c new file mode 100644 index 000000000..d971c8d26 --- /dev/null +++ b/src/http/modules/ngx_http_quic_module.c @@ -0,0 +1,344 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Roman Arutyunyan + */ + + +#include +#include +#include + + +static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_quic_add_variables(ngx_conf_t *cf); +static void *ngx_http_quic_create_srv_conf(ngx_conf_t *cf); +static char *ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, + void *data); +static char *ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, + void *data); + + +static ngx_conf_post_t ngx_http_quic_max_ack_delay_post = + { ngx_http_quic_max_ack_delay }; +static ngx_conf_post_t ngx_http_quic_max_udp_payload_size_post = + { ngx_http_quic_max_udp_payload_size }; +static ngx_conf_num_bounds_t ngx_http_quic_ack_delay_exponent_bounds = + { ngx_conf_check_num_bounds, 0, 20 }; +static ngx_conf_num_bounds_t ngx_http_quic_active_connection_id_limit_bounds = + { ngx_conf_check_num_bounds, 2, -1 }; + + +static ngx_command_t ngx_http_quic_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_quic_conf_t, tp.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_quic_conf_t, tp.max_ack_delay), + &ngx_http_quic_max_ack_delay_post }, + + { ngx_string("quic_max_udp_payload_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_quic_conf_t, tp.max_udp_payload_size), + &ngx_http_quic_max_udp_payload_size_post }, + + { ngx_string("quic_initial_max_data"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_quic_conf_t, tp.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_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_quic_conf_t, tp.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_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_quic_conf_t, tp.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_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_quic_conf_t, tp.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_quic_conf_t, tp.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_quic_conf_t, tp.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_quic_conf_t, tp.ack_delay_exponent), + &ngx_http_quic_ack_delay_exponent_bounds }, + + { 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_quic_conf_t, tp.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_quic_conf_t, tp.active_connection_id_limit), + &ngx_http_quic_active_connection_id_limit_bounds }, + + { ngx_string("quic_retry"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_quic_conf_t, retry), + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_quic_module_ctx = { + ngx_http_quic_add_variables, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_http_quic_create_srv_conf, /* create server configuration */ + ngx_http_quic_merge_srv_conf, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_quic_module = { + NGX_MODULE_V1, + &ngx_http_quic_module_ctx, /* module context */ + ngx_http_quic_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_variable_t ngx_http_quic_vars[] = { + + { ngx_string("quic"), NULL, ngx_http_variable_quic, 0, 0, 0 }, + + ngx_http_null_variable +}; + + +static ngx_int_t +ngx_http_variable_quic(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->connection->qs) { + + v->len = 4; + v->valid = 1; + v->no_cacheable = 1; + v->not_found = 0; + v->data = (u_char *) "quic"; + return NGX_OK; + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_quic_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_quic_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static void * +ngx_http_quic_create_srv_conf(ngx_conf_t *cf) +{ + ngx_quic_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_quic_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->tp.original_dcid = { 0, NULL }; + * conf->tp.initial_scid = { 0, NULL }; + * conf->tp.retry_scid = { 0, NULL }; + * conf->tp.stateless_reset_token = { 0 } + * conf->tp.preferred_address = NULL + */ + + conf->tp.max_idle_timeout = NGX_CONF_UNSET_MSEC; + conf->tp.max_ack_delay = NGX_CONF_UNSET_MSEC; + conf->tp.max_udp_payload_size = NGX_CONF_UNSET_SIZE; + conf->tp.initial_max_data = NGX_CONF_UNSET_SIZE; + conf->tp.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_SIZE; + conf->tp.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_SIZE; + conf->tp.initial_max_stream_data_uni = NGX_CONF_UNSET_SIZE; + conf->tp.initial_max_streams_bidi = NGX_CONF_UNSET_UINT; + conf->tp.initial_max_streams_uni = NGX_CONF_UNSET_UINT; + conf->tp.ack_delay_exponent = NGX_CONF_UNSET_UINT; + conf->tp.disable_active_migration = NGX_CONF_UNSET_UINT; + conf->tp.active_connection_id_limit = NGX_CONF_UNSET_UINT; + + conf->retry = NGX_CONF_UNSET; + + return conf; +} + + +static char * +ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_quic_conf_t *prev = parent; + ngx_quic_conf_t *conf = child; + + ngx_conf_merge_msec_value(conf->tp.max_idle_timeout, + prev->tp.max_idle_timeout, 60000); + + ngx_conf_merge_msec_value(conf->tp.max_ack_delay, + prev->tp.max_ack_delay, + NGX_QUIC_DEFAULT_MAX_ACK_DELAY); + + ngx_conf_merge_size_value(conf->tp.max_udp_payload_size, + prev->tp.max_udp_payload_size, + NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); + + ngx_conf_merge_size_value(conf->tp.initial_max_data, + prev->tp.initial_max_data, + 16 * NGX_QUIC_STREAM_BUFSIZE); + + ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_bidi_local, + prev->tp.initial_max_stream_data_bidi_local, + NGX_QUIC_STREAM_BUFSIZE); + + ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_bidi_remote, + prev->tp.initial_max_stream_data_bidi_remote, + NGX_QUIC_STREAM_BUFSIZE); + + ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_uni, + prev->tp.initial_max_stream_data_uni, + NGX_QUIC_STREAM_BUFSIZE); + + ngx_conf_merge_uint_value(conf->tp.initial_max_streams_bidi, + prev->tp.initial_max_streams_bidi, 16); + + ngx_conf_merge_uint_value(conf->tp.initial_max_streams_uni, + prev->tp.initial_max_streams_uni, 16); + + ngx_conf_merge_uint_value(conf->tp.ack_delay_exponent, + prev->tp.ack_delay_exponent, + NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); + + ngx_conf_merge_uint_value(conf->tp.disable_active_migration, + prev->tp.disable_active_migration, 1); + + ngx_conf_merge_uint_value(conf->tp.active_connection_id_limit, + prev->tp.active_connection_id_limit, 2); + + ngx_conf_merge_value(conf->retry, prev->retry, 0); + + if (conf->retry) { + if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) { + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, void *data) +{ + ngx_msec_t *sp = data; + + if (*sp > 16384) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"quic_max_ack_delay\" must be less than 16384"); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp < NGX_QUIC_MIN_INITIAL_SIZE + || *sp > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"quic_max_udp_payload_size\" must be between " + "%d and %d", + NGX_QUIC_MIN_INITIAL_SIZE, + NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} diff --git a/src/http/modules/ngx_http_quic_module.h b/src/http/modules/ngx_http_quic_module.h new file mode 100644 index 000000000..e744eb197 --- /dev/null +++ b/src/http/modules/ngx_http_quic_module.h @@ -0,0 +1,25 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Roman Arutyunyan + */ + + +#ifndef _NGX_HTTP_QUIC_H_INCLUDED_ +#define _NGX_HTTP_QUIC_H_INCLUDED_ + + +#include +#include +#include + + +#define NGX_HTTP_QUIC_ALPN(s) NGX_HTTP_QUIC_ALPN_DRAFT(s) +#define NGX_HTTP_QUIC_ALPN_DRAFT(s) "\x05hq-" #s +#define NGX_HTTP_QUIC_ALPN_ADVERTISE NGX_HTTP_QUIC_ALPN(NGX_QUIC_DRAFT_VERSION) + + +extern ngx_module_t ngx_http_quic_module; + + +#endif /* _NGX_HTTP_QUIC_H_INCLUDED_ */ diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 7daa4daf2..409514821 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -402,7 +402,7 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, #if (NGX_DEBUG) unsigned int i; #endif -#if (NGX_HTTP_V2 || NGX_HTTP_V3) +#if (NGX_HTTP_V2 || NGX_HTTP_QUIC) ngx_http_connection_t *hc; #endif #if (NGX_HTTP_V2 || NGX_DEBUG) @@ -419,7 +419,7 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, } #endif -#if (NGX_HTTP_V2 || NGX_HTTP_V3) +#if (NGX_HTTP_V2 || NGX_HTTP_QUIC) hc = c->data; #endif @@ -436,6 +436,12 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, srv = (unsigned char *) NGX_HTTP_V3_ALPN_ADVERTISE; srvlen = sizeof(NGX_HTTP_V3_ALPN_ADVERTISE) - 1; } else +#endif +#if (NGX_HTTP_QUIC) + if (hc->addr_conf->quic) { + srv = (unsigned char *) NGX_HTTP_QUIC_ALPN_ADVERTISE; + srvlen = sizeof(NGX_HTTP_QUIC_ALPN_ADVERTISE) - 1; + } else #endif { srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE; @@ -1247,6 +1253,7 @@ static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf) { ngx_uint_t a, p, s; + const char *name; ngx_http_conf_addr_t *addr; ngx_http_conf_port_t *port; ngx_http_ssl_srv_conf_t *sscf; @@ -1296,26 +1303,36 @@ ngx_http_ssl_init(ngx_conf_t *cf) addr = port[p].addrs.elts; for (a = 0; a < port[p].addrs.nelts; a++) { - if (!addr[a].opt.ssl && !addr[a].opt.http3) { + if (!addr[a].opt.ssl && !addr[a].opt.quic) { continue; } + if (addr[a].opt.http3) { + name = "http3"; + + } else if (addr[a].opt.quic) { + name = "quic"; + + } else { + name = "ssl"; + } + cscf = addr[a].default_server; sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; if (sscf->certificates == NULL) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "no \"ssl_certificate\" is defined for " - "the \"listen ... ssl\" directive in %s:%ui", - cscf->file_name, cscf->line); + "the \"listen ... %s\" directive in %s:%ui", + name, cscf->file_name, cscf->line); return NGX_ERROR; } - if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) { + if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "\"ssl_protocols\" did not enable TLSv1.3 for " - "the \"listen ... http3\" directive in %s:%ui", - cscf->file_name, cscf->line); + "the \"listen ... %s\" directives in %s:%ui", + name, cscf->file_name, cscf->line); return NGX_ERROR; } } diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 7da64f906..b075f30be 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1200,10 +1200,13 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #if (NGX_HTTP_SSL) ngx_uint_t ssl; #endif +#if (NGX_HTTP_QUIC) + ngx_uint_t quic; +#endif #if (NGX_HTTP_V2) ngx_uint_t http2; #endif -#if (NGX_HTTP_SSL) +#if (NGX_HTTP_V3) ngx_uint_t http3; #endif @@ -1238,10 +1241,13 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #if (NGX_HTTP_SSL) ssl = lsopt->ssl || addr[i].opt.ssl; #endif +#if (NGX_HTTP_QUIC) + quic = lsopt->quic || addr[i].opt.quic; +#endif #if (NGX_HTTP_V2) http2 = lsopt->http2 || addr[i].opt.http2; #endif -#if (NGX_HTTP_SSL) +#if (NGX_HTTP_V3) http3 = lsopt->http3 || addr[i].opt.http3; #endif @@ -1277,10 +1283,13 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #if (NGX_HTTP_SSL) addr[i].opt.ssl = ssl; #endif +#if (NGX_HTTP_QUIC) + addr[i].opt.quic = quic; +#endif #if (NGX_HTTP_V2) addr[i].opt.http2 = http2; #endif -#if (NGX_HTTP_SSL) +#if (NGX_HTTP_V3) addr[i].opt.http3 = http3; #endif @@ -1326,12 +1335,12 @@ ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #endif -#if (NGX_HTTP_SSL && !defined NGX_OPENSSL_QUIC) +#if (NGX_HTTP_QUIC && !defined NGX_OPENSSL_QUIC) - if (lsopt->http3) { + if (lsopt->quic) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "nginx was built with OpenSSL that lacks QUIC " - "support, HTTP/3 is not enabled for %V", + "support, QUIC is not enabled for %V", &lsopt->addr_text); } @@ -1797,8 +1806,8 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) ls->wildcard = addr->opt.wildcard; -#if (NGX_HTTP_SSL) - ls->quic = addr->opt.http3; +#if (NGX_HTTP_QUIC) + ls->quic = addr->opt.quic; #endif return ls; @@ -1830,10 +1839,13 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, #if (NGX_HTTP_SSL) addrs[i].conf.ssl = addr[i].opt.ssl; #endif +#if (NGX_HTTP_QUIC) + addrs[i].conf.quic = addr[i].opt.quic; +#endif #if (NGX_HTTP_V2) addrs[i].conf.http2 = addr[i].opt.http2; #endif -#if (NGX_HTTP_SSL) +#if (NGX_HTTP_V3) addrs[i].conf.http3 = addr[i].opt.http3; #endif addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; @@ -1898,10 +1910,13 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, #if (NGX_HTTP_SSL) addrs6[i].conf.ssl = addr[i].opt.ssl; #endif +#if (NGX_HTTP_QUIC) + addrs6[i].conf.quic = addr[i].opt.quic; +#endif #if (NGX_HTTP_V2) addrs6[i].conf.http2 = addr[i].opt.http2; #endif -#if (NGX_HTTP_SSL) +#if (NGX_HTTP_V3) addrs6[i].conf.http3 = addr[i].opt.http3; #endif addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index a0946c95a..480802d40 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -50,6 +50,9 @@ typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r, #if (NGX_HTTP_SSL) #include #endif +#if (NGX_HTTP_QUIC) +#include +#endif struct ngx_http_log_ctx_s { diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 9bb89ee37..ef8b649ef 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -4079,8 +4079,22 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #endif } + if (ngx_strcmp(value[n].data, "quic") == 0) { +#if (NGX_HTTP_QUIC) + lsopt.quic = 1; + lsopt.type = SOCK_DGRAM; + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"quic\" parameter requires " + "ngx_http_quic_module"); + return NGX_CONF_ERROR; +#endif + } + if (ngx_strcmp(value[n].data, "http3") == 0) { #if (NGX_HTTP_V3) + lsopt.quic = 1; lsopt.http3 = 1; lsopt.type = SOCK_DGRAM; continue; @@ -4201,6 +4215,22 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } +#if (NGX_HTTP_SSL) + +#if (NGX_HTTP_V3) + if (lsopt.ssl && lsopt.http3) { + return "\"ssl\" parameter is incompatible with \"http3\""; + } +#endif + +#if (NGX_HTTP_QUIC) + if (lsopt.ssl && lsopt.quic) { + return "\"ssl\" parameter is incompatible with \"quic\""; + } +#endif + +#endif + for (n = 0; n < u.naddrs; n++) { lsopt.sockaddr = u.addrs[n].sockaddr; lsopt.socklen = u.addrs[n].socklen; diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index e7c117c9e..02e2a9e59 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -74,6 +74,7 @@ typedef struct { unsigned bind:1; unsigned wildcard:1; unsigned ssl:1; + unsigned quic:1; unsigned http2:1; unsigned http3:1; #if (NGX_HAVE_INET6) @@ -238,6 +239,7 @@ struct ngx_http_addr_conf_s { ngx_http_virtual_names_t *virtual_names; unsigned ssl:1; + unsigned quic:1; unsigned http2:1; unsigned http3:1; unsigned proxy_protocol:1; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 8933bb3c3..bfa8e11c5 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -64,9 +64,6 @@ static void ngx_http_ssl_handshake(ngx_event_t *rev); static void ngx_http_ssl_handshake_handler(ngx_connection_t *c); #endif -#if (NGX_HTTP_V3) -static void ngx_http_quic_stream_handler(ngx_connection_t *c); -#endif static char *ngx_http_client_errors[] = { @@ -221,26 +218,7 @@ ngx_http_init_connection(ngx_connection_t *c) ngx_http_in6_addr_t *addr6; #endif -#if (NGX_HTTP_V3) - if (c->type == SOCK_DGRAM) { - ngx_http_v3_connection_t *h3c; - - h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t)); - if (h3c == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_queue_init(&h3c->blocked); - - hc = &h3c->hc; - hc->quic = 1; - hc->ssl = 1; - - } else -#endif - hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); - + hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); if (hc == NULL) { ngx_http_close_connection(c); return; @@ -325,6 +303,46 @@ ngx_http_init_connection(ngx_connection_t *c) /* the default server configuration for the address:port */ hc->conf_ctx = hc->addr_conf->default_server->ctx; +#if (NGX_HTTP_QUIC) + + if (hc->addr_conf->quic) { + ngx_quic_conf_t *qcf; + ngx_http_ssl_srv_conf_t *sscf; + +#if (NGX_HTTP_V3) + + if (hc->addr_conf->http3) { + ngx_int_t rc; + + rc = ngx_http_v3_init_connection(c); + + if (rc == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (rc == NGX_DONE) { + return; + } + } + +#endif + + if (c->qs == NULL) { + c->log->connection = c->number; + + qcf = ngx_http_get_module_srv_conf(hc->conf_ctx, + ngx_http_quic_module); + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, + ngx_http_ssl_module); + + ngx_quic_run(c, &sscf->ssl, qcf, ngx_http_init_connection); + return; + } + } + +#endif + ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); if (ctx == NULL) { ngx_http_close_connection(c); @@ -346,23 +364,6 @@ ngx_http_init_connection(ngx_connection_t *c) rev->handler = ngx_http_wait_request_handler; c->write->handler = ngx_http_empty_handler; - if (c->shared) { - rev->ready = 1; - } - -#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, &v3cf->quic, ngx_http_quic_stream_handler); - return; - } -#endif - #if (NGX_HTTP_V2) if (hc->addr_conf->http2) { rev->handler = ngx_http_v2_init; @@ -410,72 +411,6 @@ ngx_http_init_connection(ngx_connection_t *c) } -#if (NGX_HTTP_V3) - -static void -ngx_http_quic_stream_handler(ngx_connection_t *c) -{ - ngx_event_t *rev; - ngx_http_log_ctx_t *ctx; - ngx_http_connection_t *hc; - ngx_http_v3_connection_t *h3c; - - h3c = c->qs->parent->data; - - if (!h3c->settings_sent) { - h3c->settings_sent = 1; - - if (ngx_http_v3_send_settings(c) != NGX_OK) { - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - "could not send settings"); - ngx_http_close_connection(c); - return; - } - } - - if (c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { - ngx_http_v3_handle_client_uni_stream(c); - return; - } - - hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); - if (hc == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(hc, h3c, sizeof(ngx_http_connection_t)); - c->data = hc; - - ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); - if (ctx == NULL) { - ngx_http_close_connection(c); - return; - } - - ctx->connection = c; - ctx->request = NULL; - ctx->current_request = NULL; - - c->log->handler = ngx_http_log_error; - c->log->data = ctx; - c->log->action = "waiting for request"; - - c->log_error = NGX_ERROR_INFO; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 new stream id:0x%uXL", c->qs->id); - - rev = c->read; - rev->handler = ngx_http_wait_request_handler; - c->write->handler = ngx_http_empty_handler; - - rev->handler(rev); -} - -#endif - - static void ngx_http_wait_request_handler(ngx_event_t *rev) { @@ -725,7 +660,7 @@ ngx_http_alloc_request(ngx_connection_t *c) r->http_version = NGX_HTTP_VERSION_10; #if (NGX_HTTP_V3) - if (hc->quic) { + if (hc->addr_conf->http3) { r->http_version = NGX_HTTP_VERSION_30; } #endif diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 1994000a4..3d33d93d4 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -324,7 +324,6 @@ typedef struct { ngx_chain_t *free; unsigned ssl:1; - unsigned quic:1; unsigned proxy_protocol:1; } ngx_http_connection_t; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index d4ea19aed..51117f720 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -118,6 +118,8 @@ typedef struct { } ngx_http_v3_connection_t; +ngx_int_t ngx_http_v3_init_connection(ngx_connection_t *c); + ngx_int_t ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b); ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t allow_underscores); @@ -130,9 +132,6 @@ uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix); -ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); -void ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c); - ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value); ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 386209cb7..58d75f6f7 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -10,114 +10,8 @@ #include -static char *ngx_http_v3_max_ack_delay(ngx_conf_t *cf, void *post, void *data); -static char *ngx_http_v3_max_udp_payload_size(ngx_conf_t *cf, void *post, - void *data); - - -static ngx_conf_post_t ngx_http_v3_max_ack_delay_post = - { ngx_http_v3_max_ack_delay }; -static ngx_conf_post_t ngx_http_v3_max_udp_payload_size_post = - { ngx_http_v3_max_udp_payload_size }; -static ngx_conf_num_bounds_t ngx_http_v3_ack_delay_exponent_bounds = - { ngx_conf_check_num_bounds, 0, 20 }; -static ngx_conf_num_bounds_t ngx_http_v3_active_connection_id_limit_bounds = - { ngx_conf_check_num_bounds, 2, -1 }; - - 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), - &ngx_http_v3_max_ack_delay_post }, - - { ngx_string("quic_max_udp_payload_size"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, quic.max_udp_payload_size), - &ngx_http_v3_max_udp_payload_size_post }, - - { ngx_string("quic_initial_max_data"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_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_size_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_size_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_size_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), - &ngx_http_v3_ack_delay_exponent_bounds }, - - { 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), - &ngx_http_v3_active_connection_id_limit_bounds }, - - { ngx_string("quic_retry"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, quic.retry), - NULL }, - { ngx_string("http3_max_field_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, @@ -143,8 +37,6 @@ static ngx_command_t ngx_http_v3_commands[] = { }; -static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_http3(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); @@ -185,8 +77,6 @@ ngx_module_t ngx_http_v3_module = { static ngx_http_variable_t ngx_http_v3_vars[] = { - { ngx_string("quic"), NULL, ngx_http_variable_quic, - 0, 0, 0 }, { ngx_string("http3"), NULL, ngx_http_variable_http3, 0, 0, 0 }, @@ -195,26 +85,6 @@ static ngx_http_variable_t ngx_http_v3_vars[] = { }; -static ngx_int_t -ngx_http_variable_quic(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data) -{ - if (r->connection->qs) { - - v->len = 4; - v->valid = 1; - v->no_cacheable = 1; - v->not_found = 0; - v->data = (u_char *) "quic"; - return NGX_OK; - } - - v->not_found = 1; - - return NGX_OK; -} - - static ngx_int_t ngx_http_variable_http3(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) @@ -264,31 +134,6 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) return NULL; } - /* - * set by ngx_pcalloc(): - * v3cf->quic.original_dcid = { 0, NULL }; - * v3cf->quic.initial_scid = { 0, NULL }; - * v3cf->quic.retry_scid = { 0, NULL }; - * 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_udp_payload_size = NGX_CONF_UNSET_SIZE; - v3cf->quic.initial_max_data = NGX_CONF_UNSET_SIZE; - v3cf->quic.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_SIZE; - v3cf->quic.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_SIZE; - v3cf->quic.initial_max_stream_data_uni = NGX_CONF_UNSET_SIZE; - 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; - - v3cf->quic.retry = NGX_CONF_UNSET; - v3cf->max_field_size = NGX_CONF_UNSET_SIZE; v3cf->max_table_capacity = NGX_CONF_UNSET_SIZE; v3cf->max_blocked_streams = NGX_CONF_UNSET_UINT; @@ -303,57 +148,6 @@ 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, 60000); - - ngx_conf_merge_msec_value(conf->quic.max_ack_delay, - prev->quic.max_ack_delay, - NGX_QUIC_DEFAULT_MAX_ACK_DELAY); - - ngx_conf_merge_size_value(conf->quic.max_udp_payload_size, - prev->quic.max_udp_payload_size, - NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); - - ngx_conf_merge_size_value(conf->quic.initial_max_data, - prev->quic.initial_max_data, - 16 * NGX_QUIC_STREAM_BUFSIZE); - - ngx_conf_merge_size_value(conf->quic.initial_max_stream_data_bidi_local, - prev->quic.initial_max_stream_data_bidi_local, - NGX_QUIC_STREAM_BUFSIZE); - - ngx_conf_merge_size_value(conf->quic.initial_max_stream_data_bidi_remote, - prev->quic.initial_max_stream_data_bidi_remote, - NGX_QUIC_STREAM_BUFSIZE); - - ngx_conf_merge_size_value(conf->quic.initial_max_stream_data_uni, - prev->quic.initial_max_stream_data_uni, - NGX_QUIC_STREAM_BUFSIZE); - - 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); - - ngx_conf_merge_uint_value(conf->quic.ack_delay_exponent, - prev->quic.ack_delay_exponent, - NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); - - ngx_conf_merge_uint_value(conf->quic.disable_active_migration, - prev->quic.disable_active_migration, 1); - - ngx_conf_merge_uint_value(conf->quic.active_connection_id_limit, - prev->quic.active_connection_id_limit, 2); - - ngx_conf_merge_value(conf->quic.retry, prev->quic.retry, 0); - - if (conf->quic.retry) { - if (RAND_bytes(conf->quic.token_key, sizeof(conf->quic.token_key)) <= 0) { - return NGX_CONF_ERROR; - } - } - ngx_conf_merge_size_value(conf->max_field_size, prev->max_field_size, NGX_HTTP_V3_DEFAULT_MAX_FIELD_SIZE); @@ -368,40 +162,3 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_OK; } - - -static char * -ngx_http_v3_max_ack_delay(ngx_conf_t *cf, void *post, void *data) -{ - ngx_msec_t *sp = data; - - if (*sp > 16384) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_max_ack_delay\" must be less than 16384"); - - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} - - -static char * -ngx_http_v3_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data) -{ - size_t *sp = data; - - if (*sp < NGX_QUIC_MIN_INITIAL_SIZE - || *sp > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_max_udp_payload_size\" must be between " - "%d and %d", - NGX_QUIC_MIN_INITIAL_SIZE, - NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); - - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 8eaa7fde6..04070f9a6 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -27,22 +27,55 @@ static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type); +static ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); -void -ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) +ngx_int_t +ngx_http_v3_init_connection(ngx_connection_t *c) { + ngx_http_connection_t *hc; ngx_http_v3_uni_stream_t *us; + ngx_http_v3_connection_t *h3c; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 new uni stream id:0x%uxL", c->qs->id); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init connection"); + + hc = c->data; + + if (c->qs == NULL) { + h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t)); + if (h3c == NULL) { + return NGX_ERROR; + } + + h3c->hc = *hc; + + ngx_queue_init(&h3c->blocked); + + c->data = h3c; + return NGX_OK; + } + + h3c = c->qs->parent->data; + + if (!h3c->settings_sent) { + h3c->settings_sent = 1; + + if (ngx_http_v3_send_settings(c) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "could not send settings"); + return NGX_ERROR; + } + } + + if ((c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { + return NGX_OK; + } us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, NULL); - ngx_http_v3_close_uni_stream(c); - return; + return NGX_ERROR; } us->index = -1; @@ -53,6 +86,8 @@ ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) c->write->handler = ngx_http_v3_dummy_write_handler; ngx_http_v3_read_uni_stream_type(c->read); + + return NGX_DONE; } @@ -366,7 +401,7 @@ failed: } -ngx_int_t +static ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c) { u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; -- cgit From 36f2873f6b0d8512c053935614fcc6ae9d969858 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 21 Jul 2020 23:08:39 +0300 Subject: QUIC: added "quic" listen parameter in Stream. Also, introduced ngx_stream_quic_module. --- src/http/modules/ngx_http_quic_module.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index d971c8d26..9888e2eae 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -241,6 +241,7 @@ ngx_http_quic_create_srv_conf(ngx_conf_t *cf) conf->tp.active_connection_id_limit = NGX_CONF_UNSET_UINT; conf->retry = NGX_CONF_UNSET; + conf->require_alpn = 1; return conf; } -- cgit From 3073ad1381c4d8f8aae4501d66497164167b2081 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 21 Jul 2020 23:08:23 +0300 Subject: QUIC: eliminated connection handler argument in ngx_quic_run(). Now c->listening->handler() is called instead. --- src/http/ngx_http_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index bfa8e11c5..9b6d461e0 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -336,7 +336,7 @@ ngx_http_init_connection(ngx_connection_t *c) sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); - ngx_quic_run(c, &sscf->ssl, qcf, ngx_http_init_connection); + ngx_quic_run(c, &sscf->ssl, qcf); return; } } -- cgit From cdc0d61ea0c55f89c9fbc35b4c785c1f5d7f895e Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 22 Jul 2020 11:03:42 +0300 Subject: HTTP/3: do not call shutdown() for QUIC streams. Previously, this triggered an alert "shutdown() failed" in error log. --- src/http/ngx_http_request.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 9b6d461e0..285879f2f 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -3504,11 +3504,13 @@ ngx_http_set_lingering_close(ngx_http_request_t *r) } } - if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { - ngx_connection_error(c, ngx_socket_errno, - ngx_shutdown_socket_n " failed"); - ngx_http_close_request(r, 0); - return; + if (c->fd != NGX_INVALID_FILE) { + if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); + ngx_http_close_request(r, 0); + return; + } } if (rev->ready) { -- cgit From 04b2a169a47ac5054350e670bc5d6fd10f2434fc Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 13 Jul 2020 16:00:00 +0300 Subject: HTTP/3: header encoding functions. --- src/http/v3/ngx_http_v3.c | 128 +++++++++++++++++++++++++++ src/http/v3/ngx_http_v3.h | 12 +++ src/http/v3/ngx_http_v3_request.c | 176 +++++++++++++++++++------------------- 3 files changed, 226 insertions(+), 90 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index e796da772..a80310faf 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -97,3 +97,131 @@ ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix) return (uintptr_t) p; } + + +uintptr_t +ngx_http_v3_encode_header_block_prefix(u_char *p, ngx_uint_t insert_count, + ngx_uint_t sign, ngx_uint_t delta_base) +{ + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, insert_count, 8) + + ngx_http_v3_encode_prefix_int(NULL, delta_base, 7); + } + + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, insert_count, 8); + + *p = sign ? 0x80 : 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, delta_base, 7); + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index) +{ + /* Indexed Header Field */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 6); + } + + *p = dynamic ? 0x80 : 0xc0; + + return ngx_http_v3_encode_prefix_int(p, index, 6); +} + + +uintptr_t +ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, + u_char *data, size_t len) +{ + /* Literal Header Field With Name Reference */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 4) + + ngx_http_v3_encode_prefix_int(NULL, len, 7) + + len; + } + + *p = dynamic ? 0x60 : 0x70; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 4); + + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7); + + if (data) { + p = ngx_cpymem(p, data, len); + } + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, ngx_str_t *value) +{ + /* Literal Header Field Without Name Reference */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, name->len, 3) + + name->len + + ngx_http_v3_encode_prefix_int(NULL, value->len, 7) + + value->len; + } + + *p = 0x30; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, name->len, 3); + + ngx_strlow(p, name->data, name->len); + p += name->len; + + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, value->len, 7); + + p = ngx_cpymem(p, value->data, value->len); + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index) +{ + /* Indexed Header Field With Post-Base Index */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 4); + } + + *p = 0x10; + + return ngx_http_v3_encode_prefix_int(p, index, 4); +} + + +uintptr_t +ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, u_char *data, + size_t len) +{ + /* Literal Header Field With Post-Base Name Reference */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 3) + + ngx_http_v3_encode_prefix_int(NULL, len, 7) + + len; + } + + *p = 0x08; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 3); + + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7); + + if (data) { + p = ngx_cpymem(p, data, len); + } + + return (uintptr_t) p; +} diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 51117f720..28e0e6f11 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -132,6 +132,18 @@ uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix); +uintptr_t ngx_http_v3_encode_header_block_prefix(u_char *p, + ngx_uint_t insert_count, ngx_uint_t sign, ngx_uint_t delta_base); +uintptr_t ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic, + ngx_uint_t index); +uintptr_t ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, + ngx_uint_t index, u_char *data, size_t len); +uintptr_t ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, + ngx_str_t *value); +uintptr_t ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index); +uintptr_t ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, + u_char *data, size_t len); + ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value); ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 0ffa8927d..5fa232a91 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,6 +10,16 @@ #include +/* static table indices */ +#define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO 4 +#define NGX_HTTP_V3_HEADER_DATE 6 +#define NGX_HTTP_V3_HEADER_LAST_MODIFIED 10 +#define NGX_HTTP_V3_HEADER_STATUS_200 25 +#define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 +#define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING 59 +#define NGX_HTTP_V3_HEADER_SERVER 92 + + static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); @@ -416,7 +426,7 @@ ngx_http_v3_create_header(ngx_http_request_t *r) u_char *p; size_t len, n; ngx_buf_t *b; - ngx_uint_t i, j; + ngx_uint_t i; ngx_chain_t *hl, *cl, *bl; ngx_list_part_t *part; ngx_table_elt_t *header; @@ -427,14 +437,16 @@ ngx_http_v3_create_header(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create header"); - len = 2; + len = ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); if (r->headers_out.status == NGX_HTTP_OK) { - len += ngx_http_v3_encode_prefix_int(NULL, 25, 6); + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_STATUS_200); } else { - len += 3 + ngx_http_v3_encode_prefix_int(NULL, 25, 4) - + ngx_http_v3_encode_prefix_int(NULL, 3, 7); + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_STATUS_200, + NULL, 3); } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -450,15 +462,14 @@ ngx_http_v3_create_header(ngx_http_request_t *r) n = sizeof("nginx") - 1; } - len += ngx_http_v3_encode_prefix_int(NULL, 92, 4) - + ngx_http_v3_encode_prefix_int(NULL, n, 7) + n; + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_SERVER, + NULL, n); } if (r->headers_out.date == NULL) { - len += ngx_http_v3_encode_prefix_int(NULL, 6, 4) - + ngx_http_v3_encode_prefix_int(NULL, ngx_cached_http_time.len, - 7) - + ngx_cached_http_time.len; + len += ngx_http_v3_encode_header_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE, + NULL, ngx_cached_http_time.len); } if (r->headers_out.content_type.len) { @@ -470,22 +481,29 @@ ngx_http_v3_create_header(ngx_http_request_t *r) n += sizeof("; charset=") - 1 + r->headers_out.charset.len; } - len += ngx_http_v3_encode_prefix_int(NULL, 53, 4) - + ngx_http_v3_encode_prefix_int(NULL, n, 7) + n; + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, + NULL, n); } - if (r->headers_out.content_length_n > 0) { - len += ngx_http_v3_encode_prefix_int(NULL, 4, 4) + 1 + NGX_OFF_T_LEN; + if (r->headers_out.content_length == NULL) { + if (r->headers_out.content_length_n > 0) { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, + NULL, NGX_OFF_T_LEN); - } else if (r->headers_out.content_length_n == 0) { - len += ngx_http_v3_encode_prefix_int(NULL, 4, 6); + } else if (r->headers_out.content_length_n == 0) { + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); + } } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { - len += ngx_http_v3_encode_prefix_int(NULL, 10, 4) + 1 - + sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT"); + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, + sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); } /* XXX location */ @@ -493,8 +511,8 @@ ngx_http_v3_create_header(ngx_http_request_t *r) #if (NGX_HTTP_GZIP) if (r->gzip_vary) { if (clcf->gzip_vary) { - /* Vary: Accept-Encoding */ - len += ngx_http_v3_encode_prefix_int(NULL, 59, 6); + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); } else { r->gzip_vary = 0; @@ -521,10 +539,8 @@ ngx_http_v3_create_header(ngx_http_request_t *r) continue; } - len += ngx_http_v3_encode_prefix_int(NULL, header[i].key.len, 3) - + header[i].key.len - + ngx_http_v3_encode_prefix_int(NULL, header[i].value.len, 7 ) - + header[i].value.len; + len += ngx_http_v3_encode_header_l(NULL, &header[i].key, + &header[i].value); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); @@ -534,20 +550,17 @@ ngx_http_v3_create_header(ngx_http_request_t *r) return NULL; } - *b->last++ = 0; - *b->last++ = 0; + b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, + 0, 0, 0); if (r->headers_out.status == NGX_HTTP_OK) { - /* :status: 200 */ - *b->last = 0xc0; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 6); + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_STATUS_200); } else { - /* :status: 200 */ - *b->last = 0x70; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 4); - *b->last = 0; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 3, 7); + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_STATUS_200, + NULL, 3); b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); } @@ -565,23 +578,16 @@ ngx_http_v3_create_header(ngx_http_request_t *r) n = sizeof("nginx") - 1; } - /* server */ - *b->last = 0x70; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 92, 4); - *b->last = 0; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, n, 7); - b->last = ngx_cpymem(b->last, p, n); + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_SERVER, + p, n); } if (r->headers_out.date == NULL) { - /* date */ - *b->last = 0x70; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 6, 4); - *b->last = 0; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, - ngx_cached_http_time.len, 7); - b->last = ngx_cpymem(b->last, ngx_cached_http_time.data, - ngx_cached_http_time.len); + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_DATE, + ngx_cached_http_time.data, + ngx_cached_http_time.len); } if (r->headers_out.content_type.len) { @@ -593,23 +599,21 @@ ngx_http_v3_create_header(ngx_http_request_t *r) n += sizeof("; charset=") - 1 + r->headers_out.charset.len; } - /* content-type: text/plain */ - *b->last = 0x70; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 53, 4); - *b->last = 0; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, n, 7); + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, + NULL, n); p = b->last; - b->last = ngx_copy(b->last, r->headers_out.content_type.data, - r->headers_out.content_type.len); + b->last = ngx_cpymem(b->last, r->headers_out.content_type.data, + r->headers_out.content_type.len); if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { b->last = ngx_cpymem(b->last, "; charset=", sizeof("; charset=") - 1); - b->last = ngx_copy(b->last, r->headers_out.charset.data, - r->headers_out.charset.len); + b->last = ngx_cpymem(b->last, r->headers_out.charset.data, + r->headers_out.charset.len); /* update r->headers_out.content_type for possible logging */ @@ -618,36 +622,38 @@ ngx_http_v3_create_header(ngx_http_request_t *r) } } - if (r->headers_out.content_length_n > 0) { - /* content-length: 0 */ - *b->last = 0x70; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 4); - p = b->last++; - b->last = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); - *p = b->last - p - 1; + if (r->headers_out.content_length == NULL) { + if (r->headers_out.content_length_n > 0) { + p = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); + n = p - b->last; + + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, + NULL, n); + + b->last = ngx_sprintf(b->last, "%O", + r->headers_out.content_length_n); - } else if (r->headers_out.content_length_n == 0) { - /* content-length: 0 */ - *b->last = 0xc0; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 6); + } else if (r->headers_out.content_length_n == 0) { + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); + } } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { - /* last-modified */ - *b->last = 0x70; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 10, 4); - p = b->last++; + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, + sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); + b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); - *p = b->last - p - 1; } #if (NGX_HTTP_GZIP) if (r->gzip_vary) { - /* vary: accept-encoding */ - *b->last = 0xc0; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 59, 6); + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); } #endif @@ -670,19 +676,9 @@ ngx_http_v3_create_header(ngx_http_request_t *r) continue; } - *b->last = 0x30; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, - header[i].key.len, - 3); - for (j = 0; j < header[i].key.len; j++) { - *b->last++ = ngx_tolower(header[i].key.data[j]); - } - - *b->last = 0; - b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, - header[i].value.len, - 7); - b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + b->last = (u_char *) ngx_http_v3_encode_header_l(b->last, + &header[i].key, + &header[i].value); } if (r->header_only) { -- cgit From fc5a7234b469cf939c7e44b76ca1146ac7ecf519 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 23 Jul 2020 12:31:40 +0300 Subject: HTTP/3: generate Location response header for absolute redirects. --- src/http/v3/ngx_http_v3_request.c | 83 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 5fa232a91..0edd8f514 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -14,6 +14,7 @@ #define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO 4 #define NGX_HTTP_V3_HEADER_DATE 6 #define NGX_HTTP_V3_HEADER_LAST_MODIFIED 10 +#define NGX_HTTP_V3_HEADER_LOCATION 12 #define NGX_HTTP_V3_HEADER_STATUS_200 25 #define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 #define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING 59 @@ -426,12 +427,15 @@ ngx_http_v3_create_header(ngx_http_request_t *r) u_char *p; size_t len, n; ngx_buf_t *b; - ngx_uint_t i; + ngx_str_t host; + ngx_uint_t i, port; ngx_chain_t *hl, *cl, *bl; ngx_list_part_t *part; ngx_table_elt_t *header; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + u_char addr[NGX_SOCKADDR_STRLEN]; c = r->connection; @@ -506,7 +510,52 @@ ngx_http_v3_create_header(ngx_http_request_t *r) sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); } - /* XXX location */ + if (r->headers_out.location + && r->headers_out.location->value.len + && r->headers_out.location->value.data[0] == '/' + && clcf->absolute_redirect) + { + r->headers_out.location->hash = 0; + + if (clcf->server_name_in_redirect) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + host = cscf->server_name; + + } else if (r->headers_in.server.len) { + host = r->headers_in.server; + + } else { + host.len = NGX_SOCKADDR_STRLEN; + host.data = addr; + + if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { + return NULL; + } + } + + port = ngx_inet_get_port(c->local_sockaddr); + + n = sizeof("https://") - 1 + host.len + + r->headers_out.location->value.len; + + if (clcf->port_in_redirect) { + port = (port == 443) ? 0 : port; + + } else { + port = 0; + } + + if (port) { + n += sizeof(":65535") - 1; + } + + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_LOCATION, NULL, n); + + } else { + ngx_str_null(&host); + port = 0; + } #if (NGX_HTTP_GZIP) if (r->gzip_vary) { @@ -650,6 +699,36 @@ ngx_http_v3_create_header(ngx_http_request_t *r) b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); } + if (host.data) { + n = sizeof("https://") - 1 + host.len + + r->headers_out.location->value.len; + + if (port) { + n += ngx_sprintf(b->last, ":%ui", port) - b->last; + } + + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_LOCATION, + NULL, n); + + p = b->last; + b->last = ngx_cpymem(b->last, "https://", sizeof("https://") - 1); + b->last = ngx_cpymem(b->last, host.data, host.len); + + if (port) { + b->last = ngx_sprintf(b->last, ":%ui", port); + } + + b->last = ngx_cpymem(b->last, r->headers_out.location->value.data, + r->headers_out.location->value.len); + + /* update r->headers_out.location->value for possible logging */ + + r->headers_out.location->value.len = b->last - p; + r->headers_out.location->value.data = p; + ngx_str_set(&r->headers_out.location->key, "Location"); + } + #if (NGX_HTTP_GZIP) if (r->gzip_vary) { b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, -- cgit From 6d7ddb54711c00e6a43ae16c9151ad9d9c89a86c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 13 Jul 2020 12:33:00 +0300 Subject: HTTP/3: encode frame ids with ngx_http_v3_encode_varlen_int(). Even though typically frame ids fit into a single byte, calling ngx_http_v3_encode_varlen_int() adds to the code clarity. --- src/http/v3/ngx_http_v3_request.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 0edd8f514..af9cbd2b3 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -774,14 +774,16 @@ ngx_http_v3_create_header(ngx_http_request_t *r) n = b->last - b->pos; - len = 1 + ngx_http_v3_encode_varlen_int(NULL, n); + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) + + ngx_http_v3_encode_varlen_int(NULL, n); b = ngx_create_temp_buf(c->pool, len); if (b == NULL) { return NULL; } - *b->last++ = NGX_HTTP_V3_FRAME_HEADERS; + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_HEADERS); b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); hl = ngx_alloc_chain_link(c->pool); @@ -793,7 +795,8 @@ ngx_http_v3_create_header(ngx_http_request_t *r) hl->next = cl; if (r->headers_out.content_length_n >= 0 && !r->header_only) { - len = 1 + ngx_http_v3_encode_varlen_int(NULL, + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA) + + ngx_http_v3_encode_varlen_int(NULL, r->headers_out.content_length_n); b = ngx_create_temp_buf(c->pool, len); @@ -801,7 +804,8 @@ ngx_http_v3_create_header(ngx_http_request_t *r) return NULL; } - *b->last++ = NGX_HTTP_V3_FRAME_DATA; + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_DATA); b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, r->headers_out.content_length_n); -- cgit From 47ed87f85548e329bbfa76dfc8749ac0f21e55ca Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 13 Jul 2020 12:38:08 +0300 Subject: HTTP/3: renamed ngx_http_v3.c to ngx_http_v3_encode.c. The file contains only encoding functions. --- src/http/v3/ngx_http_v3.c | 227 --------------------------------------- src/http/v3/ngx_http_v3_encode.c | 227 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+), 227 deletions(-) delete mode 100644 src/http/v3/ngx_http_v3.c create mode 100644 src/http/v3/ngx_http_v3_encode.c (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c deleted file mode 100644 index a80310faf..000000000 --- a/src/http/v3/ngx_http_v3.c +++ /dev/null @@ -1,227 +0,0 @@ - -/* - * Copyright (C) Roman Arutyunyan - * Copyright (C) Nginx, Inc. - */ - - -#include -#include -#include - - -uintptr_t -ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value) -{ - if (value <= 63) { - if (p == NULL) { - return 1; - } - - *p++ = value; - return (uintptr_t) p; - } - - if (value <= 16383) { - if (p == NULL) { - return 2; - } - - *p++ = 0x40 | (value >> 8); - *p++ = value; - return (uintptr_t) p; - } - - if (value <= 1073741823) { - if (p == NULL) { - return 4; - } - - *p++ = 0x80 | (value >> 24); - *p++ = (value >> 16); - *p++ = (value >> 8); - *p++ = value; - return (uintptr_t) p; - } - - if (p == NULL) { - return 8; - } - - *p++ = 0xc0 | (value >> 56); - *p++ = (value >> 48); - *p++ = (value >> 40); - *p++ = (value >> 32); - *p++ = (value >> 24); - *p++ = (value >> 16); - *p++ = (value >> 8); - *p++ = value; - return (uintptr_t) p; -} - - -uintptr_t -ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix) -{ - ngx_uint_t thresh, n; - - thresh = (1 << prefix) - 1; - - if (value < thresh) { - if (p == NULL) { - return 1; - } - - *p++ |= value; - return (uintptr_t) p; - } - - value -= thresh; - - if (p == NULL) { - for (n = 2; value >= 128; n++) { - value >>= 7; - } - - return n; - } - - *p++ |= thresh; - - while (value >= 128) { - *p++ = 0x80 | value; - value >>= 7; - } - - *p++ = value; - - return (uintptr_t) p; -} - - -uintptr_t -ngx_http_v3_encode_header_block_prefix(u_char *p, ngx_uint_t insert_count, - ngx_uint_t sign, ngx_uint_t delta_base) -{ - if (p == NULL) { - return ngx_http_v3_encode_prefix_int(NULL, insert_count, 8) - + ngx_http_v3_encode_prefix_int(NULL, delta_base, 7); - } - - *p = 0; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, insert_count, 8); - - *p = sign ? 0x80 : 0; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, delta_base, 7); - - return (uintptr_t) p; -} - - -uintptr_t -ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index) -{ - /* Indexed Header Field */ - - if (p == NULL) { - return ngx_http_v3_encode_prefix_int(NULL, index, 6); - } - - *p = dynamic ? 0x80 : 0xc0; - - return ngx_http_v3_encode_prefix_int(p, index, 6); -} - - -uintptr_t -ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, - u_char *data, size_t len) -{ - /* Literal Header Field With Name Reference */ - - if (p == NULL) { - return ngx_http_v3_encode_prefix_int(NULL, index, 4) - + ngx_http_v3_encode_prefix_int(NULL, len, 7) - + len; - } - - *p = dynamic ? 0x60 : 0x70; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 4); - - *p = 0; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7); - - if (data) { - p = ngx_cpymem(p, data, len); - } - - return (uintptr_t) p; -} - - -uintptr_t -ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, ngx_str_t *value) -{ - /* Literal Header Field Without Name Reference */ - - if (p == NULL) { - return ngx_http_v3_encode_prefix_int(NULL, name->len, 3) - + name->len - + ngx_http_v3_encode_prefix_int(NULL, value->len, 7) - + value->len; - } - - *p = 0x30; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, name->len, 3); - - ngx_strlow(p, name->data, name->len); - p += name->len; - - *p = 0; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, value->len, 7); - - p = ngx_cpymem(p, value->data, value->len); - - return (uintptr_t) p; -} - - -uintptr_t -ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index) -{ - /* Indexed Header Field With Post-Base Index */ - - if (p == NULL) { - return ngx_http_v3_encode_prefix_int(NULL, index, 4); - } - - *p = 0x10; - - return ngx_http_v3_encode_prefix_int(p, index, 4); -} - - -uintptr_t -ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, u_char *data, - size_t len) -{ - /* Literal Header Field With Post-Base Name Reference */ - - if (p == NULL) { - return ngx_http_v3_encode_prefix_int(NULL, index, 3) - + ngx_http_v3_encode_prefix_int(NULL, len, 7) - + len; - } - - *p = 0x08; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 3); - - *p = 0; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7); - - if (data) { - p = ngx_cpymem(p, data, len); - } - - return (uintptr_t) p; -} diff --git a/src/http/v3/ngx_http_v3_encode.c b/src/http/v3/ngx_http_v3_encode.c new file mode 100644 index 000000000..a80310faf --- /dev/null +++ b/src/http/v3/ngx_http_v3_encode.c @@ -0,0 +1,227 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +uintptr_t +ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value) +{ + if (value <= 63) { + if (p == NULL) { + return 1; + } + + *p++ = value; + return (uintptr_t) p; + } + + if (value <= 16383) { + if (p == NULL) { + return 2; + } + + *p++ = 0x40 | (value >> 8); + *p++ = value; + return (uintptr_t) p; + } + + if (value <= 1073741823) { + if (p == NULL) { + return 4; + } + + *p++ = 0x80 | (value >> 24); + *p++ = (value >> 16); + *p++ = (value >> 8); + *p++ = value; + return (uintptr_t) p; + } + + if (p == NULL) { + return 8; + } + + *p++ = 0xc0 | (value >> 56); + *p++ = (value >> 48); + *p++ = (value >> 40); + *p++ = (value >> 32); + *p++ = (value >> 24); + *p++ = (value >> 16); + *p++ = (value >> 8); + *p++ = value; + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix) +{ + ngx_uint_t thresh, n; + + thresh = (1 << prefix) - 1; + + if (value < thresh) { + if (p == NULL) { + return 1; + } + + *p++ |= value; + return (uintptr_t) p; + } + + value -= thresh; + + if (p == NULL) { + for (n = 2; value >= 128; n++) { + value >>= 7; + } + + return n; + } + + *p++ |= thresh; + + while (value >= 128) { + *p++ = 0x80 | value; + value >>= 7; + } + + *p++ = value; + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_header_block_prefix(u_char *p, ngx_uint_t insert_count, + ngx_uint_t sign, ngx_uint_t delta_base) +{ + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, insert_count, 8) + + ngx_http_v3_encode_prefix_int(NULL, delta_base, 7); + } + + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, insert_count, 8); + + *p = sign ? 0x80 : 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, delta_base, 7); + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index) +{ + /* Indexed Header Field */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 6); + } + + *p = dynamic ? 0x80 : 0xc0; + + return ngx_http_v3_encode_prefix_int(p, index, 6); +} + + +uintptr_t +ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, + u_char *data, size_t len) +{ + /* Literal Header Field With Name Reference */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 4) + + ngx_http_v3_encode_prefix_int(NULL, len, 7) + + len; + } + + *p = dynamic ? 0x60 : 0x70; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 4); + + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7); + + if (data) { + p = ngx_cpymem(p, data, len); + } + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, ngx_str_t *value) +{ + /* Literal Header Field Without Name Reference */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, name->len, 3) + + name->len + + ngx_http_v3_encode_prefix_int(NULL, value->len, 7) + + value->len; + } + + *p = 0x30; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, name->len, 3); + + ngx_strlow(p, name->data, name->len); + p += name->len; + + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, value->len, 7); + + p = ngx_cpymem(p, value->data, value->len); + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index) +{ + /* Indexed Header Field With Post-Base Index */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 4); + } + + *p = 0x10; + + return ngx_http_v3_encode_prefix_int(p, index, 4); +} + + +uintptr_t +ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, u_char *data, + size_t len) +{ + /* Literal Header Field With Post-Base Name Reference */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 3) + + ngx_http_v3_encode_prefix_int(NULL, len, 7) + + len; + } + + *p = 0x08; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 3); + + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7); + + if (data) { + p = ngx_cpymem(p, data, len); + } + + return (uintptr_t) p; +} -- cgit From f2368597c2b2b0263e5472c2002a52e7594094f0 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 23 Jul 2020 13:12:01 +0300 Subject: HTTP/3: renamed server configuration variables from v3cf to h3scf. Now they are similar to HTTP/2 where they are called h2scf. --- src/http/v3/ngx_http_v3_module.c | 14 +++++++------- src/http/v3/ngx_http_v3_parse.c | 6 +++--- src/http/v3/ngx_http_v3_streams.c | 12 ++++++------ src/http/v3/ngx_http_v3_tables.c | 20 ++++++++++---------- 4 files changed, 26 insertions(+), 26 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 58d75f6f7..00169ef4e 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -127,18 +127,18 @@ ngx_http_v3_add_variables(ngx_conf_t *cf) static void * ngx_http_v3_create_srv_conf(ngx_conf_t *cf) { - ngx_http_v3_srv_conf_t *v3cf; + ngx_http_v3_srv_conf_t *h3scf; - v3cf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_srv_conf_t)); - if (v3cf == NULL) { + h3scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_srv_conf_t)); + if (h3scf == NULL) { return NULL; } - v3cf->max_field_size = NGX_CONF_UNSET_SIZE; - v3cf->max_table_capacity = NGX_CONF_UNSET_SIZE; - v3cf->max_blocked_streams = NGX_CONF_UNSET_UINT; + h3scf->max_field_size = NGX_CONF_UNSET_SIZE; + h3scf->max_table_capacity = NGX_CONF_UNSET_SIZE; + h3scf->max_blocked_streams = NGX_CONF_UNSET_UINT; - return v3cf; + return h3scf; } diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index f9e26a4f5..8a68fddd8 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -426,7 +426,7 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, u_char ch) { ngx_uint_t n; - ngx_http_v3_srv_conf_t *v3cf; + ngx_http_v3_srv_conf_t *h3scf; enum { sw_start = 0, sw_value @@ -442,9 +442,9 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, n = st->length; - v3cf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - if (n > v3cf->max_field_size) { + if (n > h3scf->max_field_size) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_field_size limit"); return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 04070f9a6..63b88f259 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -407,7 +407,7 @@ ngx_http_v3_send_settings(ngx_connection_t *c) u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; size_t n; ngx_connection_t *cc; - ngx_http_v3_srv_conf_t *v3cf; + ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_connection_t *h3c; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); @@ -418,23 +418,23 @@ ngx_http_v3_send_settings(ngx_connection_t *c) } h3c = c->qs->parent->data; - v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); + h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); n = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); - n += ngx_http_v3_encode_varlen_int(NULL, v3cf->max_table_capacity); + n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_table_capacity); n += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); - n += ngx_http_v3_encode_varlen_int(NULL, v3cf->max_blocked_streams); + n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_blocked_streams); p = (u_char *) ngx_http_v3_encode_varlen_int(buf, NGX_HTTP_V3_FRAME_SETTINGS); p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, v3cf->max_table_capacity); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_table_capacity); p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, v3cf->max_blocked_streams); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_blocked_streams); n = p - buf; if (cc->send(cc, buf, n) != (ssize_t) n) { diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index 446601b01..bf4f1449c 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -243,7 +243,7 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) ngx_connection_t *pc; ngx_pool_cleanup_t *cln; ngx_http_v3_header_t **elts; - ngx_http_v3_srv_conf_t *v3cf; + ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; @@ -252,9 +252,9 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) pc = c->qs->parent; h3c = pc->data; - v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); + h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); - if (capacity > v3cf->max_table_capacity) { + if (capacity > h3scf->max_table_capacity) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_table_capacity limit"); return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; @@ -496,7 +496,7 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) { ngx_uint_t max_entries, full_range, max_value, max_wrapped, req_insert_count; - ngx_http_v3_srv_conf_t *v3cf; + ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; @@ -509,9 +509,9 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) h3c = c->qs->parent->data; dt = &h3c->table; - v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); + h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); - max_entries = v3cf->max_table_capacity / 32; + max_entries = h3scf->max_table_capacity / 32; full_range = 2 * max_entries; if (*insert_count > full_range) { @@ -551,7 +551,7 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) ngx_connection_t *pc; ngx_pool_cleanup_t *cln; ngx_http_v3_block_t *block; - ngx_http_v3_srv_conf_t *v3cf; + ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; @@ -595,10 +595,10 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) } if (block->queue.prev == NULL) { - v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, - ngx_http_v3_module); + h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, + ngx_http_v3_module); - if (h3c->nblocked == v3cf->max_blocked_streams) { + if (h3c->nblocked == h3scf->max_blocked_streams) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_blocked_streams limit"); return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; -- cgit From c8a194b29aed55fee195b5e11a20af6d1eeef961 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 23 Jul 2020 11:40:10 +0300 Subject: Style: moved function declarations to match usual code style. Plus a few other minor style changes. --- src/http/v3/ngx_http_v3_module.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 00169ef4e..2483b9cb6 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -10,6 +10,14 @@ #include +static ngx_int_t ngx_http_variable_http3(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); +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_command_t ngx_http_v3_commands[] = { { ngx_string("http3_max_field_size"), @@ -37,14 +45,6 @@ static ngx_command_t ngx_http_v3_commands[] = { }; -static ngx_int_t ngx_http_variable_http3(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data); -static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); -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 = { ngx_http_v3_add_variables, /* preconfiguration */ NULL, /* postconfiguration */ @@ -78,8 +78,7 @@ ngx_module_t ngx_http_v3_module = { static ngx_http_variable_t ngx_http_v3_vars[] = { - { ngx_string("http3"), NULL, ngx_http_variable_http3, - 0, 0, 0 }, + { ngx_string("http3"), NULL, ngx_http_variable_http3, 0, 0, 0 }, ngx_http_null_variable }; @@ -123,7 +122,6 @@ ngx_http_v3_add_variables(ngx_conf_t *cf) } - static void * ngx_http_v3_create_srv_conf(ngx_conf_t *cf) { -- cgit From 5e036a6bef567bbe4e87ce7958ab76663ed3242e Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 14 Jul 2020 16:52:44 +0300 Subject: HTTP/3: support $server_protocol variable. Now it holds "HTTP/3.0". Previously it was empty. --- src/http/ngx_http_parse.c | 4 ++++ src/http/ngx_http_request.c | 4 ---- src/http/v3/ngx_http_v3_request.c | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index b59a6ce4c..a351f2b27 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -833,6 +833,10 @@ done: r->request_end = p; } + if (r->http_protocol.data) { + r->http_protocol.len = r->request_end - r->http_protocol.data; + } + r->http_version = r->http_major * 1000 + r->http_minor; r->state = sw_start; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 285879f2f..82deef674 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1139,10 +1139,6 @@ ngx_http_process_request_line(ngx_event_t *rev) r->method_name.len = r->method_end - r->method_start; r->method_name.data = r->method_start; - if (r->http_protocol.data) { - r->http_protocol.len = r->request_end - r->http_protocol.data; - } - if (ngx_http_process_request_uri(r) != NGX_OK) { break; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index af9cbd2b3..adc40d368 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -110,6 +110,8 @@ ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) continue; } + ngx_str_set(&r->http_protocol, "HTTP/3.0"); + len = (r->method_end - r->method_start) + 1 + (r->uri_end - r->uri_start) + 1 + sizeof("HTTP/3") - 1; -- cgit From a1f7106bf76bad578e63a62cf992c06912a15274 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 27 Jul 2020 18:51:42 +0300 Subject: QUIC: limited the number of server-initiated streams. Also, ngx_quic_create_uni_stream() is replaced with ngx_quic_open_stream() which is capable of creating a bidi stream. --- src/http/v3/ngx_http_v3_streams.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 63b88f259..2ac3f7d74 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -55,16 +55,8 @@ ngx_http_v3_init_connection(ngx_connection_t *c) return NGX_OK; } - h3c = c->qs->parent->data; - - if (!h3c->settings_sent) { - h3c->settings_sent = 1; - - if (ngx_http_v3_send_settings(c) != NGX_OK) { - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - "could not send settings"); - return NGX_ERROR; - } + if (ngx_http_v3_send_settings(c) == NGX_ERROR) { + return NGX_ERROR; } if ((c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { @@ -361,7 +353,7 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) } } - sc = ngx_quic_create_uni_stream(c); + sc = ngx_quic_open_stream(c, 0); if (sc == NULL) { return NULL; } @@ -410,14 +402,19 @@ ngx_http_v3_send_settings(ngx_connection_t *c) ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_connection_t *h3c; + h3c = c->qs->parent->data; + + if (h3c->settings_sent) { + return NGX_OK; + } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); if (cc == NULL) { - return NGX_ERROR; + return NGX_DECLINED; } - h3c = c->qs->parent->data; h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); n = ngx_http_v3_encode_varlen_int(NULL, @@ -441,12 +438,17 @@ ngx_http_v3_send_settings(ngx_connection_t *c) goto failed; } + h3c->settings_sent = 1; + return NGX_OK; failed: ngx_http_v3_close_uni_stream(cc); + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "could not send settings"); + return NGX_ERROR; } -- cgit From 6d064c94e0600f7ff15e2815784f9f89cc4858b4 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 23 Jul 2020 13:41:24 +0300 Subject: HTTP/3: server pushes. New directives are added: - http3_max_concurrent_pushes - http3_push - http3_push_preload --- src/http/ngx_http.h | 1 + src/http/ngx_http_request.c | 3 +- src/http/v3/ngx_http_v3.h | 21 ++ src/http/v3/ngx_http_v3_module.c | 133 ++++++- src/http/v3/ngx_http_v3_parse.c | 36 +- src/http/v3/ngx_http_v3_request.c | 704 +++++++++++++++++++++++++++++++++++++- src/http/v3/ngx_http_v3_streams.c | 135 ++++++++ 7 files changed, 1020 insertions(+), 13 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 480802d40..2a3d81a37 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -93,6 +93,7 @@ ngx_int_t ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, void ngx_http_init_connection(ngx_connection_t *c); void ngx_http_close_connection(ngx_connection_t *c); +u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len); #if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME) int ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 82deef674..54e5e2866 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -55,7 +55,6 @@ static ngx_int_t ngx_http_post_action(ngx_http_request_t *r); static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error); static void ngx_http_log_request(ngx_http_request_t *r); -static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len); static u_char *ngx_http_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr, u_char *buf, size_t len); @@ -3838,7 +3837,7 @@ ngx_http_close_connection(ngx_connection_t *c) } -static u_char * +u_char * ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len) { u_char *p; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 28e0e6f11..aab27b3ac 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -50,6 +50,7 @@ #define NGX_HTTP_V3_DEFAULT_MAX_FIELD_SIZE 4096 #define NGX_HTTP_V3_DEFAULT_MAX_TABLE_CAPACITY 16384 #define NGX_HTTP_V3_DEFAULT_MAX_BLOCKED_STREAMS 16 +#define NGX_HTTP_V3_DEFAULT_MAX_CONCURRENT_PUSHES 10 /* HTTP/3 errors */ #define NGX_HTTP_V3_ERR_NO_ERROR 0x100 @@ -89,9 +90,17 @@ typedef struct { size_t max_field_size; size_t max_table_capacity; ngx_uint_t max_blocked_streams; + ngx_uint_t max_concurrent_pushes; } ngx_http_v3_srv_conf_t; +typedef struct { + ngx_flag_t push_preload; + ngx_flag_t push; + ngx_array_t *pushes; +} ngx_http_v3_loc_conf_t; + + typedef struct { ngx_str_t name; ngx_str_t value; @@ -110,8 +119,15 @@ typedef struct { typedef struct { ngx_http_connection_t hc; ngx_http_v3_dynamic_table_t table; + ngx_queue_t blocked; ngx_uint_t nblocked; + + ngx_queue_t pushing; + ngx_uint_t npushing; + uint64_t next_push_id; + uint64_t max_push_id; + ngx_uint_t settings_sent; /* unsigned settings_sent:1; */ ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; @@ -144,6 +160,8 @@ uintptr_t ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index); uintptr_t ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, u_char *data, size_t len); +ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, + uint64_t push_id); ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value); ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, @@ -163,6 +181,9 @@ ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count); ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value); +ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, + uint64_t max_push_id); +ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); ngx_int_t ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value); diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 2483b9cb6..89748f5f4 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -16,6 +16,10 @@ static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); 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 void *ngx_http_v3_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_v3_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_http_v3_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_command_t ngx_http_v3_commands[] = { @@ -41,6 +45,27 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, max_blocked_streams), NULL }, + { ngx_string("http3_max_concurrent_pushes"), + 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, max_concurrent_pushes), + NULL }, + + { ngx_string("http3_push"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_v3_push, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("http3_push_preload"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_v3_loc_conf_t, push_preload), + NULL }, + ngx_null_command }; @@ -55,8 +80,8 @@ static ngx_http_module_t ngx_http_v3_module_ctx = { 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 */ + ngx_http_v3_create_loc_conf, /* create location configuration */ + ngx_http_v3_merge_loc_conf /* merge location configuration */ }; @@ -135,6 +160,7 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) h3scf->max_field_size = NGX_CONF_UNSET_SIZE; h3scf->max_table_capacity = NGX_CONF_UNSET_SIZE; h3scf->max_blocked_streams = NGX_CONF_UNSET_UINT; + h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT; return h3scf; } @@ -158,5 +184,108 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) prev->max_blocked_streams, NGX_HTTP_V3_DEFAULT_MAX_BLOCKED_STREAMS); + ngx_conf_merge_uint_value(conf->max_concurrent_pushes, + prev->max_concurrent_pushes, + NGX_HTTP_V3_DEFAULT_MAX_CONCURRENT_PUSHES); + + return NGX_CONF_OK; +} + + +static void * +ngx_http_v3_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_v3_loc_conf_t *h3lcf; + + h3lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_loc_conf_t)); + if (h3lcf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * h3lcf->pushes = NULL; + */ + + h3lcf->push_preload = NGX_CONF_UNSET; + h3lcf->push = NGX_CONF_UNSET; + + return h3lcf; +} + + +static char * +ngx_http_v3_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_v3_loc_conf_t *prev = parent; + ngx_http_v3_loc_conf_t *conf = child; + + ngx_conf_merge_value(conf->push, prev->push, 1); + + if (conf->push && conf->pushes == NULL) { + conf->pushes = prev->pushes; + } + + ngx_conf_merge_value(conf->push_preload, prev->push_preload, 0); + + return NGX_CONF_OK; +} + + +static char * +ngx_http_v3_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_v3_loc_conf_t *h3lcf = conf; + + ngx_str_t *value; + ngx_http_complex_value_t *cv; + ngx_http_compile_complex_value_t ccv; + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "off") == 0) { + + if (h3lcf->pushes) { + return "\"off\" parameter cannot be used with URI"; + } + + if (h3lcf->push == 0) { + return "is duplicate"; + } + + h3lcf->push = 0; + return NGX_CONF_OK; + } + + if (h3lcf->push == 0) { + return "URI cannot be used with \"off\" parameter"; + } + + h3lcf->push = 1; + + if (h3lcf->pushes == NULL) { + h3lcf->pushes = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_complex_value_t)); + if (h3lcf->pushes == NULL) { + return NGX_CONF_ERROR; + } + } + + cv = ngx_array_push(h3lcf->pushes); + if (cv == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = cv; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + return NGX_CONF_OK; } diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 8a68fddd8..1a7aa17f8 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -933,6 +933,7 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) sw_first_type, sw_type, sw_length, + sw_cancel_push, sw_settings, sw_max_push_id, sw_skip @@ -988,6 +989,10 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) switch (st->type) { + case NGX_HTTP_V3_FRAME_CANCEL_PUSH: + st->state = sw_cancel_push; + break; + case NGX_HTTP_V3_FRAME_SETTINGS: st->state = sw_settings; break; @@ -1004,6 +1009,26 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) break; + case sw_cancel_push: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + + if (--st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } + + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_cancel_push(c, st->vlint.value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_type; + break; + case sw_settings: rc = ngx_http_v3_parse_settings(c, &st->settings, ch); @@ -1025,12 +1050,19 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) case sw_max_push_id: rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + + if (--st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } + if (rc != NGX_DONE) { return rc; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse MAX_PUSH_ID:%uL", st->vlint.value); + rc = ngx_http_v3_set_max_push_id(c, st->vlint.value); + if (rc != NGX_OK) { + return rc; + } st->state = sw_type; break; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index adc40d368..a0259be11 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -11,18 +11,37 @@ /* static table indices */ +#define NGX_HTTP_V3_HEADER_AUTHORITY 0 +#define NGX_HTTP_V3_HEADER_PATH_ROOT 1 #define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO 4 #define NGX_HTTP_V3_HEADER_DATE 6 #define NGX_HTTP_V3_HEADER_LAST_MODIFIED 10 #define NGX_HTTP_V3_HEADER_LOCATION 12 +#define NGX_HTTP_V3_HEADER_METHOD_GET 17 +#define NGX_HTTP_V3_HEADER_SCHEME_HTTP 22 +#define NGX_HTTP_V3_HEADER_SCHEME_HTTPS 23 #define NGX_HTTP_V3_HEADER_STATUS_200 25 +#define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING 31 #define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 #define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING 59 +#define NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE 72 #define NGX_HTTP_V3_HEADER_SERVER 92 +#define NGX_HTTP_V3_HEADER_USER_AGENT 95 static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); +static ngx_int_t ngx_http_v3_push_resources(ngx_http_request_t *r, + ngx_chain_t ***out); +static ngx_int_t ngx_http_v3_push_resource(ngx_http_request_t *r, + ngx_str_t *path, ngx_chain_t ***out); +static ngx_int_t ngx_http_v3_create_push_request( + ngx_http_request_t *pr, ngx_str_t *path, uint64_t push_id); +static ngx_int_t ngx_http_v3_set_push_header(ngx_http_request_t *r, + const char *name, ngx_str_t *value); +static void ngx_http_v3_push_request_handler(ngx_event_t *ev); +static ngx_chain_t *ngx_http_v3_create_push_promise(ngx_http_request_t *r, + ngx_str_t *path, uint64_t push_id); struct { @@ -431,7 +450,7 @@ ngx_http_v3_create_header(ngx_http_request_t *r) ngx_buf_t *b; ngx_str_t host; ngx_uint_t i, port; - ngx_chain_t *hl, *cl, *bl; + ngx_chain_t *out, *hl, *cl, **ll; ngx_list_part_t *part; ngx_table_elt_t *header; ngx_connection_t *c; @@ -443,6 +462,17 @@ ngx_http_v3_create_header(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create header"); + out = NULL; + ll = &out; + + if ((c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0 + && r->method != NGX_HTTP_HEAD) + { + if (ngx_http_v3_push_resources(r, &ll) != NGX_OK) { + return NULL; + } + } + len = ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); if (r->headers_out.status == NGX_HTTP_OK) { @@ -796,6 +826,9 @@ ngx_http_v3_create_header(ngx_http_request_t *r) hl->buf = b; hl->next = cl; + *ll = hl; + ll = &cl->next; + if (r->headers_out.content_length_n >= 0 && !r->header_only) { len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA) + ngx_http_v3_encode_varlen_int(NULL, @@ -811,17 +844,18 @@ ngx_http_v3_create_header(ngx_http_request_t *r) b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, r->headers_out.content_length_n); - bl = ngx_alloc_chain_link(c->pool); - if (bl == NULL) { + cl = ngx_alloc_chain_link(c->pool); + if (cl == NULL) { return NULL; } - bl->buf = b; - bl->next = NULL; - cl->next = bl; + cl->buf = b; + cl->next = NULL; + + *ll = cl; } - return hl; + return out; } @@ -853,3 +887,659 @@ ngx_http_v3_create_trailers(ngx_http_request_t *r) return cl; } + + +static ngx_int_t +ngx_http_v3_push_resources(ngx_http_request_t *r, ngx_chain_t ***out) +{ + u_char *start, *end, *last; + ngx_str_t path; + ngx_int_t rc; + ngx_uint_t i, push; + ngx_table_elt_t **h; + ngx_http_v3_loc_conf_t *h3lcf; + ngx_http_complex_value_t *pushes; + + h3lcf = ngx_http_get_module_loc_conf(r, ngx_http_v3_module); + + if (h3lcf->pushes) { + pushes = h3lcf->pushes->elts; + + for (i = 0; i < h3lcf->pushes->nelts; i++) { + + if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) { + return NGX_ERROR; + } + + if (path.len == 0) { + continue; + } + + if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) { + continue; + } + + rc = ngx_http_v3_push_resource(r, &path, out); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_ABORT) { + return NGX_OK; + } + + /* NGX_OK, NGX_DECLINED */ + } + } + + if (!h3lcf->push_preload) { + return NGX_OK; + } + + h = r->headers_out.link.elts; + + for (i = 0; i < r->headers_out.link.nelts; i++) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 parse link: \"%V\"", &h[i]->value); + + start = h[i]->value.data; + end = h[i]->value.data + h[i]->value.len; + + next_link: + + while (start < end && *start == ' ') { start++; } + + if (start == end || *start++ != '<') { + continue; + } + + while (start < end && *start == ' ') { start++; } + + for (last = start; last < end && *last != '>'; last++) { + /* void */ + } + + if (last == start || last == end) { + continue; + } + + path.len = last - start; + path.data = start; + + start = last + 1; + + while (start < end && *start == ' ') { start++; } + + if (start == end) { + continue; + } + + if (*start == ',') { + start++; + goto next_link; + } + + if (*start++ != ';') { + continue; + } + + last = ngx_strlchr(start, end, ','); + + if (last == NULL) { + last = end; + } + + push = 0; + + for ( ;; ) { + + while (start < last && *start == ' ') { start++; } + + if (last - start >= 6 + && ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0) + { + start += 6; + + if (start == last || *start == ' ' || *start == ';') { + push = 0; + break; + } + + goto next_param; + } + + if (last - start >= 11 + && ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0) + { + start += 11; + + if (start == last || *start == ' ' || *start == ';') { + push = 1; + } + + goto next_param; + } + + if (last - start >= 4 + && ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0) + { + start += 4; + + while (start < last && *start == ' ') { start++; } + + if (start == last || *start++ != '"') { + goto next_param; + } + + for ( ;; ) { + + while (start < last && *start == ' ') { start++; } + + if (last - start >= 7 + && ngx_strncasecmp(start, (u_char *) "preload", 7) == 0) + { + start += 7; + + if (start < last && (*start == ' ' || *start == '"')) { + push = 1; + break; + } + } + + while (start < last && *start != ' ' && *start != '"') { + start++; + } + + if (start == last) { + break; + } + + if (*start == '"') { + break; + } + + start++; + } + } + + next_param: + + start = ngx_strlchr(start, last, ';'); + + if (start == NULL) { + break; + } + + start++; + } + + if (push) { + while (path.len && path.data[path.len - 1] == ' ') { + path.len--; + } + } + + if (push && path.len + && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/')) + { + rc = ngx_http_v3_push_resource(r, &path, out); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_ABORT) { + return NGX_OK; + } + + /* NGX_OK, NGX_DECLINED */ + } + + if (last < end) { + start = last + 1; + goto next_link; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, + ngx_chain_t ***ll) +{ + uint64_t push_id; + ngx_int_t rc; + ngx_chain_t *cl; + ngx_connection_t *c; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_connection_t *h3c; + + c = r->connection; + h3c = c->qs->parent->data; + h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 push \"%V\" pushing:%ui/%ui id:%uL/%uL", + path, h3c->npushing, h3scf->max_concurrent_pushes, + h3c->next_push_id, h3c->max_push_id); + + if (!ngx_path_separator(path->data[0])) { + ngx_log_error(NGX_LOG_WARN, c->log, 0, + "non-absolute path \"%V\" not pushed", path); + return NGX_DECLINED; + } + + if (h3c->next_push_id > h3c->max_push_id) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 abort pushes due to max_push_id"); + return NGX_ABORT; + } + + if (h3c->npushing >= h3scf->max_concurrent_pushes) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 abort pushes due to max_concurrent_pushes"); + return NGX_ABORT; + } + + push_id = h3c->next_push_id++; + + rc = ngx_http_v3_create_push_request(r, path, push_id); + if (rc != NGX_OK) { + return rc; + } + + cl = ngx_http_v3_create_push_promise(r, path, push_id); + if (cl == NULL) { + return NGX_ERROR; + } + + for (**ll = cl; **ll; *ll = &(**ll)->next); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, + uint64_t push_id) +{ + ngx_pool_t *pool; + ngx_connection_t *c, *pc; + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + ngx_http_v3_connection_t *h3c; + + pc = pr->connection; + + r = NULL; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "http3 create push request id:%uL", push_id); + + c = ngx_http_v3_create_push_stream(pc, push_id); + if (c == NULL) { + return NGX_ABORT; + } + + hc = ngx_palloc(c->pool, sizeof(ngx_http_connection_t)); + if (hc == NULL) { + goto failed; + } + + h3c = c->qs->parent->data; + ngx_memcpy(hc, h3c, sizeof(ngx_http_connection_t)); + c->data = hc; + + ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); + if (ctx == NULL) { + goto failed; + } + + ctx->connection = c; + ctx->request = NULL; + ctx->current_request = NULL; + + c->log->handler = ngx_http_log_error; + c->log->data = ctx; + c->log->action = "processing pushed request headers"; + + c->log_error = NGX_ERROR_INFO; + + r = ngx_http_create_request(c); + if (r == NULL) { + goto failed; + } + + c->data = r; + + ngx_str_set(&r->http_protocol, "HTTP/3.0"); + + r->method_name = ngx_http_core_get_method; + r->method = NGX_HTTP_GET; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + r->header_in = ngx_create_temp_buf(r->pool, + cscf->client_header_buffer_size); + if (r->header_in == NULL) { + goto failed; + } + + if (ngx_list_init(&r->headers_in.headers, r->pool, 4, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + goto failed; + } + + r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; + + r->schema.data = ngx_pstrdup(r->pool, &pr->schema); + if (r->schema.data == NULL) { + goto failed; + } + + r->schema.len = pr->schema.len; + + r->uri_start = ngx_pstrdup(r->pool, path); + if (r->uri_start == NULL) { + goto failed; + } + + r->uri_end = r->uri_start + path->len; + + if (ngx_http_parse_uri(r) != NGX_OK) { + goto failed; + } + + if (ngx_http_process_request_uri(r) != NGX_OK) { + goto failed; + } + + if (ngx_http_v3_set_push_header(r, "host", &pr->headers_in.server) + != NGX_OK) + { + goto failed; + } + + if (pr->headers_in.accept_encoding) { + if (ngx_http_v3_set_push_header(r, "accept-encoding", + &pr->headers_in.accept_encoding->value) + != NGX_OK) + { + goto failed; + } + } + + if (pr->headers_in.accept_language) { + if (ngx_http_v3_set_push_header(r, "accept-language", + &pr->headers_in.accept_language->value) + != NGX_OK) + { + goto failed; + } + } + + if (pr->headers_in.user_agent) { + if (ngx_http_v3_set_push_header(r, "user-agent", + &pr->headers_in.user_agent->value) + != NGX_OK) + { + goto failed; + } + } + + c->read->handler = ngx_http_v3_push_request_handler; + c->read->handler = ngx_http_v3_push_request_handler; + + ngx_post_event(c->read, &ngx_posted_events); + + return NGX_OK; + +failed: + + if (r) { + ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + c->destroyed = 1; + + pool = c->pool; + + ngx_close_connection(c); + + ngx_destroy_pool(pool); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_v3_set_push_header(ngx_http_request_t *r, const char *name, + ngx_str_t *value) +{ + u_char *p; + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 push header \"%s\": \"%V\"", name, value); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + p = ngx_pnalloc(r->pool, value->len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, value->data, value->len); + p[value->len] = '\0'; + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->key.data = (u_char *) name; + h->key.len = ngx_strlen(name); + h->hash = ngx_hash_key(h->key.data, h->key.len); + h->lowcase_key = (u_char *) name; + h->value.data = p; + h->value.len = value->len; + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static void +ngx_http_v3_push_request_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + + c = ev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 push request handler"); + + ngx_http_process_request(r); +} + + +static ngx_chain_t * +ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, + uint64_t push_id) +{ + size_t n, len; + ngx_buf_t *b; + ngx_chain_t *hl, *cl; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 create push promise id:%uL", push_id); + + len = ngx_http_v3_encode_varlen_int(NULL, push_id); + + len += ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); + + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_METHOD_GET); + + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_AUTHORITY, + NULL, r->headers_in.server.len); + + if (path->len == 1 && path->data[0] == '/') { + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT); + + } else { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT, + NULL, path->len); + } + + if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTPS); + + } else if (r->schema.len == 4 + && ngx_strncmp(r->schema.data, "http", 4) == 0) + { + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTP); + + } else { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTP, + NULL, r->schema.len); + } + + if (r->headers_in.accept_encoding) { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, NULL, + r->headers_in.accept_encoding->value.len); + } + + if (r->headers_in.accept_language) { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, NULL, + r->headers_in.accept_language->value.len); + } + + if (r->headers_in.user_agent) { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_USER_AGENT, NULL, + r->headers_in.user_agent->value.len); + } + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NULL; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, push_id); + + b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, + 0, 0, 0); + + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_METHOD_GET); + + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_AUTHORITY, + r->headers_in.server.data, + r->headers_in.server.len); + + if (path->len == 1 && path->data[0] == '/') { + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT); + + } else { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT, + path->data, path->len); + } + + if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTPS); + + } else if (r->schema.len == 4 + && ngx_strncmp(r->schema.data, "http", 4) == 0) + { + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTP); + + } else { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTP, + r->schema.data, r->schema.len); + } + + if (r->headers_in.accept_encoding) { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, + r->headers_in.accept_encoding->value.data, + r->headers_in.accept_encoding->value.len); + } + + if (r->headers_in.accept_language) { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, + r->headers_in.accept_language->value.data, + r->headers_in.accept_language->value.len); + } + + if (r->headers_in.user_agent) { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_USER_AGENT, + r->headers_in.user_agent->value.data, + r->headers_in.user_agent->value.len); + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = b; + cl->next = NULL; + + n = b->last - b->pos; + + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_PUSH_PROMISE) + + ngx_http_v3_encode_varlen_int(NULL, n); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NULL; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_PUSH_PROMISE); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); + + hl = ngx_alloc_chain_link(r->pool); + if (hl == NULL) { + return NULL; + } + + hl->buf = b; + hl->next = cl; + + return hl; +} diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 2ac3f7d74..8d5147f4d 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -21,10 +21,19 @@ typedef struct { } ngx_http_v3_uni_stream_t; +typedef struct { + ngx_queue_t queue; + uint64_t id; + ngx_connection_t *connection; + ngx_uint_t *npushing; +} ngx_http_v3_push_t; + + static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); static void ngx_http_v3_read_uni_stream_type(ngx_event_t *rev); static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); +static void ngx_http_v3_push_cleanup(void *data); static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type); static ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); @@ -50,6 +59,7 @@ ngx_http_v3_init_connection(ngx_connection_t *c) h3c->hc = *hc; ngx_queue_init(&h3c->blocked); + ngx_queue_init(&h3c->pushing); c->data = h3c; return NGX_OK; @@ -321,6 +331,70 @@ ngx_http_v3_dummy_write_handler(ngx_event_t *wev) /* XXX async & buffered stream writes */ +ngx_connection_t * +ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 2]; + size_t n; + ngx_connection_t *sc; + ngx_pool_cleanup_t *cln; + ngx_http_v3_push_t *push; + ngx_http_v3_connection_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 create push stream id:%uL", push_id); + + sc = ngx_quic_open_stream(c, 0); + if (sc == NULL) { + return NULL; + } + + p = buf; + p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_STREAM_PUSH); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, push_id); + n = p - buf; + + if (sc->send(sc, buf, n) != (ssize_t) n) { + goto failed; + } + + cln = ngx_pool_cleanup_add(sc->pool, sizeof(ngx_http_v3_push_t)); + if (cln == NULL) { + goto failed; + } + + h3c = c->qs->parent->data; + h3c->npushing++; + + cln->handler = ngx_http_v3_push_cleanup; + + push = cln->data; + push->id = push_id; + push->connection = sc; + push->npushing = &h3c->npushing; + + ngx_queue_insert_tail(&h3c->pushing, &push->queue); + + return sc; + +failed: + + ngx_http_v3_close_uni_stream(sc); + + return NULL; +} + + +static void +ngx_http_v3_push_cleanup(void *data) +{ + ngx_http_v3_push_t *push = data; + + ngx_queue_remove(&push->queue); + (*push->npushing)--; +} + + static ngx_connection_t * ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) { @@ -682,3 +756,64 @@ ngx_http_v3_client_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) return NGX_OK; } + + +ngx_int_t +ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) +{ + ngx_http_v3_connection_t *h3c; + + h3c = c->qs->parent->data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 MAX_PUSH_ID:%uL", max_push_id); + + if (max_push_id < h3c->max_push_id) { + return NGX_HTTP_V3_ERR_ID_ERROR; + } + + h3c->max_push_id = max_push_id; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) +{ + ngx_queue_t *q; + ngx_http_request_t *r; + ngx_http_v3_push_t *push; + ngx_http_v3_connection_t *h3c; + + h3c = c->qs->parent->data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 CANCEL_PUSH:%uL", push_id); + + if (push_id >= h3c->next_push_id) { + return NGX_HTTP_V3_ERR_ID_ERROR; + } + + for (q = ngx_queue_head(&h3c->pushing); + q != ngx_queue_sentinel(&h3c->pushing); + q = ngx_queue_next(&h3c->pushing)) + { + push = (ngx_http_v3_push_t *) q; + + if (push->id != push_id) { + continue; + } + + r = push->connection->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 cancel push"); + + ngx_http_finalize_request(r, NGX_HTTP_CLOSE); + + break; + } + + return NGX_OK; +} -- cgit From 68c5d80ee5381db9ea20e2ef247153e300fe837c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 11 Aug 2020 10:41:39 +0300 Subject: QUIC: fixed ngx_http_test_reading() for QUIC streams. Previously this function generated an error trying to figure out if client shut down the write end of the connection. The reason for this error was that a QUIC stream has no socket descriptor. However checking for eof is not the right thing to do for an HTTP/3 QUIC stream since HTTP/3 clients are expected to shut down the write end of the stream after sending the request. Now the function handles QUIC streams separately. It checks if c->read->error is set. The error flags for c->read and c->write are now set for all streams when closing the QUIC connection instead of setting the pending_eof flag. --- src/http/ngx_http_request.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 54e5e2866..30a22fa22 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -3022,6 +3022,19 @@ ngx_http_test_reading(ngx_http_request_t *r) #endif +#if (NGX_HTTP_QUIC) + + if (c->qs) { + if (c->read->error) { + err = 0; + goto closed; + } + + return; + } + +#endif + #if (NGX_HAVE_KQUEUE) if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { -- cgit From 9a0fb643bfcea20f8a3ede222f9a1d8ec234a2cd Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 18 Aug 2020 17:11:32 +0300 Subject: HTTP/3: fixed context storage in request body parser. --- src/http/v3/ngx_http_v3_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index a0259be11..24ad771d6 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -392,7 +392,7 @@ ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, goto failed; } - r->h3_parse = st; + ctx->h3_parse = st; } if (ctx->size) { -- cgit From 7ce1a68aadec80a2c4a3b27f3fd3d7342043299b Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 18 Aug 2020 17:23:16 +0300 Subject: HTTP/3: request more client body bytes. Previously the request body DATA frame header was read by one byte because filters were called only when the requested number of bytes were read. Now, after 08ff2e10ae92 (1.19.2), filters are called after each read. More bytes can be read at once, which simplifies and optimizes the code. This also reduces diff with the default branch. --- src/http/ngx_http_request_body.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index 204253ca2..d0fcb00f4 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -1029,12 +1029,6 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) r->headers_in.content_length_n = 0; rb->rest = cscf->large_client_header_buffers.size; - -#if (NGX_HTTP_V3) - if (r->http_version == NGX_HTTP_VERSION_30) { - rb->rest = 1; - } -#endif } out = NULL; -- cgit From 5a4aaa6aed4d4b97d09548035298a90bf984a997 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 20 Aug 2020 12:33:00 +0300 Subject: HTTP/3: special handling of client errors in the upstream module. The function ngx_http_upstream_check_broken_connection() terminates the HTTP/1 request if client sends eof. For QUIC (including HTTP/3) the c->write->error flag is now checked instead. This flag is set when the entire QUIC connection is closed or STOP_SENDING was received from client. --- src/http/ngx_http_upstream.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 47f98ccb2..88e7b6fad 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -1345,6 +1345,19 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, } #endif +#if (NGX_HTTP_QUIC) + + if (c->qs) { + if (c->write->error) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + } + + return; + } + +#endif + #if (NGX_HAVE_KQUEUE) if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { -- cgit From 693e55a4b232afc6f7b4ba8e25f07decd86baa21 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 25 Aug 2020 12:45:21 +0300 Subject: HTTP/3: drop the unwanted remainder of the request. As per HTTP/3 draft 29, section 4.1: When the server does not need to receive the remainder of the request, it MAY abort reading the request stream, send a complete response, and cleanly close the sending part of the stream. --- src/http/ngx_http_request.c | 7 +++++++ src/http/ngx_http_request_body.c | 6 ++++++ 2 files changed, 13 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 7dbbcceb2..e322fae4b 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2819,6 +2819,13 @@ ngx_http_finalize_connection(ngx_http_request_t *r) } #endif +#if (NGX_HTTP_QUIC) + if (r->connection->qs) { + ngx_http_close_request(r, 0); + return; + } +#endif + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->main->count != 1) { diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index d0fcb00f4..7a9bbdd5d 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -579,6 +579,12 @@ ngx_http_discard_request_body(ngx_http_request_t *r) } #endif +#if (NGX_HTTP_QUIC) + if (r->connection->qs) { + return NGX_OK; + } +#endif + if (ngx_http_test_expect(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } -- cgit From e443b1244f60448805a873bf488a9d9f89bf7488 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 31 Aug 2020 18:42:26 +0300 Subject: HTTP/3: do not set the never-indexed literal bit by default. The "Literal Header Field Never Indexed" header field representation is not used in HTTP/2, and it makes little sense to make a distinction in HTTP/3. --- src/http/v3/ngx_http_v3_encode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_encode.c b/src/http/v3/ngx_http_v3_encode.c index a80310faf..b7bc3dd7c 100644 --- a/src/http/v3/ngx_http_v3_encode.c +++ b/src/http/v3/ngx_http_v3_encode.c @@ -145,7 +145,7 @@ ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, + len; } - *p = dynamic ? 0x60 : 0x70; + *p = dynamic ? 0x40 : 0x50; p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 4); *p = 0; @@ -171,7 +171,7 @@ ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, ngx_str_t *value) + value->len; } - *p = 0x30; + *p = 0x20; p = (u_char *) ngx_http_v3_encode_prefix_int(p, name->len, 3); ngx_strlow(p, name->data, name->len); @@ -213,7 +213,7 @@ ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, u_char *data, + len; } - *p = 0x08; + *p = 0; p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 3); *p = 0; -- cgit From 46173bd4b40023fd5e35bfe77b9ac2205e0c6bb0 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 16 Sep 2020 18:59:25 +0100 Subject: HTTP/3: fixed handling request body eof. While for HTTP/1 unexpected eof always means an error, for HTTP/3 an eof right after a DATA frame end means the end of the request body. For this reason, since adding HTTP/3 support, eof no longer produced an error right after recv() but was passed to filters which would make a decision. This decision was made in ngx_http_parse_chunked() and ngx_http_v3_parse_request_body() based on the b->last_buf flag. Now that since 0f7f1a509113 (1.19.2) rb->chunked->length is a lower threshold for the expected number of bytes, it can be set to zero to indicate that more bytes may or may not follow. Now it's possible to move the check for eof from parser functions to ngx_http_request_body_chunked_filter() and clean up the parsing code. Also, in the default branch, in case of eof, the following three things happened, which were replaced with returning NGX_ERROR while implementing HTTP/3: - "client prematurely closed connection" message was logged - c->error flag was set - NGX_HTTP_BAD_REQUEST was returned The change brings back this behavior for HTTP/1 as well as HTTP/3. --- src/http/ngx_http_parse.c | 5 ----- src/http/ngx_http_request_body.c | 30 +++++++++++++++++++++++------- src/http/v3/ngx_http_v3_request.c | 24 ++++++++++++------------ 3 files changed, 35 insertions(+), 24 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index a351f2b27..2015f56b2 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -2377,11 +2377,6 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, } } - if (b->last_buf) { - /* XXX client prematurely closed connection */ - return NGX_ERROR; - } - data: ctx->state = state; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index 7a9bbdd5d..a6dbcf502 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -960,6 +960,15 @@ ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) break; } + size = cl->buf->last - cl->buf->pos; + + if (cl->buf->last_buf && (off_t) size < rb->rest) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client prematurely closed connection"); + r->connection->error = 1; + return NGX_HTTP_BAD_REQUEST; + } + tl = ngx_chain_get_free_buf(r->pool, &rb->free); if (tl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -977,8 +986,6 @@ ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) b->end = cl->buf->end; b->flush = r->request_body_no_buffering; - size = cl->buf->last - cl->buf->pos; - if ((off_t) size < rb->rest) { cl->buf->pos = cl->buf->last; rb->rest -= size; @@ -990,11 +997,6 @@ ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) b->last_buf = 1; } - if (cl->buf->last_buf && rb->rest > 0) { - /* XXX client prematurely closed connection */ - return NGX_ERROR; - } - *ll = tl; ll = &tl->next; } @@ -1148,6 +1150,20 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) continue; } + if (rc == NGX_AGAIN && cl->buf->last_buf) { + + /* last body buffer */ + + if (rb->chunked->length > 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client prematurely closed connection"); + r->connection->error = 1; + return NGX_HTTP_BAD_REQUEST; + } + + rc = NGX_DONE; + } + if (rc == NGX_DONE) { /* a whole response has been parsed successfully */ diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 24ad771d6..fe3c79bf0 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -379,6 +379,10 @@ ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, ngx_int_t rc; ngx_connection_t *c; ngx_http_v3_parse_data_t *st; + enum { + sw_start = 0, + sw_skip + }; c = r->connection; st = ctx->h3_parse; @@ -395,12 +399,8 @@ ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, ctx->h3_parse = st; } - if (ctx->size) { - ctx->length = ctx->size + 1; - return (b->pos == b->last) ? NGX_AGAIN : NGX_OK; - } + while (b->pos < b->last && ctx->size == 0) { - while (b->pos < b->last) { rc = ngx_http_v3_parse_data(c, st, *b->pos++); if (rc > 0) { @@ -414,27 +414,27 @@ ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, } if (rc == NGX_AGAIN) { + ctx->state = sw_skip; continue; } /* rc == NGX_DONE */ ctx->size = st->length; - return NGX_OK; + ctx->state = sw_start; } - if (!b->last_buf) { + if (ctx->state == sw_skip) { ctx->length = 1; return NGX_AGAIN; } - if (st->state) { - goto failed; + if (b->pos == b->last) { + ctx->length = ctx->size; + return NGX_AGAIN; } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header done"); - - return NGX_DONE; + return NGX_OK; failed: -- cgit From d294369915461ba764426c709301b6c66ed33681 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 24 Aug 2020 09:56:36 +0300 Subject: HTTP/3: skip unknown frames on request stream. As per HTTP/3 draft 29, section 4.1: Frames of unknown types (Section 9), including reserved frames (Section 7.2.8) MAY be sent on a request or push stream before, after, or interleaved with other frames described in this section. Also, trailers frame is now used as an indication of the request body end. --- src/http/v3/ngx_http_v3_parse.c | 63 ++++++++++++++++++++++++++++++++------- src/http/v3/ngx_http_v3_parse.h | 2 ++ src/http/v3/ngx_http_v3_request.c | 6 +++- 3 files changed, 60 insertions(+), 11 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 1a7aa17f8..8f47b4d99 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -155,7 +155,9 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, ngx_int_t rc; enum { sw_start = 0, + sw_type, sw_length, + sw_skip, sw_prefix, sw_verify, sw_header_rep, @@ -168,10 +170,18 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers"); - if (ch != NGX_HTTP_V3_FRAME_HEADERS) { - return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + st->state = sw_type; + + /* fall through */ + + case sw_type: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + if (rc != NGX_DONE) { + return rc; } + st->type = st->vlint.value; st->state = sw_length; break; @@ -184,12 +194,26 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, st->length = st->vlint.value; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse headers len:%ui", st->length); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse headers type:%ui, len:%ui", + st->type, st->length); + + if (st->type != NGX_HTTP_V3_FRAME_HEADERS) { + st->state = st->length > 0 ? sw_skip : sw_type; + break; + } st->state = sw_prefix; break; + case sw_skip: + + if (--st->length == 0) { + st->state = sw_type; + } + + break; + case sw_prefix: if (st->length-- == 0) { @@ -1529,7 +1553,8 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, enum { sw_start = 0, sw_type, - sw_length + sw_length, + sw_skip }; switch (st->state) { @@ -1549,8 +1574,11 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, return rc; } - if (st->vlint.value != NGX_HTTP_V3_FRAME_DATA) { - return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + st->type = st->vlint.value; + + if (st->type == NGX_HTTP_V3_FRAME_HEADERS) { + /* trailers */ + goto done; } st->state = sw_length; @@ -1565,10 +1593,25 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, st->length = st->vlint.value; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse data frame len:%ui", st->length); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse data type:%ui, len:%ui", + st->type, st->length); - goto done; + if (st->type != NGX_HTTP_V3_FRAME_DATA && st->length > 0) { + st->state = sw_skip; + break; + } + + st->state = sw_type; + return NGX_OK; + + case sw_skip: + + if (--st->length == 0) { + st->state = sw_type; + } + + break; } return NGX_AGAIN; diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h index 0c0af33b7..856f021bd 100644 --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -76,6 +76,7 @@ typedef struct { typedef struct { ngx_uint_t state; + ngx_uint_t type; ngx_uint_t length; ngx_http_v3_parse_varlen_int_t vlint; ngx_http_v3_parse_header_block_prefix_t prefix; @@ -107,6 +108,7 @@ typedef struct { typedef struct { ngx_uint_t state; + ngx_uint_t type; ngx_uint_t length; ngx_http_v3_parse_varlen_int_t vlint; } ngx_http_v3_parse_data_t; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index fe3c79bf0..d9f4c9d55 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -418,7 +418,11 @@ ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, continue; } - /* rc == NGX_DONE */ + if (rc == NGX_DONE) { + return NGX_DONE; + } + + /* rc == NGX_OK */ ctx->size = st->length; ctx->state = sw_start; -- cgit From 9fff3b7516936ef663926f33c30b15b08ecfd7ae Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 16 Sep 2020 12:27:23 +0100 Subject: HTTP/3: reject HTTP/2 frames. As per HTTP/3 draft 30, section 7.2.8: Frame types that were used in HTTP/2 where there is no corresponding HTTP/3 frame have also been reserved (Section 11.2.1). These frame types MUST NOT be sent, and their receipt MUST be treated as a connection error of type H3_FRAME_UNEXPECTED. --- src/http/v3/ngx_http_v3_parse.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 8f47b4d99..96f87b0b6 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -10,6 +10,10 @@ #include +#define ngx_http_v3_is_v2_frame(type) \ + ((type) == 0x02 || (type) == 0x06 || (type) == 0x08 || (type) == 0x09) + + static ngx_int_t ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value); @@ -182,6 +186,11 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, } st->type = st->vlint.value; + + if (ngx_http_v3_is_v2_frame(st->type)) { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } + st->state = sw_length; break; @@ -986,6 +995,10 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse frame type:%ui", st->type); + if (ngx_http_v3_is_v2_frame(st->type)) { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } + if (st->state == sw_first_type && st->type != NGX_HTTP_V3_FRAME_SETTINGS) { @@ -1581,6 +1594,10 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, goto done; } + if (ngx_http_v3_is_v2_frame(st->type)) { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } + st->state = sw_length; break; -- cgit From ebbcc329cb5f9878d2eb27f53e74e951bdaa449d Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 16 Sep 2020 19:48:33 +0100 Subject: HTTP/3: removed HTTP/3 parser call from discard body filter. Request body discard is disabled for QUIC streams anyway. --- src/http/ngx_http_request_body.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index a6dbcf502..760a3cd05 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -804,16 +804,7 @@ ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b) for ( ;; ) { - switch (r->http_version) { -#if (NGX_HTTP_V3) - case NGX_HTTP_VERSION_30: - rc = ngx_http_v3_parse_request_body(r, b, rb->chunked); - break; -#endif - - default: /* HTTP/1.x */ - rc = ngx_http_parse_chunked(r, b, rb->chunked); - } + rc = ngx_http_parse_chunked(r, b, rb->chunked); if (rc == NGX_OK) { -- cgit From be719bbec8a9130223e25c4c22b52a73c44eb1ec Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 16 Sep 2020 20:21:03 +0100 Subject: HTTP/3: rearranged length check when parsing header. The new code looks simpler and is similar to other checks. --- src/http/v3/ngx_http_v3_parse.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 96f87b0b6..6ce5f10a7 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -212,6 +212,10 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, break; } + if (st->length == 0) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } + st->state = sw_prefix; break; @@ -225,7 +229,7 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, case sw_prefix: - if (st->length-- == 0) { + if (--st->length == 0) { return NGX_HTTP_V3_ERR_FRAME_ERROR; } @@ -234,10 +238,6 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, return rc; } - if (st->length == 0) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } - st->state = sw_verify; break; -- cgit From 0824d61fc9d28898e7d771825eca2880bc08df8b Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 23 Sep 2020 13:13:04 +0100 Subject: QUIC: unbreak client certificate verification after 0d2b2664b41c. Initially, client certificate verification didn't work due to the missing hc->ssl on a QUIC stream, which is started to be set in 7738:7f0981be07c4. Then it was lost in 7999:0d2b2664b41c introducing "quic" listen parameter. This change re-adds hc->ssl back for all QUIC connections, similar to SSL. --- src/http/ngx_http_request.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index e322fae4b..f1c6fa45c 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -308,6 +308,8 @@ ngx_http_init_connection(ngx_connection_t *c) ngx_quic_conf_t *qcf; ngx_http_ssl_srv_conf_t *sscf; + hc->ssl = 1; + #if (NGX_HTTP_V3) if (hc->addr_conf->http3) { -- cgit From 1f90fccd972d3395239a7f6dea733dfe3627abc5 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 29 Sep 2020 22:09:09 +0100 Subject: QUIC: switch stream context to a server selected by SNI. Previously the default server configuration context was used until the :authority or host header was parsed. This led to using the configuration parameters like client_header_buffer_size or request_pool_size from the default server rather than from the server selected by SNI. Also, the switch to the right server log is implemented. This issue manifested itself as QUIC stream being logged to the default server log until :authority or host is parsed. --- src/http/ngx_http_request.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index f1c6fa45c..2a8a22564 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -305,8 +305,10 @@ ngx_http_init_connection(ngx_connection_t *c) #if (NGX_HTTP_QUIC) if (hc->addr_conf->quic) { - ngx_quic_conf_t *qcf; - ngx_http_ssl_srv_conf_t *sscf; + ngx_quic_conf_t *qcf; + ngx_http_connection_t *phc; + ngx_http_ssl_srv_conf_t *sscf; + ngx_http_core_loc_conf_t *clcf; hc->ssl = 1; @@ -340,6 +342,17 @@ ngx_http_init_connection(ngx_connection_t *c) ngx_quic_run(c, &sscf->ssl, qcf); return; } + + phc = c->qs->parent->data; + + if (phc->ssl_servername) { + hc->ssl_servername = phc->ssl_servername; + hc->conf_ctx = phc->conf_ctx; + + clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, + ngx_http_core_module); + ngx_set_connection_log(c, clcf->error_log); + } } #endif -- cgit From f797a8a5b5a2012b0cae9745f05386b628365cb7 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Wed, 30 Sep 2020 20:54:46 +0300 Subject: QUIC: added stateless reset support. The new "quic_stateless_reset_token_key" directive is added. It sets the endpoint key used to generate stateless reset tokens and enables feature. If the endpoint receives short-header packet that can't be matched to existing connection, a stateless reset packet is generated with a proper token. If a valid stateless reset token is found in the incoming packet, the connection is closed. Example configuration: http { quic_stateless_reset_token_key "foo"; ... } --- src/http/modules/ngx_http_quic_module.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 9888e2eae..ec70c7286 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -125,6 +125,13 @@ static ngx_command_t ngx_http_quic_commands[] = { offsetof(ngx_quic_conf_t, retry), NULL }, + { ngx_string("quic_stateless_reset_token_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_quic_conf_t, sr_token_key), + NULL }, + ngx_null_command }; @@ -223,8 +230,10 @@ ngx_http_quic_create_srv_conf(ngx_conf_t *cf) * conf->tp.original_dcid = { 0, NULL }; * conf->tp.initial_scid = { 0, NULL }; * conf->tp.retry_scid = { 0, NULL }; - * conf->tp.stateless_reset_token = { 0 } + * conf->tp.sr_token = { 0 } + * conf->tp.sr_enabled = 0 * conf->tp.preferred_address = NULL + * conf->sr_token_key = { 0, NULL } */ conf->tp.max_idle_timeout = NGX_CONF_UNSET_MSEC; @@ -304,6 +313,8 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } } + ngx_conf_merge_str_value(conf->sr_token_key, prev->sr_token_key, ""); + return NGX_CONF_OK; } -- cgit From 0f843cfb74dd4dab7bff4d9a0f7e73b8b8cb61f0 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Thu, 1 Oct 2020 10:04:35 +0300 Subject: QUIC: moved ssl configuration pointer to quic configuration. The ssl configuration is obtained at config time and saved for future use. --- src/http/modules/ngx_http_quic_module.c | 5 +++++ src/http/ngx_http_request.c | 6 +----- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index ec70c7286..34898984a 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -262,6 +262,8 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_quic_conf_t *prev = parent; ngx_quic_conf_t *conf = child; + ngx_http_ssl_srv_conf_t *sscf; + ngx_conf_merge_msec_value(conf->tp.max_idle_timeout, prev->tp.max_idle_timeout, 60000); @@ -315,6 +317,9 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_str_value(conf->sr_token_key, prev->sr_token_key, ""); + sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); + conf->ssl = &sscf->ssl; + return NGX_CONF_OK; } diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 2a8a22564..b3e27c62e 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -307,7 +307,6 @@ ngx_http_init_connection(ngx_connection_t *c) if (hc->addr_conf->quic) { ngx_quic_conf_t *qcf; ngx_http_connection_t *phc; - ngx_http_ssl_srv_conf_t *sscf; ngx_http_core_loc_conf_t *clcf; hc->ssl = 1; @@ -336,10 +335,7 @@ ngx_http_init_connection(ngx_connection_t *c) qcf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_quic_module); - sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, - ngx_http_ssl_module); - - ngx_quic_run(c, &sscf->ssl, qcf); + ngx_quic_run(c, qcf); return; } -- cgit From 72b566cea5387644853def8230aa19e4c1e1990b Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 12 Oct 2020 14:00:00 +0100 Subject: QUIC: fixed ngx_http_upstream_init() much like HTTP/2 connections. --- src/http/ngx_http_upstream.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 53c77105b..419d936b8 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -523,6 +523,13 @@ ngx_http_upstream_init(ngx_http_request_t *r) } #endif +#if (NGX_HTTP_QUIC) + if (c->qs) { + ngx_http_upstream_init_request(r); + return; + } +#endif + if (c->read->timer_set) { ngx_del_timer(c->read); } -- cgit From e8277e42241a848b63d4af2a05ceec156642690c Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Tue, 15 Sep 2020 22:44:46 +0300 Subject: SSL: added the "ssl_keys_file" directive. --- src/http/modules/ngx_http_ssl_module.c | 21 +++++++++++++++++++++ src/http/modules/ngx_http_ssl_module.h | 2 ++ 2 files changed, 23 insertions(+) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 409514821..99dbd8ec6 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -119,6 +119,13 @@ static ngx_command_t ngx_http_ssl_commands[] = { 0, NULL }, + { ngx_string("ssl_keys_file"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, keys_file), + NULL }, + { ngx_string("ssl_dhparam"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -605,6 +612,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) * sscf->trusted_certificate = { 0, NULL }; * sscf->crl = { 0, NULL }; * sscf->ciphers = { 0, NULL }; + * sscf->keys_file = { 0, NULL }; * sscf->shm_zone = NULL; * sscf->ocsp_responder = { 0, NULL }; * sscf->stapling_file = { 0, NULL }; @@ -676,6 +684,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL); + ngx_conf_merge_str_value(conf->keys_file, prev->keys_file, ""); + ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate, @@ -912,6 +922,17 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } + if (conf->keys_file.len) { + + conf->ssl.keylog = ngx_conf_open_file(cf->cycle, &conf->keys_file); + + if (conf->ssl.keylog == NULL) { + return NGX_CONF_ERROR; + } + + SSL_CTX_set_keylog_callback(conf->ssl.ctx, ngx_ssl_keylogger); + } + if (conf->stapling) { if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file, diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h index 98aa1be40..aab185e05 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -36,6 +36,8 @@ typedef struct { ngx_array_t *certificates; ngx_array_t *certificate_keys; + ngx_str_t keys_file; + ngx_array_t *certificate_values; ngx_array_t *certificate_key_values; -- cgit From 743cc997811336b01109f83c659a67752015ffad Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Mon, 19 Oct 2020 10:32:53 +0300 Subject: QUIC: reverted previous 3 commits. Changes were intended for the test repository. --- src/http/modules/ngx_http_ssl_module.c | 21 --------------------- src/http/modules/ngx_http_ssl_module.h | 2 -- 2 files changed, 23 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 99dbd8ec6..409514821 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -119,13 +119,6 @@ static ngx_command_t ngx_http_ssl_commands[] = { 0, NULL }, - { ngx_string("ssl_keys_file"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_ssl_srv_conf_t, keys_file), - NULL }, - { ngx_string("ssl_dhparam"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -612,7 +605,6 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) * sscf->trusted_certificate = { 0, NULL }; * sscf->crl = { 0, NULL }; * sscf->ciphers = { 0, NULL }; - * sscf->keys_file = { 0, NULL }; * sscf->shm_zone = NULL; * sscf->ocsp_responder = { 0, NULL }; * sscf->stapling_file = { 0, NULL }; @@ -684,8 +676,6 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL); - ngx_conf_merge_str_value(conf->keys_file, prev->keys_file, ""); - ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate, @@ -922,17 +912,6 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - if (conf->keys_file.len) { - - conf->ssl.keylog = ngx_conf_open_file(cf->cycle, &conf->keys_file); - - if (conf->ssl.keylog == NULL) { - return NGX_CONF_ERROR; - } - - SSL_CTX_set_keylog_callback(conf->ssl.ctx, ngx_ssl_keylogger); - } - if (conf->stapling) { if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file, diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h index aab185e05..98aa1be40 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -36,8 +36,6 @@ typedef struct { ngx_array_t *certificates; ngx_array_t *certificate_keys; - ngx_str_t keys_file; - ngx_array_t *certificate_values; ngx_array_t *certificate_key_values; -- cgit From b19923f91bd41f17470c0d4538ba15adcc0b95e8 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Nov 2020 00:32:56 +0300 Subject: QUIC: multiple versions support in ALPN. Previously, a version based on NGX_QUIC_DRAFT_VERSION was always set. Now it is taken from the negotiated QUIC version that may differ. --- src/http/modules/ngx_http_quic_module.h | 5 ++--- src/http/modules/ngx_http_ssl_module.c | 35 +++++++++++++++++++++++++-------- src/http/v3/ngx_http_v3.h | 5 ++--- 3 files changed, 31 insertions(+), 14 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.h b/src/http/modules/ngx_http_quic_module.h index e744eb197..bd4930f8a 100644 --- a/src/http/modules/ngx_http_quic_module.h +++ b/src/http/modules/ngx_http_quic_module.h @@ -14,9 +14,8 @@ #include -#define NGX_HTTP_QUIC_ALPN(s) NGX_HTTP_QUIC_ALPN_DRAFT(s) -#define NGX_HTTP_QUIC_ALPN_DRAFT(s) "\x05hq-" #s -#define NGX_HTTP_QUIC_ALPN_ADVERTISE NGX_HTTP_QUIC_ALPN(NGX_QUIC_DRAFT_VERSION) +#define NGX_HTTP_QUIC_ALPN_ADVERTISE "\x02hq" +#define NGX_HTTP_QUIC_ALPN_DRAFT_FMT "\x05hq-%02uD" extern ngx_module_t ngx_http_quic_module; diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index a2db307f7..111de479b 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -418,6 +418,9 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { +#if (NGX_HTTP_QUIC) + const char *fmt; +#endif unsigned int srvlen; unsigned char *srv; #if (NGX_DEBUG) @@ -452,16 +455,32 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, } else #endif -#if (NGX_HTTP_V3) - if (hc->addr_conf->http3) { - srv = (unsigned char *) NGX_HTTP_V3_ALPN_ADVERTISE; - srvlen = sizeof(NGX_HTTP_V3_ALPN_ADVERTISE) - 1; - } else -#endif #if (NGX_HTTP_QUIC) if (hc->addr_conf->quic) { - srv = (unsigned char *) NGX_HTTP_QUIC_ALPN_ADVERTISE; - srvlen = sizeof(NGX_HTTP_QUIC_ALPN_ADVERTISE) - 1; +#if (NGX_HTTP_V3) + if (hc->addr_conf->http3) { + srv = (unsigned char *) NGX_HTTP_V3_ALPN_ADVERTISE; + srvlen = sizeof(NGX_HTTP_V3_ALPN_ADVERTISE) - 1; + fmt = NGX_HTTP_V3_ALPN_DRAFT_FMT; + + } else +#endif + { + srv = (unsigned char *) NGX_HTTP_QUIC_ALPN_ADVERTISE; + srvlen = sizeof(NGX_HTTP_QUIC_ALPN_ADVERTISE) - 1; + fmt = NGX_HTTP_QUIC_ALPN_DRAFT_FMT; + } + + /* QUIC draft */ + + if (ngx_quic_version(c) > 1) { + srv = ngx_pnalloc(c->pool, sizeof("\x05h3-xx") - 1); + if (srv == NULL) { + return SSL_TLSEXT_ERR_NOACK; + } + srvlen = ngx_sprintf(srv, fmt, ngx_quic_version(c)) - srv; + } + } else #endif { diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index aab27b3ac..c244ab861 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -15,9 +15,8 @@ #include -#define NGX_HTTP_V3_ALPN(s) NGX_HTTP_V3_ALPN_DRAFT(s) -#define NGX_HTTP_V3_ALPN_DRAFT(s) "\x05h3-" #s -#define NGX_HTTP_V3_ALPN_ADVERTISE NGX_HTTP_V3_ALPN(NGX_QUIC_DRAFT_VERSION) +#define NGX_HTTP_V3_ALPN_ADVERTISE "\x02h3" +#define NGX_HTTP_V3_ALPN_DRAFT_FMT "\x05h3-%02uD" #define NGX_HTTP_V3_VARLEN_INT_LEN 4 #define NGX_HTTP_V3_PREFIX_INT_LEN 11 -- cgit From 1be6d80089335f5f8635230f50a0ef4173ed9593 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 11 Nov 2020 11:57:50 +0000 Subject: QUIC: connection multiplexing per port. Also, connection migration within a single worker is implemented. --- src/http/modules/ngx_http_quic_module.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 34898984a..3993d4692 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -104,9 +104,9 @@ static ngx_command_t ngx_http_quic_commands[] = { offsetof(ngx_quic_conf_t, tp.ack_delay_exponent), &ngx_http_quic_ack_delay_exponent_bounds }, - { ngx_string("quic_active_migration"), + { ngx_string("quic_disable_active_migration"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_num_slot, + ngx_conf_set_flag_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_quic_conf_t, tp.disable_active_migration), NULL }, @@ -246,7 +246,7 @@ ngx_http_quic_create_srv_conf(ngx_conf_t *cf) conf->tp.initial_max_streams_bidi = NGX_CONF_UNSET_UINT; conf->tp.initial_max_streams_uni = NGX_CONF_UNSET_UINT; conf->tp.ack_delay_exponent = NGX_CONF_UNSET_UINT; - conf->tp.disable_active_migration = NGX_CONF_UNSET_UINT; + conf->tp.disable_active_migration = NGX_CONF_UNSET; conf->tp.active_connection_id_limit = NGX_CONF_UNSET_UINT; conf->retry = NGX_CONF_UNSET; @@ -301,8 +301,8 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) prev->tp.ack_delay_exponent, NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); - ngx_conf_merge_uint_value(conf->tp.disable_active_migration, - prev->tp.disable_active_migration, 1); + ngx_conf_merge_value(conf->tp.disable_active_migration, + prev->tp.disable_active_migration, 0); ngx_conf_merge_uint_value(conf->tp.active_connection_id_limit, prev->tp.active_connection_id_limit, 2); -- cgit From 2fd31c8959fbae8f069d09b61f339358214e75d1 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 10 Nov 2020 19:40:00 +0000 Subject: QUIC: renamed c->qs to c->quic. --- src/http/modules/ngx_http_quic_module.c | 2 +- src/http/ngx_http_request.c | 8 ++++---- src/http/ngx_http_request_body.c | 2 +- src/http/ngx_http_upstream.c | 4 ++-- src/http/v3/ngx_http_v3.h | 6 +++--- src/http/v3/ngx_http_v3_parse.c | 2 +- src/http/v3/ngx_http_v3_request.c | 6 +++--- src/http/v3/ngx_http_v3_streams.c | 18 +++++++++--------- src/http/v3/ngx_http_v3_tables.c | 16 ++++++++-------- 9 files changed, 32 insertions(+), 32 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 3993d4692..515d6c953 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -179,7 +179,7 @@ static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - if (r->connection->qs) { + if (r->connection->quic) { v->len = 4; v->valid = 1; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index f33555687..8df43891a 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -330,7 +330,7 @@ ngx_http_init_connection(ngx_connection_t *c) #endif - if (c->qs == NULL) { + if (c->quic == NULL) { c->log->connection = c->number; qcf = ngx_http_get_module_srv_conf(hc->conf_ctx, @@ -339,7 +339,7 @@ ngx_http_init_connection(ngx_connection_t *c) return; } - phc = c->qs->parent->data; + phc = c->quic->parent->data; if (phc->ssl_servername) { hc->ssl_servername = phc->ssl_servername; @@ -2847,7 +2847,7 @@ ngx_http_finalize_connection(ngx_http_request_t *r) #endif #if (NGX_HTTP_QUIC) - if (r->connection->qs) { + if (r->connection->quic) { ngx_http_close_request(r, 0); return; } @@ -3064,7 +3064,7 @@ ngx_http_test_reading(ngx_http_request_t *r) #if (NGX_HTTP_QUIC) - if (c->qs) { + if (c->quic) { if (c->read->error) { err = 0; goto closed; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index 760a3cd05..c8ed658cd 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -580,7 +580,7 @@ ngx_http_discard_request_body(ngx_http_request_t *r) #endif #if (NGX_HTTP_QUIC) - if (r->connection->qs) { + if (r->connection->quic) { return NGX_OK; } #endif diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 419d936b8..ceb98f140 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -524,7 +524,7 @@ ngx_http_upstream_init(ngx_http_request_t *r) #endif #if (NGX_HTTP_QUIC) - if (c->qs) { + if (c->quic) { ngx_http_upstream_init_request(r); return; } @@ -1354,7 +1354,7 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, #if (NGX_HTTP_QUIC) - if (c->qs) { + if (c->quic) { if (c->write->error) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_CLIENT_CLOSED_REQUEST); diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index c244ab861..949fc2206 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -77,11 +77,11 @@ #define ngx_http_v3_get_module_srv_conf(c, module) \ ngx_http_get_module_srv_conf( \ - ((ngx_http_v3_connection_t *) c->qs->parent->data)->hc.conf_ctx, \ - module) + ((ngx_http_v3_connection_t *) c->quic->parent->data)->hc.conf_ctx, \ + module) #define ngx_http_v3_finalize_connection(c, code, reason) \ - ngx_quic_finalize_connection(c->qs->parent, code, reason) + ngx_quic_finalize_connection(c->quic->parent, code, reason) typedef struct { diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 6ce5f10a7..d5ff3cb8f 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -279,7 +279,7 @@ done: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers done"); if (st->prefix.insert_count > 0) { - if (ngx_http_v3_client_ack_header(c, c->qs->id) != NGX_OK) { + if (ngx_http_v3_client_ack_header(c, c->quic->id) != NGX_OK) { return NGX_ERROR; } } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index d9f4c9d55..5511e3031 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -469,7 +469,7 @@ ngx_http_v3_create_header(ngx_http_request_t *r) out = NULL; ll = &out; - if ((c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0 + if ((c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0 && r->method != NGX_HTTP_HEAD) { if (ngx_http_v3_push_resources(r, &ll) != NGX_OK) { @@ -1123,7 +1123,7 @@ ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, ngx_http_v3_connection_t *h3c; c = r->connection; - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -1196,7 +1196,7 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, goto failed; } - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; ngx_memcpy(hc, h3c, sizeof(ngx_http_connection_t)); c->data = hc; diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 8d5147f4d..8ac048715 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -50,7 +50,7 @@ ngx_http_v3_init_connection(ngx_connection_t *c) hc = c->data; - if (c->qs == NULL) { + if (c->quic == NULL) { h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t)); if (h3c == NULL) { return NGX_ERROR; @@ -69,7 +69,7 @@ ngx_http_v3_init_connection(ngx_connection_t *c) return NGX_ERROR; } - if ((c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { + if ((c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { return NGX_OK; } @@ -101,7 +101,7 @@ ngx_http_v3_close_uni_stream(ngx_connection_t *c) ngx_http_v3_uni_stream_t *us; us = c->data; - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 close stream"); @@ -131,7 +131,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) c = rev->data; us = c->data; - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read stream type"); @@ -363,7 +363,7 @@ ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) goto failed; } - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; h3c->npushing++; cln->handler = ngx_http_v3_push_cleanup; @@ -419,7 +419,7 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) index = -1; } - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; if (index >= 0) { if (h3c->known_streams[index]) { @@ -476,7 +476,7 @@ ngx_http_v3_send_settings(ngx_connection_t *c) ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_connection_t *h3c; - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; if (h3c->settings_sent) { return NGX_OK; @@ -763,7 +763,7 @@ ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) { ngx_http_v3_connection_t *h3c; - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 MAX_PUSH_ID:%uL", max_push_id); @@ -786,7 +786,7 @@ ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) ngx_http_v3_push_t *push; ngx_http_v3_connection_t *h3c; - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 CANCEL_PUSH:%uL", push_id); diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index bf4f1449c..5389d0e2f 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -198,7 +198,7 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; dt = &h3c->table; ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -250,7 +250,7 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 set capacity %ui", capacity); - pc = c->qs->parent; + pc = c->quic->parent; h3c = pc->data; h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); @@ -324,7 +324,7 @@ ngx_http_v3_evict(ngx_connection_t *c, size_t need) ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; dt = &h3c->table; if (need > dt->capacity) { @@ -367,7 +367,7 @@ ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; dt = &h3c->table; if (dt->base + dt->nelts <= index) { @@ -463,7 +463,7 @@ ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; dt = &h3c->table; if (index < dt->base || index - dt->base >= dt->nelts) { @@ -506,7 +506,7 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) return NGX_OK; } - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; dt = &h3c->table; h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); @@ -555,7 +555,7 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; - pc = c->qs->parent; + pc = c->quic->parent; h3c = pc->data; dt = &h3c->table; @@ -636,7 +636,7 @@ ngx_http_v3_new_header(ngx_connection_t *c) ngx_http_v3_block_t *block; ngx_http_v3_connection_t *h3c; - h3c = c->qs->parent->data; + h3c = c->quic->parent->data; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 new dynamic header, blocked:%ui", h3c->nblocked); -- cgit From 5bbc3f1967a8ac1cce0f16b428f156301b81beb9 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 11 Nov 2020 21:08:48 +0000 Subject: QUIC: generate default stateless reset token key. Previously, if quic_stateless_reset_token_key was empty or unspecified, initial stateless reset token was not generated. However subsequent tokens were generated with empty key, which resulted in error with certain SSL libraries, for example OpenSSL. Now a random 32-byte stateless reset token key is generated if none is specified in the configuration. As a result, stateless reset tokens are now generated for all server ids. --- src/http/modules/ngx_http_quic_module.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 515d6c953..ff79cdc8d 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -317,6 +317,19 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_str_value(conf->sr_token_key, prev->sr_token_key, ""); + if (conf->sr_token_key.len == 0) { + conf->sr_token_key.len = NGX_QUIC_DEFAULT_SRT_KEY_LEN; + + conf->sr_token_key.data = ngx_pnalloc(cf->pool, conf->sr_token_key.len); + if (conf->sr_token_key.data == NULL) { + return NGX_CONF_ERROR; + } + + if (RAND_bytes(conf->sr_token_key.data, conf->sr_token_key.len) <= 0) { + return NGX_CONF_ERROR; + } + } + sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); conf->ssl = &sscf->ssl; -- cgit From eb8f476d599306507300a82b4a26d2a2476b748c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 10 Nov 2020 20:42:45 +0000 Subject: Fixed generating chunked response after 46e3542d51b3. If trailers were missing and a chain carrying the last_buf flag had no data in it, then last HTTP/1 chunk was broken. The problem was introduced while implementing HTTP/3 response body generation. The change fixes the issue and reduces diff to the mainline nginx. --- src/http/modules/ngx_http_chunked_filter_module.c | 45 +++++++++++++---------- 1 file changed, 26 insertions(+), 19 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c index 87b032496..371559e2f 100644 --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -18,7 +18,7 @@ typedef struct { static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r, - ngx_http_chunked_filter_ctx_t *ctx, size_t size); + ngx_http_chunked_filter_ctx_t *ctx); static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { @@ -204,8 +204,25 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) out = tl; } +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + + if (cl->buf->last_buf) { + tl = ngx_http_v3_create_trailers(r); + if (tl == NULL) { + return NGX_ERROR; + } + + cl->buf->last_buf = 0; + + *ll = tl; + } + + } else +#endif + if (cl->buf->last_buf) { - tl = ngx_http_chunked_create_trailers(r, ctx, size); + tl = ngx_http_chunked_create_trailers(r, ctx); if (tl == NULL) { return NGX_ERROR; } @@ -214,12 +231,11 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) *ll = tl; - } else if (size > 0 -#if (NGX_HTTP_V3) - && r->http_version != NGX_HTTP_VERSION_30 -#endif - ) - { + if (size == 0) { + tl->buf->pos += 2; + } + + } else if (size > 0) { tl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (tl == NULL) { return NGX_ERROR; @@ -250,7 +266,7 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) static ngx_chain_t * ngx_http_chunked_create_trailers(ngx_http_request_t *r, - ngx_http_chunked_filter_ctx_t *ctx, size_t size) + ngx_http_chunked_filter_ctx_t *ctx) { size_t len; ngx_buf_t *b; @@ -259,12 +275,6 @@ ngx_http_chunked_create_trailers(ngx_http_request_t *r, ngx_list_part_t *part; ngx_table_elt_t *header; -#if (NGX_HTTP_V3) - if (r->http_version == NGX_HTTP_VERSION_30) { - return ngx_http_v3_create_trailers(r); - } -#endif - len = 0; part = &r->headers_out.trailers.part; @@ -317,10 +327,7 @@ ngx_http_chunked_create_trailers(ngx_http_request_t *r, b->last = b->pos; - if (size > 0) { - *b->last++ = CR; *b->last++ = LF; - } - + *b->last++ = CR; *b->last++ = LF; *b->last++ = '0'; *b->last++ = CR; *b->last++ = LF; -- cgit From 49f0b0d99d70fa4e895b939a320c29df28a34fff Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 17 Nov 2020 21:12:36 +0000 Subject: HTTP/3: finalize chunked response body chain with NULL. Unfinalized chain could result in segfault. The problem was introduced in ef83990f0e25. Patch by Andrey Kolyshkin. --- src/http/modules/ngx_http_chunked_filter_module.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c index 371559e2f..138369815 100644 --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -216,6 +216,9 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) cl->buf->last_buf = 0; *ll = tl; + + } else { + *ll = NULL; } } else -- cgit From 9129fb3db9e2f9899161e9573e7a19c774a0df6a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 17 Nov 2020 20:54:10 +0000 Subject: HTTP/3: null-terminate empty header value. Header value returned from the HTTP parser is expected to be null-terminated or have a spare byte after the value bytes. When an empty header value was passed by client in a literal header representation, neither was true. This could result in segfault. The fix is to assign a literal empty null-terminated string in this case. Thanks to Andrey Kolyshkin. --- src/http/v3/ngx_http_v3_parse.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index d5ff3cb8f..afe442464 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -746,6 +746,7 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, st->literal.length = st->pint.value; if (st->literal.length == 0) { + st->value.data = (u_char *) ""; goto done; } -- cgit From 7cfc5eb11fbfe3beb9d793dbacae70fb658d46c2 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 25 Nov 2020 17:57:43 +0000 Subject: HTTP/3: eliminated r->method_start. The field was introduced to ease parsing HTTP/3 requests. The change reduces diff to the default branch. --- src/http/ngx_http_parse.c | 7 +------ src/http/ngx_http_request.c | 8 ++++++-- src/http/ngx_http_request.h | 1 - src/http/v3/ngx_http_v3_request.c | 15 +++++++-------- 4 files changed, 14 insertions(+), 17 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 2015f56b2..f8ec03c50 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -145,7 +145,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) case sw_start: r->parse_start = p; r->request_start = p; - r->method_start = p; if (ch == CR || ch == LF) { break; @@ -160,7 +159,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) case sw_method: if (ch == ' ') { - r->method_end = p; + r->method_end = p - 1; m = r->request_start; switch (p - m) { @@ -833,10 +832,6 @@ done: r->request_end = p; } - if (r->http_protocol.data) { - r->http_protocol.len = r->request_end - r->http_protocol.data; - } - r->http_version = r->http_major * 1000 + r->http_minor; r->state = sw_start; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 3b9e59005..a60c5758f 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1162,8 +1162,12 @@ ngx_http_process_request_line(ngx_event_t *rev) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http request line: \"%V\"", &r->request_line); - r->method_name.len = r->method_end - r->method_start; - r->method_name.data = r->method_start; + r->method_name.len = r->method_end - r->request_start + 1; + r->method_name.data = r->request_line.data; + + if (r->http_protocol.data) { + r->http_protocol.len = r->request_end - r->http_protocol.data; + } if (ngx_http_process_request_uri(r) != NGX_OK) { break; diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 3d33d93d4..9a610e805 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -585,7 +585,6 @@ struct ngx_http_request_s { u_char *args_start; u_char *request_start; u_char *request_end; - u_char *method_start; u_char *method_end; u_char *schema_start; u_char *schema_end; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 5511e3031..2ff0440d9 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -129,11 +129,9 @@ ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) continue; } - ngx_str_set(&r->http_protocol, "HTTP/3.0"); - - len = (r->method_end - r->method_start) + 1 + len = r->method_name.len + 1 + (r->uri_end - r->uri_start) + 1 - + sizeof("HTTP/3") - 1; + + sizeof("HTTP/3.0") - 1; p = ngx_pnalloc(c->pool, len); if (p == NULL) { @@ -142,11 +140,13 @@ ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) r->request_start = p; - p = ngx_cpymem(p, r->method_start, r->method_end - r->method_start); + p = ngx_cpymem(p, r->method_name.data, r->method_name.len); + r->method_end = p - 1; *p++ = ' '; p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start); *p++ = ' '; - p = ngx_cpymem(p, "HTTP/3", sizeof("HTTP/3") - 1); + r->http_protocol.data = p; + p = ngx_cpymem(p, "HTTP/3.0", sizeof("HTTP/3.0") - 1); r->request_end = p; r->state = 0; @@ -309,8 +309,7 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, c = r->connection; if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) { - r->method_start = value->data; - r->method_end = value->data + value->len; + r->method_name = *value; for (i = 0; i < sizeof(ngx_http_v3_methods) / sizeof(ngx_http_v3_methods[0]); i++) -- cgit From 4b440cbf97af3ffe0ab31cb083fb1ce5b0fb5f89 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 27 Nov 2020 17:46:21 +0000 Subject: HTTP/3: introduced ngx_http_v3_filter. The filter is responsible for creating HTTP/3 response header and body. The change removes differences to the default branch for ngx_http_chunked_filter_module and ngx_http_header_filter_module. --- src/http/modules/ngx_http_chunked_filter_module.c | 50 +- src/http/ngx_http_header_filter_module.c | 28 +- src/http/v3/ngx_http_v3.h | 2 - src/http/v3/ngx_http_v3_filter_module.c | 1360 +++++++++++++++++++++ src/http/v3/ngx_http_v3_request.c | 1133 ----------------- 5 files changed, 1369 insertions(+), 1204 deletions(-) create mode 100644 src/http/v3/ngx_http_v3_filter_module.c (limited to 'src/http') diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c index 138369815..4d6fd3eed 100644 --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -106,7 +106,6 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { u_char *chunk; off_t size; - size_t n; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *out, *cl, *tl, **ll; @@ -162,68 +161,27 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) chunk = b->start; if (chunk == NULL) { + /* the "0000000000000000" is 64-bit hexadecimal string */ -#if (NGX_HTTP_V3) - if (r->http_version == NGX_HTTP_VERSION_30) { - n = NGX_HTTP_V3_VARLEN_INT_LEN * 2; - - } else -#endif - { - /* the "0000000000000000" is 64-bit hexadecimal string */ - n = sizeof("0000000000000000" CRLF) - 1; - } - - chunk = ngx_palloc(r->pool, n); + chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1); if (chunk == NULL) { return NGX_ERROR; } b->start = chunk; - b->end = chunk + n; + b->end = chunk + sizeof("0000000000000000" CRLF) - 1; } b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; b->memory = 0; b->temporary = 1; b->pos = chunk; - -#if (NGX_HTTP_V3) - if (r->http_version == NGX_HTTP_VERSION_30) { - b->last = (u_char *) ngx_http_v3_encode_varlen_int(chunk, - NGX_HTTP_V3_FRAME_DATA); - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, size); - - } else -#endif - { - b->last = ngx_sprintf(chunk, "%xO" CRLF, size); - } + b->last = ngx_sprintf(chunk, "%xO" CRLF, size); tl->next = out; out = tl; } -#if (NGX_HTTP_V3) - if (r->http_version == NGX_HTTP_VERSION_30) { - - if (cl->buf->last_buf) { - tl = ngx_http_v3_create_trailers(r); - if (tl == NULL) { - return NGX_ERROR; - } - - cl->buf->last_buf = 0; - - *ll = tl; - - } else { - *ll = NULL; - } - - } else -#endif - if (cl->buf->last_buf) { tl = ngx_http_chunked_create_trailers(r, ctx); if (tl == NULL) { diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c index 85edf3657..9b8940590 100644 --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -187,29 +187,6 @@ ngx_http_header_filter(ngx_http_request_t *r) r->header_only = 1; } - if (r->headers_out.status_line.len == 0) { - if (r->headers_out.status == NGX_HTTP_NO_CONTENT - || r->headers_out.status == NGX_HTTP_NOT_MODIFIED) - { - r->header_only = 1; - } - } - -#if (NGX_HTTP_V3) - - if (r->http_version == NGX_HTTP_VERSION_30) { - ngx_chain_t *cl; - - cl = ngx_http_v3_create_header(r); - if (cl == NULL) { - return NGX_ERROR; - } - - return ngx_http_write_filter(r, cl); - } - -#endif - if (r->headers_out.last_modified_time != -1) { if (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT @@ -243,6 +220,7 @@ ngx_http_header_filter(ngx_http_request_t *r) /* 2XX */ if (status == NGX_HTTP_NO_CONTENT) { + r->header_only = 1; ngx_str_null(&r->headers_out.content_type); r->headers_out.last_modified_time = -1; r->headers_out.last_modified = NULL; @@ -259,6 +237,10 @@ ngx_http_header_filter(ngx_http_request_t *r) { /* 3XX */ + if (status == NGX_HTTP_NOT_MODIFIED) { + r->header_only = 1; + } + status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX; status_line = &ngx_http_status_lines[status]; len += ngx_http_status_lines[status].len; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 949fc2206..89ed9c98a 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -140,8 +140,6 @@ ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t allow_underscores); ngx_int_t ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, ngx_http_chunked_t *ctx); -ngx_chain_t *ngx_http_v3_create_header(ngx_http_request_t *r); -ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r); uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c new file mode 100644 index 000000000..9a2313a14 --- /dev/null +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -0,0 +1,1360 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +/* static table indices */ +#define NGX_HTTP_V3_HEADER_AUTHORITY 0 +#define NGX_HTTP_V3_HEADER_PATH_ROOT 1 +#define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO 4 +#define NGX_HTTP_V3_HEADER_DATE 6 +#define NGX_HTTP_V3_HEADER_LAST_MODIFIED 10 +#define NGX_HTTP_V3_HEADER_LOCATION 12 +#define NGX_HTTP_V3_HEADER_METHOD_GET 17 +#define NGX_HTTP_V3_HEADER_SCHEME_HTTP 22 +#define NGX_HTTP_V3_HEADER_SCHEME_HTTPS 23 +#define NGX_HTTP_V3_HEADER_STATUS_200 25 +#define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING 31 +#define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 +#define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING 59 +#define NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE 72 +#define NGX_HTTP_V3_HEADER_SERVER 92 +#define NGX_HTTP_V3_HEADER_USER_AGENT 95 + + +typedef struct { + ngx_chain_t *free; + ngx_chain_t *busy; +} ngx_http_v3_filter_ctx_t; + + +static ngx_int_t ngx_http_v3_header_filter(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_push_resources(ngx_http_request_t *r, + ngx_chain_t ***out); +static ngx_int_t ngx_http_v3_push_resource(ngx_http_request_t *r, + ngx_str_t *path, ngx_chain_t ***out); +static ngx_int_t ngx_http_v3_create_push_request( + ngx_http_request_t *pr, ngx_str_t *path, uint64_t push_id); +static ngx_int_t ngx_http_v3_set_push_header(ngx_http_request_t *r, + const char *name, ngx_str_t *value); +static void ngx_http_v3_push_request_handler(ngx_event_t *ev); +static ngx_chain_t *ngx_http_v3_create_push_promise(ngx_http_request_t *r, + ngx_str_t *path, uint64_t push_id); +static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r, + ngx_chain_t *in); +static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_filter_init(ngx_conf_t *cf); + + +static ngx_http_module_t ngx_http_v3_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_v3_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_v3_filter_module = { + NGX_MODULE_V1, + &ngx_http_v3_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; + + +static ngx_int_t +ngx_http_v3_header_filter(ngx_http_request_t *r) +{ + u_char *p; + size_t len, n; + ngx_buf_t *b; + ngx_str_t host; + ngx_uint_t i, port; + ngx_chain_t *out, *hl, *cl, **ll; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_connection_t *c; + ngx_http_v3_filter_ctx_t *ctx; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + u_char addr[NGX_SOCKADDR_STRLEN]; + + if (r->http_version != NGX_HTTP_VERSION_30) { + return ngx_http_next_header_filter(r); + } + + if (r->header_sent) { + return NGX_OK; + } + + r->header_sent = 1; + + if (r != r->main) { + return NGX_OK; + } + + if (r->method == NGX_HTTP_HEAD) { + r->header_only = 1; + } + + if (r->headers_out.last_modified_time != -1) { + if (r->headers_out.status != NGX_HTTP_OK + && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT + && r->headers_out.status != NGX_HTTP_NOT_MODIFIED) + { + r->headers_out.last_modified_time = -1; + r->headers_out.last_modified = NULL; + } + } + + if (r->headers_out.status == NGX_HTTP_NO_CONTENT) { + r->header_only = 1; + ngx_str_null(&r->headers_out.content_type); + r->headers_out.last_modified_time = -1; + r->headers_out.last_modified = NULL; + r->headers_out.content_length = NULL; + r->headers_out.content_length_n = -1; + } + + if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { + r->header_only = 1; + } + + c = r->connection; + + out = NULL; + ll = &out; + + if ((c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0 + && r->method != NGX_HTTP_HEAD) + { + if (ngx_http_v3_push_resources(r, &ll) != NGX_OK) { + return NGX_ERROR; + } + } + + len = ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); + + if (r->headers_out.status == NGX_HTTP_OK) { + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_STATUS_200); + + } else { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_STATUS_200, + NULL, 3); + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->headers_out.server == NULL) { + if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { + n = sizeof(NGINX_VER) - 1; + + } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { + n = sizeof(NGINX_VER_BUILD) - 1; + + } else { + n = sizeof("nginx") - 1; + } + + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_SERVER, + NULL, n); + } + + if (r->headers_out.date == NULL) { + len += ngx_http_v3_encode_header_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE, + NULL, ngx_cached_http_time.len); + } + + if (r->headers_out.content_type.len) { + n = r->headers_out.content_type.len; + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + n += sizeof("; charset=") - 1 + r->headers_out.charset.len; + } + + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, + NULL, n); + } + + if (r->headers_out.content_length == NULL) { + if (r->headers_out.content_length_n > 0) { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, + NULL, NGX_OFF_T_LEN); + + } else if (r->headers_out.content_length_n == 0) { + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); + } + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, + sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); + } + + if (r->headers_out.location + && r->headers_out.location->value.len + && r->headers_out.location->value.data[0] == '/' + && clcf->absolute_redirect) + { + r->headers_out.location->hash = 0; + + if (clcf->server_name_in_redirect) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + host = cscf->server_name; + + } else if (r->headers_in.server.len) { + host = r->headers_in.server; + + } else { + host.len = NGX_SOCKADDR_STRLEN; + host.data = addr; + + if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + port = ngx_inet_get_port(c->local_sockaddr); + + n = sizeof("https://") - 1 + host.len + + r->headers_out.location->value.len; + + if (clcf->port_in_redirect) { + port = (port == 443) ? 0 : port; + + } else { + port = 0; + } + + if (port) { + n += sizeof(":65535") - 1; + } + + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_LOCATION, NULL, n); + + } else { + ngx_str_null(&host); + port = 0; + } + +#if (NGX_HTTP_GZIP) + if (r->gzip_vary) { + if (clcf->gzip_vary) { + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); + + } else { + r->gzip_vary = 0; + } + } +#endif + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += ngx_http_v3_encode_header_l(NULL, &header[i].key, + &header[i].value); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, + 0, 0, 0); + + if (r->headers_out.status == NGX_HTTP_OK) { + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_STATUS_200); + + } else { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_STATUS_200, + NULL, 3); + b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); + } + + if (r->headers_out.server == NULL) { + if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { + p = (u_char *) NGINX_VER; + n = sizeof(NGINX_VER) - 1; + + } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { + p = (u_char *) NGINX_VER_BUILD; + n = sizeof(NGINX_VER_BUILD) - 1; + + } else { + p = (u_char *) "nginx"; + n = sizeof("nginx") - 1; + } + + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_SERVER, + p, n); + } + + if (r->headers_out.date == NULL) { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_DATE, + ngx_cached_http_time.data, + ngx_cached_http_time.len); + } + + if (r->headers_out.content_type.len) { + n = r->headers_out.content_type.len; + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + n += sizeof("; charset=") - 1 + r->headers_out.charset.len; + } + + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, + NULL, n); + + p = b->last; + b->last = ngx_cpymem(b->last, r->headers_out.content_type.data, + r->headers_out.content_type.len); + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + b->last = ngx_cpymem(b->last, "; charset=", + sizeof("; charset=") - 1); + b->last = ngx_cpymem(b->last, r->headers_out.charset.data, + r->headers_out.charset.len); + + /* update r->headers_out.content_type for possible logging */ + + r->headers_out.content_type.len = b->last - p; + r->headers_out.content_type.data = p; + } + } + + if (r->headers_out.content_length == NULL) { + if (r->headers_out.content_length_n > 0) { + p = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); + n = p - b->last; + + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, + NULL, n); + + b->last = ngx_sprintf(b->last, "%O", + r->headers_out.content_length_n); + + } else if (r->headers_out.content_length_n == 0) { + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); + } + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, + sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); + + b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); + } + + if (host.data) { + n = sizeof("https://") - 1 + host.len + + r->headers_out.location->value.len; + + if (port) { + n += ngx_sprintf(b->last, ":%ui", port) - b->last; + } + + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_LOCATION, + NULL, n); + + p = b->last; + b->last = ngx_cpymem(b->last, "https://", sizeof("https://") - 1); + b->last = ngx_cpymem(b->last, host.data, host.len); + + if (port) { + b->last = ngx_sprintf(b->last, ":%ui", port); + } + + b->last = ngx_cpymem(b->last, r->headers_out.location->value.data, + r->headers_out.location->value.len); + + /* update r->headers_out.location->value for possible logging */ + + r->headers_out.location->value.len = b->last - p; + r->headers_out.location->value.data = p; + ngx_str_set(&r->headers_out.location->key, "Location"); + } + +#if (NGX_HTTP_GZIP) + if (r->gzip_vary) { + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); + } +#endif + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + b->last = (u_char *) ngx_http_v3_encode_header_l(b->last, + &header[i].key, + &header[i].value); + } + + if (r->header_only) { + b->last_buf = 1; + } + + cl = ngx_alloc_chain_link(c->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + n = b->last - b->pos; + + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) + + ngx_http_v3_encode_varlen_int(NULL, n); + + b = ngx_create_temp_buf(c->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_HEADERS); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); + + hl = ngx_alloc_chain_link(c->pool); + if (hl == NULL) { + return NGX_ERROR; + } + + hl->buf = b; + hl->next = cl; + + *ll = hl; + ll = &cl->next; + + if (r->headers_out.content_length_n >= 0 && !r->header_only) { + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA) + + ngx_http_v3_encode_varlen_int(NULL, + r->headers_out.content_length_n); + + b = ngx_create_temp_buf(c->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_DATA); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + r->headers_out.content_length_n); + + cl = ngx_alloc_chain_link(c->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + *ll = cl; + + } else { + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_v3_filter_module); + } + + return ngx_http_write_filter(r, out); +} + + +static ngx_int_t +ngx_http_v3_push_resources(ngx_http_request_t *r, ngx_chain_t ***out) +{ + u_char *start, *end, *last; + ngx_str_t path; + ngx_int_t rc; + ngx_uint_t i, push; + ngx_table_elt_t **h; + ngx_http_v3_loc_conf_t *h3lcf; + ngx_http_complex_value_t *pushes; + + h3lcf = ngx_http_get_module_loc_conf(r, ngx_http_v3_module); + + if (h3lcf->pushes) { + pushes = h3lcf->pushes->elts; + + for (i = 0; i < h3lcf->pushes->nelts; i++) { + + if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) { + return NGX_ERROR; + } + + if (path.len == 0) { + continue; + } + + if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) { + continue; + } + + rc = ngx_http_v3_push_resource(r, &path, out); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_ABORT) { + return NGX_OK; + } + + /* NGX_OK, NGX_DECLINED */ + } + } + + if (!h3lcf->push_preload) { + return NGX_OK; + } + + h = r->headers_out.link.elts; + + for (i = 0; i < r->headers_out.link.nelts; i++) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 parse link: \"%V\"", &h[i]->value); + + start = h[i]->value.data; + end = h[i]->value.data + h[i]->value.len; + + next_link: + + while (start < end && *start == ' ') { start++; } + + if (start == end || *start++ != '<') { + continue; + } + + while (start < end && *start == ' ') { start++; } + + for (last = start; last < end && *last != '>'; last++) { + /* void */ + } + + if (last == start || last == end) { + continue; + } + + path.len = last - start; + path.data = start; + + start = last + 1; + + while (start < end && *start == ' ') { start++; } + + if (start == end) { + continue; + } + + if (*start == ',') { + start++; + goto next_link; + } + + if (*start++ != ';') { + continue; + } + + last = ngx_strlchr(start, end, ','); + + if (last == NULL) { + last = end; + } + + push = 0; + + for ( ;; ) { + + while (start < last && *start == ' ') { start++; } + + if (last - start >= 6 + && ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0) + { + start += 6; + + if (start == last || *start == ' ' || *start == ';') { + push = 0; + break; + } + + goto next_param; + } + + if (last - start >= 11 + && ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0) + { + start += 11; + + if (start == last || *start == ' ' || *start == ';') { + push = 1; + } + + goto next_param; + } + + if (last - start >= 4 + && ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0) + { + start += 4; + + while (start < last && *start == ' ') { start++; } + + if (start == last || *start++ != '"') { + goto next_param; + } + + for ( ;; ) { + + while (start < last && *start == ' ') { start++; } + + if (last - start >= 7 + && ngx_strncasecmp(start, (u_char *) "preload", 7) == 0) + { + start += 7; + + if (start < last && (*start == ' ' || *start == '"')) { + push = 1; + break; + } + } + + while (start < last && *start != ' ' && *start != '"') { + start++; + } + + if (start == last) { + break; + } + + if (*start == '"') { + break; + } + + start++; + } + } + + next_param: + + start = ngx_strlchr(start, last, ';'); + + if (start == NULL) { + break; + } + + start++; + } + + if (push) { + while (path.len && path.data[path.len - 1] == ' ') { + path.len--; + } + } + + if (push && path.len + && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/')) + { + rc = ngx_http_v3_push_resource(r, &path, out); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_ABORT) { + return NGX_OK; + } + + /* NGX_OK, NGX_DECLINED */ + } + + if (last < end) { + start = last + 1; + goto next_link; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, + ngx_chain_t ***ll) +{ + uint64_t push_id; + ngx_int_t rc; + ngx_chain_t *cl; + ngx_connection_t *c; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_connection_t *h3c; + + c = r->connection; + h3c = c->quic->parent->data; + h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 push \"%V\" pushing:%ui/%ui id:%uL/%uL", + path, h3c->npushing, h3scf->max_concurrent_pushes, + h3c->next_push_id, h3c->max_push_id); + + if (!ngx_path_separator(path->data[0])) { + ngx_log_error(NGX_LOG_WARN, c->log, 0, + "non-absolute path \"%V\" not pushed", path); + return NGX_DECLINED; + } + + if (h3c->next_push_id > h3c->max_push_id) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 abort pushes due to max_push_id"); + return NGX_ABORT; + } + + if (h3c->npushing >= h3scf->max_concurrent_pushes) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 abort pushes due to max_concurrent_pushes"); + return NGX_ABORT; + } + + push_id = h3c->next_push_id++; + + rc = ngx_http_v3_create_push_request(r, path, push_id); + if (rc != NGX_OK) { + return rc; + } + + cl = ngx_http_v3_create_push_promise(r, path, push_id); + if (cl == NULL) { + return NGX_ERROR; + } + + for (**ll = cl; **ll; *ll = &(**ll)->next); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, + uint64_t push_id) +{ + ngx_pool_t *pool; + ngx_connection_t *c, *pc; + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + ngx_http_v3_connection_t *h3c; + + pc = pr->connection; + + r = NULL; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "http3 create push request id:%uL", push_id); + + c = ngx_http_v3_create_push_stream(pc, push_id); + if (c == NULL) { + return NGX_ABORT; + } + + hc = ngx_palloc(c->pool, sizeof(ngx_http_connection_t)); + if (hc == NULL) { + goto failed; + } + + h3c = c->quic->parent->data; + ngx_memcpy(hc, h3c, sizeof(ngx_http_connection_t)); + c->data = hc; + + ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); + if (ctx == NULL) { + goto failed; + } + + ctx->connection = c; + ctx->request = NULL; + ctx->current_request = NULL; + + c->log->handler = ngx_http_log_error; + c->log->data = ctx; + c->log->action = "processing pushed request headers"; + + c->log_error = NGX_ERROR_INFO; + + r = ngx_http_create_request(c); + if (r == NULL) { + goto failed; + } + + c->data = r; + + ngx_str_set(&r->http_protocol, "HTTP/3.0"); + + r->method_name = ngx_http_core_get_method; + r->method = NGX_HTTP_GET; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + r->header_in = ngx_create_temp_buf(r->pool, + cscf->client_header_buffer_size); + if (r->header_in == NULL) { + goto failed; + } + + if (ngx_list_init(&r->headers_in.headers, r->pool, 4, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + goto failed; + } + + r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; + + r->schema.data = ngx_pstrdup(r->pool, &pr->schema); + if (r->schema.data == NULL) { + goto failed; + } + + r->schema.len = pr->schema.len; + + r->uri_start = ngx_pstrdup(r->pool, path); + if (r->uri_start == NULL) { + goto failed; + } + + r->uri_end = r->uri_start + path->len; + + if (ngx_http_parse_uri(r) != NGX_OK) { + goto failed; + } + + if (ngx_http_process_request_uri(r) != NGX_OK) { + goto failed; + } + + if (ngx_http_v3_set_push_header(r, "host", &pr->headers_in.server) + != NGX_OK) + { + goto failed; + } + + if (pr->headers_in.accept_encoding) { + if (ngx_http_v3_set_push_header(r, "accept-encoding", + &pr->headers_in.accept_encoding->value) + != NGX_OK) + { + goto failed; + } + } + + if (pr->headers_in.accept_language) { + if (ngx_http_v3_set_push_header(r, "accept-language", + &pr->headers_in.accept_language->value) + != NGX_OK) + { + goto failed; + } + } + + if (pr->headers_in.user_agent) { + if (ngx_http_v3_set_push_header(r, "user-agent", + &pr->headers_in.user_agent->value) + != NGX_OK) + { + goto failed; + } + } + + c->read->handler = ngx_http_v3_push_request_handler; + c->read->handler = ngx_http_v3_push_request_handler; + + ngx_post_event(c->read, &ngx_posted_events); + + return NGX_OK; + +failed: + + if (r) { + ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + c->destroyed = 1; + + pool = c->pool; + + ngx_close_connection(c); + + ngx_destroy_pool(pool); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_v3_set_push_header(ngx_http_request_t *r, const char *name, + ngx_str_t *value) +{ + u_char *p; + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 push header \"%s\": \"%V\"", name, value); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + p = ngx_pnalloc(r->pool, value->len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, value->data, value->len); + p[value->len] = '\0'; + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->key.data = (u_char *) name; + h->key.len = ngx_strlen(name); + h->hash = ngx_hash_key(h->key.data, h->key.len); + h->lowcase_key = (u_char *) name; + h->value.data = p; + h->value.len = value->len; + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static void +ngx_http_v3_push_request_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + + c = ev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 push request handler"); + + ngx_http_process_request(r); +} + + +static ngx_chain_t * +ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, + uint64_t push_id) +{ + size_t n, len; + ngx_buf_t *b; + ngx_chain_t *hl, *cl; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 create push promise id:%uL", push_id); + + len = ngx_http_v3_encode_varlen_int(NULL, push_id); + + len += ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); + + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_METHOD_GET); + + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_AUTHORITY, + NULL, r->headers_in.server.len); + + if (path->len == 1 && path->data[0] == '/') { + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT); + + } else { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT, + NULL, path->len); + } + + if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTPS); + + } else if (r->schema.len == 4 + && ngx_strncmp(r->schema.data, "http", 4) == 0) + { + len += ngx_http_v3_encode_header_ri(NULL, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTP); + + } else { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTP, + NULL, r->schema.len); + } + + if (r->headers_in.accept_encoding) { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, NULL, + r->headers_in.accept_encoding->value.len); + } + + if (r->headers_in.accept_language) { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, NULL, + r->headers_in.accept_language->value.len); + } + + if (r->headers_in.user_agent) { + len += ngx_http_v3_encode_header_lri(NULL, 0, + NGX_HTTP_V3_HEADER_USER_AGENT, NULL, + r->headers_in.user_agent->value.len); + } + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NULL; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, push_id); + + b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, + 0, 0, 0); + + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_METHOD_GET); + + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_AUTHORITY, + r->headers_in.server.data, + r->headers_in.server.len); + + if (path->len == 1 && path->data[0] == '/') { + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT); + + } else { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT, + path->data, path->len); + } + + if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTPS); + + } else if (r->schema.len == 4 + && ngx_strncmp(r->schema.data, "http", 4) == 0) + { + b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTP); + + } else { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTP, + r->schema.data, r->schema.len); + } + + if (r->headers_in.accept_encoding) { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, + r->headers_in.accept_encoding->value.data, + r->headers_in.accept_encoding->value.len); + } + + if (r->headers_in.accept_language) { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, + r->headers_in.accept_language->value.data, + r->headers_in.accept_language->value.len); + } + + if (r->headers_in.user_agent) { + b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + NGX_HTTP_V3_HEADER_USER_AGENT, + r->headers_in.user_agent->value.data, + r->headers_in.user_agent->value.len); + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = b; + cl->next = NULL; + + n = b->last - b->pos; + + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_PUSH_PROMISE) + + ngx_http_v3_encode_varlen_int(NULL, n); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NULL; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_PUSH_PROMISE); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); + + hl = ngx_alloc_chain_link(r->pool); + if (hl == NULL) { + return NULL; + } + + hl->buf = b; + hl->next = cl; + + return hl; +} + + +static ngx_int_t +ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + u_char *chunk; + off_t size; + ngx_int_t rc; + ngx_buf_t *b; + ngx_chain_t *out, *cl, *tl, **ll; + ngx_http_v3_filter_ctx_t *ctx; + + if (in == NULL) { + return ngx_http_next_body_filter(r, in); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_filter_module); + if (ctx == NULL) { + return ngx_http_next_body_filter(r, in); + } + + out = NULL; + ll = &out; + + size = 0; + cl = in; + + for ( ;; ) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 chunk: %O", ngx_buf_size(cl->buf)); + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush + || cl->buf->sync + || ngx_buf_in_memory(cl->buf) + || cl->buf->in_file) + { + tl = ngx_alloc_chain_link(r->pool); + if (tl == NULL) { + return NGX_ERROR; + } + + tl->buf = cl->buf; + *ll = tl; + ll = &tl->next; + } + + if (cl->next == NULL) { + break; + } + + cl = cl->next; + } + + if (size) { + tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (tl == NULL) { + return NGX_ERROR; + } + + b = tl->buf; + chunk = b->start; + + if (chunk == NULL) { + chunk = ngx_palloc(r->pool, NGX_HTTP_V3_VARLEN_INT_LEN * 2); + if (chunk == NULL) { + return NGX_ERROR; + } + + b->start = chunk; + b->end = chunk + NGX_HTTP_V3_VARLEN_INT_LEN * 2; + } + + b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module; + b->memory = 0; + b->temporary = 1; + b->pos = chunk; + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(chunk, + NGX_HTTP_V3_FRAME_DATA); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, size); + + tl->next = out; + out = tl; + } + + if (cl->buf->last_buf) { + tl = ngx_http_v3_create_trailers(r); + if (tl == NULL) { + return NGX_ERROR; + } + + cl->buf->last_buf = 0; + + *ll = tl; + + } else { + *ll = NULL; + } + + rc = ngx_http_next_body_filter(r, out); + + ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, + (ngx_buf_tag_t) &ngx_http_v3_filter_module); + + return rc; +} + + +static ngx_chain_t * +ngx_http_v3_create_trailers(ngx_http_request_t *r) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 create trailers"); + + /* XXX */ + + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NULL; + } + + b->last_buf = 1; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = b; + cl->next = NULL; + + return cl; +} + + +static ngx_int_t +ngx_http_v3_filter_init(ngx_conf_t *cf) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_v3_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_v3_body_filter; + + return NGX_OK; +} diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 2ff0440d9..2b50133f1 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,38 +10,8 @@ #include -/* static table indices */ -#define NGX_HTTP_V3_HEADER_AUTHORITY 0 -#define NGX_HTTP_V3_HEADER_PATH_ROOT 1 -#define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO 4 -#define NGX_HTTP_V3_HEADER_DATE 6 -#define NGX_HTTP_V3_HEADER_LAST_MODIFIED 10 -#define NGX_HTTP_V3_HEADER_LOCATION 12 -#define NGX_HTTP_V3_HEADER_METHOD_GET 17 -#define NGX_HTTP_V3_HEADER_SCHEME_HTTP 22 -#define NGX_HTTP_V3_HEADER_SCHEME_HTTPS 23 -#define NGX_HTTP_V3_HEADER_STATUS_200 25 -#define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING 31 -#define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 -#define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING 59 -#define NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE 72 -#define NGX_HTTP_V3_HEADER_SERVER 92 -#define NGX_HTTP_V3_HEADER_USER_AGENT 95 - - static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); -static ngx_int_t ngx_http_v3_push_resources(ngx_http_request_t *r, - ngx_chain_t ***out); -static ngx_int_t ngx_http_v3_push_resource(ngx_http_request_t *r, - ngx_str_t *path, ngx_chain_t ***out); -static ngx_int_t ngx_http_v3_create_push_request( - ngx_http_request_t *pr, ngx_str_t *path, uint64_t push_id); -static ngx_int_t ngx_http_v3_set_push_header(ngx_http_request_t *r, - const char *name, ngx_str_t *value); -static void ngx_http_v3_push_request_handler(ngx_event_t *ev); -static ngx_chain_t *ngx_http_v3_create_push_promise(ngx_http_request_t *r, - ngx_str_t *path, uint64_t push_id); struct { @@ -443,1106 +413,3 @@ failed: return NGX_ERROR; } - - -ngx_chain_t * -ngx_http_v3_create_header(ngx_http_request_t *r) -{ - u_char *p; - size_t len, n; - ngx_buf_t *b; - ngx_str_t host; - ngx_uint_t i, port; - ngx_chain_t *out, *hl, *cl, **ll; - ngx_list_part_t *part; - ngx_table_elt_t *header; - ngx_connection_t *c; - ngx_http_core_loc_conf_t *clcf; - ngx_http_core_srv_conf_t *cscf; - u_char addr[NGX_SOCKADDR_STRLEN]; - - c = r->connection; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create header"); - - out = NULL; - ll = &out; - - if ((c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0 - && r->method != NGX_HTTP_HEAD) - { - if (ngx_http_v3_push_resources(r, &ll) != NGX_OK) { - return NULL; - } - } - - len = ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); - - if (r->headers_out.status == NGX_HTTP_OK) { - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_STATUS_200); - - } else { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_STATUS_200, - NULL, 3); - } - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - if (r->headers_out.server == NULL) { - if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { - n = sizeof(NGINX_VER) - 1; - - } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { - n = sizeof(NGINX_VER_BUILD) - 1; - - } else { - n = sizeof("nginx") - 1; - } - - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_SERVER, - NULL, n); - } - - if (r->headers_out.date == NULL) { - len += ngx_http_v3_encode_header_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE, - NULL, ngx_cached_http_time.len); - } - - if (r->headers_out.content_type.len) { - n = r->headers_out.content_type.len; - - if (r->headers_out.content_type_len == r->headers_out.content_type.len - && r->headers_out.charset.len) - { - n += sizeof("; charset=") - 1 + r->headers_out.charset.len; - } - - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, - NULL, n); - } - - if (r->headers_out.content_length == NULL) { - if (r->headers_out.content_length_n > 0) { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, - NULL, NGX_OFF_T_LEN); - - } else if (r->headers_out.content_length_n == 0) { - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); - } - } - - if (r->headers_out.last_modified == NULL - && r->headers_out.last_modified_time != -1) - { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, - sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); - } - - if (r->headers_out.location - && r->headers_out.location->value.len - && r->headers_out.location->value.data[0] == '/' - && clcf->absolute_redirect) - { - r->headers_out.location->hash = 0; - - if (clcf->server_name_in_redirect) { - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - host = cscf->server_name; - - } else if (r->headers_in.server.len) { - host = r->headers_in.server; - - } else { - host.len = NGX_SOCKADDR_STRLEN; - host.data = addr; - - if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { - return NULL; - } - } - - port = ngx_inet_get_port(c->local_sockaddr); - - n = sizeof("https://") - 1 + host.len - + r->headers_out.location->value.len; - - if (clcf->port_in_redirect) { - port = (port == 443) ? 0 : port; - - } else { - port = 0; - } - - if (port) { - n += sizeof(":65535") - 1; - } - - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_LOCATION, NULL, n); - - } else { - ngx_str_null(&host); - port = 0; - } - -#if (NGX_HTTP_GZIP) - if (r->gzip_vary) { - if (clcf->gzip_vary) { - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); - - } else { - r->gzip_vary = 0; - } - } -#endif - - part = &r->headers_out.headers.part; - header = part->elts; - - for (i = 0; /* void */; i++) { - - if (i >= part->nelts) { - if (part->next == NULL) { - break; - } - - part = part->next; - header = part->elts; - i = 0; - } - - if (header[i].hash == 0) { - continue; - } - - len += ngx_http_v3_encode_header_l(NULL, &header[i].key, - &header[i].value); - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); - - b = ngx_create_temp_buf(r->pool, len); - if (b == NULL) { - return NULL; - } - - b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, - 0, 0, 0); - - if (r->headers_out.status == NGX_HTTP_OK) { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, - NGX_HTTP_V3_HEADER_STATUS_200); - - } else { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_STATUS_200, - NULL, 3); - b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); - } - - if (r->headers_out.server == NULL) { - if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { - p = (u_char *) NGINX_VER; - n = sizeof(NGINX_VER) - 1; - - } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { - p = (u_char *) NGINX_VER_BUILD; - n = sizeof(NGINX_VER_BUILD) - 1; - - } else { - p = (u_char *) "nginx"; - n = sizeof("nginx") - 1; - } - - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_SERVER, - p, n); - } - - if (r->headers_out.date == NULL) { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_DATE, - ngx_cached_http_time.data, - ngx_cached_http_time.len); - } - - if (r->headers_out.content_type.len) { - n = r->headers_out.content_type.len; - - if (r->headers_out.content_type_len == r->headers_out.content_type.len - && r->headers_out.charset.len) - { - n += sizeof("; charset=") - 1 + r->headers_out.charset.len; - } - - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, - NULL, n); - - p = b->last; - b->last = ngx_cpymem(b->last, r->headers_out.content_type.data, - r->headers_out.content_type.len); - - if (r->headers_out.content_type_len == r->headers_out.content_type.len - && r->headers_out.charset.len) - { - b->last = ngx_cpymem(b->last, "; charset=", - sizeof("; charset=") - 1); - b->last = ngx_cpymem(b->last, r->headers_out.charset.data, - r->headers_out.charset.len); - - /* update r->headers_out.content_type for possible logging */ - - r->headers_out.content_type.len = b->last - p; - r->headers_out.content_type.data = p; - } - } - - if (r->headers_out.content_length == NULL) { - if (r->headers_out.content_length_n > 0) { - p = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); - n = p - b->last; - - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, - NULL, n); - - b->last = ngx_sprintf(b->last, "%O", - r->headers_out.content_length_n); - - } else if (r->headers_out.content_length_n == 0) { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, - NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); - } - } - - if (r->headers_out.last_modified == NULL - && r->headers_out.last_modified_time != -1) - { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, - sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); - - b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); - } - - if (host.data) { - n = sizeof("https://") - 1 + host.len - + r->headers_out.location->value.len; - - if (port) { - n += ngx_sprintf(b->last, ":%ui", port) - b->last; - } - - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_LOCATION, - NULL, n); - - p = b->last; - b->last = ngx_cpymem(b->last, "https://", sizeof("https://") - 1); - b->last = ngx_cpymem(b->last, host.data, host.len); - - if (port) { - b->last = ngx_sprintf(b->last, ":%ui", port); - } - - b->last = ngx_cpymem(b->last, r->headers_out.location->value.data, - r->headers_out.location->value.len); - - /* update r->headers_out.location->value for possible logging */ - - r->headers_out.location->value.len = b->last - p; - r->headers_out.location->value.data = p; - ngx_str_set(&r->headers_out.location->key, "Location"); - } - -#if (NGX_HTTP_GZIP) - if (r->gzip_vary) { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, - NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); - } -#endif - - part = &r->headers_out.headers.part; - header = part->elts; - - for (i = 0; /* void */; i++) { - - if (i >= part->nelts) { - if (part->next == NULL) { - break; - } - - part = part->next; - header = part->elts; - i = 0; - } - - if (header[i].hash == 0) { - continue; - } - - b->last = (u_char *) ngx_http_v3_encode_header_l(b->last, - &header[i].key, - &header[i].value); - } - - if (r->header_only) { - b->last_buf = 1; - } - - cl = ngx_alloc_chain_link(c->pool); - if (cl == NULL) { - return NULL; - } - - cl->buf = b; - cl->next = NULL; - - n = b->last - b->pos; - - len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) - + ngx_http_v3_encode_varlen_int(NULL, n); - - b = ngx_create_temp_buf(c->pool, len); - if (b == NULL) { - return NULL; - } - - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, - NGX_HTTP_V3_FRAME_HEADERS); - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); - - hl = ngx_alloc_chain_link(c->pool); - if (hl == NULL) { - return NULL; - } - - hl->buf = b; - hl->next = cl; - - *ll = hl; - ll = &cl->next; - - if (r->headers_out.content_length_n >= 0 && !r->header_only) { - len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA) - + ngx_http_v3_encode_varlen_int(NULL, - r->headers_out.content_length_n); - - b = ngx_create_temp_buf(c->pool, len); - if (b == NULL) { - return NULL; - } - - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, - NGX_HTTP_V3_FRAME_DATA); - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, - r->headers_out.content_length_n); - - cl = ngx_alloc_chain_link(c->pool); - if (cl == NULL) { - return NULL; - } - - cl->buf = b; - cl->next = NULL; - - *ll = cl; - } - - return out; -} - - -ngx_chain_t * -ngx_http_v3_create_trailers(ngx_http_request_t *r) -{ - ngx_buf_t *b; - ngx_chain_t *cl; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 create trailers"); - - /* XXX */ - - b = ngx_calloc_buf(r->pool); - if (b == NULL) { - return NULL; - } - - b->last_buf = 1; - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NULL; - } - - cl->buf = b; - cl->next = NULL; - - return cl; -} - - -static ngx_int_t -ngx_http_v3_push_resources(ngx_http_request_t *r, ngx_chain_t ***out) -{ - u_char *start, *end, *last; - ngx_str_t path; - ngx_int_t rc; - ngx_uint_t i, push; - ngx_table_elt_t **h; - ngx_http_v3_loc_conf_t *h3lcf; - ngx_http_complex_value_t *pushes; - - h3lcf = ngx_http_get_module_loc_conf(r, ngx_http_v3_module); - - if (h3lcf->pushes) { - pushes = h3lcf->pushes->elts; - - for (i = 0; i < h3lcf->pushes->nelts; i++) { - - if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) { - return NGX_ERROR; - } - - if (path.len == 0) { - continue; - } - - if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) { - continue; - } - - rc = ngx_http_v3_push_resource(r, &path, out); - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc == NGX_ABORT) { - return NGX_OK; - } - - /* NGX_OK, NGX_DECLINED */ - } - } - - if (!h3lcf->push_preload) { - return NGX_OK; - } - - h = r->headers_out.link.elts; - - for (i = 0; i < r->headers_out.link.nelts; i++) { - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 parse link: \"%V\"", &h[i]->value); - - start = h[i]->value.data; - end = h[i]->value.data + h[i]->value.len; - - next_link: - - while (start < end && *start == ' ') { start++; } - - if (start == end || *start++ != '<') { - continue; - } - - while (start < end && *start == ' ') { start++; } - - for (last = start; last < end && *last != '>'; last++) { - /* void */ - } - - if (last == start || last == end) { - continue; - } - - path.len = last - start; - path.data = start; - - start = last + 1; - - while (start < end && *start == ' ') { start++; } - - if (start == end) { - continue; - } - - if (*start == ',') { - start++; - goto next_link; - } - - if (*start++ != ';') { - continue; - } - - last = ngx_strlchr(start, end, ','); - - if (last == NULL) { - last = end; - } - - push = 0; - - for ( ;; ) { - - while (start < last && *start == ' ') { start++; } - - if (last - start >= 6 - && ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0) - { - start += 6; - - if (start == last || *start == ' ' || *start == ';') { - push = 0; - break; - } - - goto next_param; - } - - if (last - start >= 11 - && ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0) - { - start += 11; - - if (start == last || *start == ' ' || *start == ';') { - push = 1; - } - - goto next_param; - } - - if (last - start >= 4 - && ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0) - { - start += 4; - - while (start < last && *start == ' ') { start++; } - - if (start == last || *start++ != '"') { - goto next_param; - } - - for ( ;; ) { - - while (start < last && *start == ' ') { start++; } - - if (last - start >= 7 - && ngx_strncasecmp(start, (u_char *) "preload", 7) == 0) - { - start += 7; - - if (start < last && (*start == ' ' || *start == '"')) { - push = 1; - break; - } - } - - while (start < last && *start != ' ' && *start != '"') { - start++; - } - - if (start == last) { - break; - } - - if (*start == '"') { - break; - } - - start++; - } - } - - next_param: - - start = ngx_strlchr(start, last, ';'); - - if (start == NULL) { - break; - } - - start++; - } - - if (push) { - while (path.len && path.data[path.len - 1] == ' ') { - path.len--; - } - } - - if (push && path.len - && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/')) - { - rc = ngx_http_v3_push_resource(r, &path, out); - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc == NGX_ABORT) { - return NGX_OK; - } - - /* NGX_OK, NGX_DECLINED */ - } - - if (last < end) { - start = last + 1; - goto next_link; - } - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, - ngx_chain_t ***ll) -{ - uint64_t push_id; - ngx_int_t rc; - ngx_chain_t *cl; - ngx_connection_t *c; - ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_connection_t *h3c; - - c = r->connection; - h3c = c->quic->parent->data; - h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); - - ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 push \"%V\" pushing:%ui/%ui id:%uL/%uL", - path, h3c->npushing, h3scf->max_concurrent_pushes, - h3c->next_push_id, h3c->max_push_id); - - if (!ngx_path_separator(path->data[0])) { - ngx_log_error(NGX_LOG_WARN, c->log, 0, - "non-absolute path \"%V\" not pushed", path); - return NGX_DECLINED; - } - - if (h3c->next_push_id > h3c->max_push_id) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 abort pushes due to max_push_id"); - return NGX_ABORT; - } - - if (h3c->npushing >= h3scf->max_concurrent_pushes) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 abort pushes due to max_concurrent_pushes"); - return NGX_ABORT; - } - - push_id = h3c->next_push_id++; - - rc = ngx_http_v3_create_push_request(r, path, push_id); - if (rc != NGX_OK) { - return rc; - } - - cl = ngx_http_v3_create_push_promise(r, path, push_id); - if (cl == NULL) { - return NGX_ERROR; - } - - for (**ll = cl; **ll; *ll = &(**ll)->next); - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, - uint64_t push_id) -{ - ngx_pool_t *pool; - ngx_connection_t *c, *pc; - ngx_http_request_t *r; - ngx_http_log_ctx_t *ctx; - ngx_http_connection_t *hc; - ngx_http_core_srv_conf_t *cscf; - ngx_http_v3_connection_t *h3c; - - pc = pr->connection; - - r = NULL; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "http3 create push request id:%uL", push_id); - - c = ngx_http_v3_create_push_stream(pc, push_id); - if (c == NULL) { - return NGX_ABORT; - } - - hc = ngx_palloc(c->pool, sizeof(ngx_http_connection_t)); - if (hc == NULL) { - goto failed; - } - - h3c = c->quic->parent->data; - ngx_memcpy(hc, h3c, sizeof(ngx_http_connection_t)); - c->data = hc; - - ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); - if (ctx == NULL) { - goto failed; - } - - ctx->connection = c; - ctx->request = NULL; - ctx->current_request = NULL; - - c->log->handler = ngx_http_log_error; - c->log->data = ctx; - c->log->action = "processing pushed request headers"; - - c->log_error = NGX_ERROR_INFO; - - r = ngx_http_create_request(c); - if (r == NULL) { - goto failed; - } - - c->data = r; - - ngx_str_set(&r->http_protocol, "HTTP/3.0"); - - r->method_name = ngx_http_core_get_method; - r->method = NGX_HTTP_GET; - - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - - r->header_in = ngx_create_temp_buf(r->pool, - cscf->client_header_buffer_size); - if (r->header_in == NULL) { - goto failed; - } - - if (ngx_list_init(&r->headers_in.headers, r->pool, 4, - sizeof(ngx_table_elt_t)) - != NGX_OK) - { - goto failed; - } - - r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; - - r->schema.data = ngx_pstrdup(r->pool, &pr->schema); - if (r->schema.data == NULL) { - goto failed; - } - - r->schema.len = pr->schema.len; - - r->uri_start = ngx_pstrdup(r->pool, path); - if (r->uri_start == NULL) { - goto failed; - } - - r->uri_end = r->uri_start + path->len; - - if (ngx_http_parse_uri(r) != NGX_OK) { - goto failed; - } - - if (ngx_http_process_request_uri(r) != NGX_OK) { - goto failed; - } - - if (ngx_http_v3_set_push_header(r, "host", &pr->headers_in.server) - != NGX_OK) - { - goto failed; - } - - if (pr->headers_in.accept_encoding) { - if (ngx_http_v3_set_push_header(r, "accept-encoding", - &pr->headers_in.accept_encoding->value) - != NGX_OK) - { - goto failed; - } - } - - if (pr->headers_in.accept_language) { - if (ngx_http_v3_set_push_header(r, "accept-language", - &pr->headers_in.accept_language->value) - != NGX_OK) - { - goto failed; - } - } - - if (pr->headers_in.user_agent) { - if (ngx_http_v3_set_push_header(r, "user-agent", - &pr->headers_in.user_agent->value) - != NGX_OK) - { - goto failed; - } - } - - c->read->handler = ngx_http_v3_push_request_handler; - c->read->handler = ngx_http_v3_push_request_handler; - - ngx_post_event(c->read, &ngx_posted_events); - - return NGX_OK; - -failed: - - if (r) { - ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - c->destroyed = 1; - - pool = c->pool; - - ngx_close_connection(c); - - ngx_destroy_pool(pool); - - return NGX_ERROR; -} - - -static ngx_int_t -ngx_http_v3_set_push_header(ngx_http_request_t *r, const char *name, - ngx_str_t *value) -{ - u_char *p; - ngx_table_elt_t *h; - ngx_http_header_t *hh; - ngx_http_core_main_conf_t *cmcf; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 push header \"%s\": \"%V\"", name, value); - - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - - p = ngx_pnalloc(r->pool, value->len + 1); - if (p == NULL) { - return NGX_ERROR; - } - - ngx_memcpy(p, value->data, value->len); - p[value->len] = '\0'; - - h = ngx_list_push(&r->headers_in.headers); - if (h == NULL) { - return NGX_ERROR; - } - - h->key.data = (u_char *) name; - h->key.len = ngx_strlen(name); - h->hash = ngx_hash_key(h->key.data, h->key.len); - h->lowcase_key = (u_char *) name; - h->value.data = p; - h->value.len = value->len; - - hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, - h->lowcase_key, h->key.len); - - if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { - return NGX_ERROR; - } - - return NGX_OK; -} - - -static void -ngx_http_v3_push_request_handler(ngx_event_t *ev) -{ - ngx_connection_t *c; - ngx_http_request_t *r; - - c = ev->data; - r = c->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 push request handler"); - - ngx_http_process_request(r); -} - - -static ngx_chain_t * -ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, - uint64_t push_id) -{ - size_t n, len; - ngx_buf_t *b; - ngx_chain_t *hl, *cl; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 create push promise id:%uL", push_id); - - len = ngx_http_v3_encode_varlen_int(NULL, push_id); - - len += ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); - - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_METHOD_GET); - - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_AUTHORITY, - NULL, r->headers_in.server.len); - - if (path->len == 1 && path->data[0] == '/') { - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_PATH_ROOT); - - } else { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_PATH_ROOT, - NULL, path->len); - } - - if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTPS); - - } else if (r->schema.len == 4 - && ngx_strncmp(r->schema.data, "http", 4) == 0) - { - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTP); - - } else { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTP, - NULL, r->schema.len); - } - - if (r->headers_in.accept_encoding) { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, NULL, - r->headers_in.accept_encoding->value.len); - } - - if (r->headers_in.accept_language) { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, NULL, - r->headers_in.accept_language->value.len); - } - - if (r->headers_in.user_agent) { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_USER_AGENT, NULL, - r->headers_in.user_agent->value.len); - } - - b = ngx_create_temp_buf(r->pool, len); - if (b == NULL) { - return NULL; - } - - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, push_id); - - b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, - 0, 0, 0); - - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, - NGX_HTTP_V3_HEADER_METHOD_GET); - - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_AUTHORITY, - r->headers_in.server.data, - r->headers_in.server.len); - - if (path->len == 1 && path->data[0] == '/') { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, - NGX_HTTP_V3_HEADER_PATH_ROOT); - - } else { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_PATH_ROOT, - path->data, path->len); - } - - if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTPS); - - } else if (r->schema.len == 4 - && ngx_strncmp(r->schema.data, "http", 4) == 0) - { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTP); - - } else { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTP, - r->schema.data, r->schema.len); - } - - if (r->headers_in.accept_encoding) { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, - r->headers_in.accept_encoding->value.data, - r->headers_in.accept_encoding->value.len); - } - - if (r->headers_in.accept_language) { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, - r->headers_in.accept_language->value.data, - r->headers_in.accept_language->value.len); - } - - if (r->headers_in.user_agent) { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, - NGX_HTTP_V3_HEADER_USER_AGENT, - r->headers_in.user_agent->value.data, - r->headers_in.user_agent->value.len); - } - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NULL; - } - - cl->buf = b; - cl->next = NULL; - - n = b->last - b->pos; - - len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_PUSH_PROMISE) - + ngx_http_v3_encode_varlen_int(NULL, n); - - b = ngx_create_temp_buf(r->pool, len); - if (b == NULL) { - return NULL; - } - - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, - NGX_HTTP_V3_FRAME_PUSH_PROMISE); - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); - - hl = ngx_alloc_chain_link(r->pool); - if (hl == NULL) { - return NULL; - } - - hl->buf = b; - hl->next = cl; - - return hl; -} -- cgit From c3714a8089f50e15966db73c227c4eea6d9f8e77 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 16 Dec 2020 12:47:38 +0000 Subject: HTTP/3: staticize ngx_http_v3_methods. --- src/http/v3/ngx_http_v3_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 2b50133f1..4597bc180 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -14,7 +14,7 @@ static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); -struct { +static const struct { ngx_str_t name; ngx_uint_t method; } ngx_http_v3_methods[] = { -- cgit From 405b9be899fe9bb10ad910bfe10a2fb7cae9a846 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 16 Dec 2020 12:47:41 +0000 Subject: HTTP/3: staticize internal parsing functions. --- src/http/v3/ngx_http_v3_parse.c | 56 +++++++++++++++++++++++++++++++---------- src/http/v3/ngx_http_v3_parse.h | 34 ++----------------------- 2 files changed, 45 insertions(+), 45 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index afe442464..7c22b482c 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -14,11 +14,41 @@ ((type) == 0x02 || (type) == 0x06 || (type) == 0x08 || (type) == 0x09) +static ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, + ngx_http_v3_parse_settings_t *st, u_char ch); + +static ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, + ngx_http_v3_parse_varlen_int_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, + ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch); + +static ngx_int_t ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, + ngx_http_v3_parse_header_block_prefix_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_header_rep(ngx_connection_t *c, + ngx_http_v3_parse_header_rep_t *st, ngx_uint_t base, u_char ch); +static ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, + ngx_http_v3_parse_literal_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_header_ri(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_header_lri(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_header_l(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_header_pbi(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); + +static ngx_int_t ngx_http_v3_parse_header_inr(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, + ngx_http_v3_parse_header_t *st, u_char ch); + static ngx_int_t ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value); -ngx_int_t +static ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, ngx_http_v3_parse_varlen_int_t *st, u_char ch) { @@ -95,7 +125,7 @@ done: } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch) { @@ -289,7 +319,7 @@ done: } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, ngx_http_v3_parse_header_block_prefix_t *st, u_char ch) { @@ -366,7 +396,7 @@ done: } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_header_rep(ngx_connection_t *c, ngx_http_v3_parse_header_rep_t *st, ngx_uint_t base, u_char ch) { @@ -454,7 +484,7 @@ ngx_http_v3_parse_header_rep(ngx_connection_t *c, } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, u_char ch) { @@ -533,7 +563,7 @@ done: } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_header_ri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { @@ -588,7 +618,7 @@ done: } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_header_lri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { @@ -679,7 +709,7 @@ done: } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_header_l(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { @@ -777,7 +807,7 @@ done: } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_header_pbi(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { @@ -826,7 +856,7 @@ done: } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { @@ -1118,7 +1148,7 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, ngx_http_v3_parse_settings_t *st, u_char ch) { @@ -1277,7 +1307,7 @@ done: } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_header_inr(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { @@ -1364,7 +1394,7 @@ done: } -ngx_int_t +static ngx_int_t ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h index 856f021bd..f039ac28d 100644 --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -124,44 +124,14 @@ typedef struct { * NGX_HTTP_V3_ERROR_XXX - HTTP/3 or QPACK error */ -ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, - ngx_http_v3_parse_varlen_int_t *st, u_char ch); -ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, - ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch); - ngx_int_t ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, u_char ch); -ngx_int_t ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, - ngx_http_v3_parse_header_block_prefix_t *st, u_char ch); -ngx_int_t ngx_http_v3_parse_header_rep(ngx_connection_t *c, - ngx_http_v3_parse_header_rep_t *st, ngx_uint_t base, u_char ch); -ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, - ngx_http_v3_parse_literal_t *st, u_char ch); -ngx_int_t ngx_http_v3_parse_header_ri(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); -ngx_int_t ngx_http_v3_parse_header_lri(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); -ngx_int_t ngx_http_v3_parse_header_l(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); -ngx_int_t ngx_http_v3_parse_header_pbi(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); -ngx_int_t ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); +ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c, + ngx_http_v3_parse_data_t *st, u_char ch); ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch); -ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, - ngx_http_v3_parse_settings_t *st, u_char ch); - ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch); -ngx_int_t ngx_http_v3_parse_header_inr(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); -ngx_int_t ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); - ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch); -ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c, - ngx_http_v3_parse_data_t *st, u_char ch); - #endif /* _NGX_HTTP_V3_PARSE_H_INCLUDED_ */ -- cgit From a96989365676de270fbb2ad19480435ad5e86df4 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 22 Dec 2020 12:04:15 +0300 Subject: QUIC: fixed building ALPN callback without debug and http2. --- src/http/modules/ngx_http_ssl_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 111de479b..97c58e445 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -429,7 +429,7 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, #if (NGX_HTTP_V2 || NGX_HTTP_QUIC) ngx_http_connection_t *hc; #endif -#if (NGX_HTTP_V2 || NGX_DEBUG) +#if (NGX_HTTP_V2 || NGX_HTTP_QUIC || NGX_DEBUG) ngx_connection_t *c; c = ngx_ssl_get_connection(ssl_conn); -- cgit From 9e489d208fff35c490b43980a064c38cc8dc4f2c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 22 Jan 2021 16:34:06 +0300 Subject: HTTP/3: refactored request parser. The change reduces diff to the default branch for src/http/ngx_http_request.c and src/http/ngx_http_parse.c. --- src/http/modules/ngx_http_quic_module.c | 37 +++ src/http/modules/ngx_http_quic_module.h | 2 +- src/http/ngx_http.h | 5 + src/http/ngx_http_parse.c | 2 - src/http/ngx_http_request.c | 197 +++--------- src/http/ngx_http_request.h | 2 +- src/http/v3/ngx_http_v3.h | 10 +- src/http/v3/ngx_http_v3_request.c | 524 +++++++++++++++++++++----------- src/http/v3/ngx_http_v3_streams.c | 66 ++-- 9 files changed, 457 insertions(+), 388 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index ff79cdc8d..5314af35b 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -175,6 +175,43 @@ static ngx_http_variable_t ngx_http_quic_vars[] = { }; +ngx_int_t +ngx_http_quic_init(ngx_connection_t *c) +{ + ngx_quic_conf_t *qcf; + ngx_http_connection_t *hc, *phc; + ngx_http_core_loc_conf_t *clcf; + + hc = c->data; + + hc->ssl = 1; + + if (c->quic == NULL) { + c->log->connection = c->number; + + qcf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_quic_module); + + ngx_quic_run(c, qcf); + + return NGX_DONE; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http init quic stream"); + + phc = c->quic->parent->data; + + if (phc->ssl_servername) { + hc->ssl_servername = phc->ssl_servername; + hc->conf_ctx = phc->conf_ctx; + + clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + ngx_set_connection_log(c, clcf->error_log); + } + + return NGX_OK; +} + + static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) diff --git a/src/http/modules/ngx_http_quic_module.h b/src/http/modules/ngx_http_quic_module.h index bd4930f8a..bc75dd501 100644 --- a/src/http/modules/ngx_http_quic_module.h +++ b/src/http/modules/ngx_http_quic_module.h @@ -18,7 +18,7 @@ #define NGX_HTTP_QUIC_ALPN_DRAFT_FMT "\x05hq-%02uD" -extern ngx_module_t ngx_http_quic_module; +ngx_int_t ngx_http_quic_init(ngx_connection_t *c); #endif /* _NGX_HTTP_QUIC_H_INCLUDED_ */ diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 2a3d81a37..778744d71 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -134,6 +134,11 @@ void ngx_http_handler(ngx_http_request_t *r); void ngx_http_run_posted_requests(ngx_connection_t *c); ngx_int_t ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr); +ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r, + ngx_str_t *host); +ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, + ngx_uint_t alloc); +void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc); diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 7b5cd30cd..20ad89a77 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -143,7 +143,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) /* HTTP methods: GET, HEAD, POST */ case sw_start: - r->parse_start = p; r->request_start = p; if (ch == CR || ch == LF) { @@ -896,7 +895,6 @@ ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b, /* first char */ case sw_start: - r->parse_start = p; r->header_name_start = p; r->invalid_header = 0; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 0ce4dacec..abcc1bba8 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -31,10 +31,6 @@ static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); -static ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, - ngx_uint_t alloc); -static ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r, - ngx_str_t *host); static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c, ngx_http_virtual_names_t *virtual_names, ngx_str_t *host, ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp); @@ -52,7 +48,6 @@ static void ngx_http_keepalive_handler(ngx_event_t *ev); static void ngx_http_set_lingering_close(ngx_connection_t *c); static void ngx_http_lingering_close_handler(ngx_event_t *ev); static ngx_int_t ngx_http_post_action(ngx_http_request_t *r); -static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error); static void ngx_http_log_request(ngx_http_request_t *r); static u_char *ngx_http_log_error_handler(ngx_http_request_t *r, @@ -303,54 +298,11 @@ ngx_http_init_connection(ngx_connection_t *c) hc->conf_ctx = hc->addr_conf->default_server->ctx; #if (NGX_HTTP_QUIC) - if (hc->addr_conf->quic) { - ngx_quic_conf_t *qcf; - ngx_http_connection_t *phc; - ngx_http_core_loc_conf_t *clcf; - - hc->ssl = 1; - -#if (NGX_HTTP_V3) - - if (hc->addr_conf->http3) { - ngx_int_t rc; - - rc = ngx_http_v3_init_connection(c); - - if (rc == NGX_ERROR) { - ngx_http_close_connection(c); - return; - } - - if (rc == NGX_DONE) { - return; - } - } - -#endif - - if (c->quic == NULL) { - c->log->connection = c->number; - - qcf = ngx_http_get_module_srv_conf(hc->conf_ctx, - ngx_http_quic_module); - ngx_quic_run(c, qcf); + if (ngx_http_quic_init(c) == NGX_DONE) { return; } - - phc = c->quic->parent->data; - - if (phc->ssl_servername) { - hc->ssl_servername = phc->ssl_servername; - hc->conf_ctx = phc->conf_ctx; - - clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, - ngx_http_core_module); - ngx_set_connection_log(c, clcf->error_log); - } } - #endif ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); @@ -380,6 +332,13 @@ ngx_http_init_connection(ngx_connection_t *c) } #endif +#if (NGX_HTTP_V3) + if (hc->addr_conf->http3) { + ngx_http_v3_init(c); + return; + } +#endif + #if (NGX_HTTP_SSL) { ngx_http_ssl_srv_conf_t *sscf; @@ -669,12 +628,6 @@ ngx_http_alloc_request(ngx_connection_t *c) r->method = NGX_HTTP_UNKNOWN; r->http_version = NGX_HTTP_VERSION_10; -#if (NGX_HTTP_V3) - if (hc->addr_conf->http3) { - r->http_version = NGX_HTTP_VERSION_30; - } -#endif - r->headers_in.content_length_n = -1; r->headers_in.keep_alive_n = -1; r->headers_out.content_length_n = -1; @@ -1140,16 +1093,7 @@ ngx_http_process_request_line(ngx_event_t *rev) } } - switch (r->http_version) { -#if (NGX_HTTP_V3) - case NGX_HTTP_VERSION_30: - rc = ngx_http_v3_parse_request(r, r->header_in); - break; -#endif - - default: /* HTTP/1.x */ - rc = ngx_http_parse_request_line(r, r->header_in); - } + rc = ngx_http_parse_request_line(r, r->header_in); if (rc == NGX_OK) { @@ -1157,7 +1101,7 @@ ngx_http_process_request_line(ngx_event_t *rev) r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; - r->request_length = r->header_in->pos - r->parse_start; + r->request_length = r->header_in->pos - r->request_start; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http request line: \"%V\"", &r->request_line); @@ -1234,15 +1178,6 @@ ngx_http_process_request_line(ngx_event_t *rev) break; } - if (rc == NGX_BUSY) { - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - break; - } - if (rc != NGX_AGAIN) { /* there was error while a request line parsing */ @@ -1272,8 +1207,8 @@ ngx_http_process_request_line(ngx_event_t *rev) } if (rv == NGX_DECLINED) { - r->request_line.len = r->header_in->end - r->parse_start; - r->request_line.data = r->parse_start; + r->request_line.len = r->header_in->end - r->request_start; + r->request_line.data = r->request_start; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long URI"); @@ -1437,7 +1372,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - rc = NGX_OK; + rc = NGX_AGAIN; for ( ;; ) { @@ -1453,7 +1388,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) } if (rv == NGX_DECLINED) { - p = r->parse_start; + p = r->header_name_start; r->lingering_close = 1; @@ -1473,7 +1408,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long header line: \"%*s...\"", - len, r->parse_start); + len, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); @@ -1491,32 +1426,21 @@ ngx_http_process_request_headers(ngx_event_t *rev) /* the host header could change the server configuration context */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - switch (r->http_version) { -#if (NGX_HTTP_V3) - case NGX_HTTP_VERSION_30: - rc = ngx_http_v3_parse_header(r, r->header_in, - cscf->underscores_in_headers); - break; -#endif - - default: /* HTTP/1.x */ - rc = ngx_http_parse_header_line(r, r->header_in, - cscf->underscores_in_headers); - } + rc = ngx_http_parse_header_line(r, r->header_in, + cscf->underscores_in_headers); if (rc == NGX_OK) { - r->request_length += r->header_in->pos - r->parse_start; + r->request_length += r->header_in->pos - r->header_name_start; if (r->invalid_header && cscf->ignore_invalid_headers) { /* there was error while a header line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid header line: \"%*s: %*s\"", - r->header_name_end - r->header_name_start, - r->header_name_start, - r->header_end - r->header_start, r->header_start); + "client sent invalid header line: \"%*s\"", + r->header_end - r->header_name_start, + r->header_name_start); continue; } @@ -1532,17 +1456,11 @@ ngx_http_process_request_headers(ngx_event_t *rev) h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; - - if (h->key.data[h->key.len]) { - h->key.data[h->key.len] = '\0'; - } + h->key.data[h->key.len] = '\0'; h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; - - if (h->value.data[h->value.len]) { - h->value.data[h->value.len] = '\0'; - } + h->value.data[h->value.len] = '\0'; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { @@ -1578,7 +1496,7 @@ ngx_http_process_request_headers(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header done"); - r->request_length += r->header_in->pos - r->parse_start; + r->request_length += r->header_in->pos - r->header_name_start; r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; @@ -1693,7 +1611,7 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, return NGX_OK; } - old = r->parse_start; + old = request_line ? r->request_start : r->header_name_start; cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); @@ -1771,14 +1689,6 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, b->pos = new + (r->header_in->pos - old); b->last = new + (r->header_in->pos - old); - r->parse_start = new; - - r->header_in = b; - - if (r->http_version > NGX_HTTP_VERSION_11) { - return NGX_OK; - } - if (request_line) { r->request_start = new; @@ -1827,6 +1737,8 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, r->header_end = new + (r->header_end - old); } + r->header_in = b; + return NGX_OK; } @@ -2047,46 +1959,13 @@ ngx_http_process_request_header(ngx_http_request_t *r) return NGX_ERROR; } - if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) { + if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent HTTP/1.1 request without \"Host\" header"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return NGX_ERROR; } - if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_20) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent HTTP/2 request without " - "\":authority\" or \"Host\" header"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; - } - - if (r->http_version == NGX_HTTP_VERSION_30) { - if (r->headers_in.server.len == 0) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent HTTP/3 request without " - "\":authority\" or \"Host\" header"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; - } - - if (r->headers_in.host) { - if (r->headers_in.host->value.len != r->headers_in.server.len - || ngx_memcmp(r->headers_in.host->value.data, - r->headers_in.server.data, - r->headers_in.server.len) - != 0) - { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent HTTP/3 request with different " - "values of \":authority\" and \"Host\" headers"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; - } - } - } - if (r->headers_in.content_length) { r->headers_in.content_length_n = ngx_atoof(r->headers_in.content_length->value.data, @@ -2125,12 +2004,6 @@ ngx_http_process_request_header(ngx_http_request_t *r) } } -#if (NGX_HTTP_V3) - if (r->http_version == NGX_HTTP_VERSION_30) { - r->headers_in.chunked = 1; - } -#endif - if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) { if (r->headers_in.keep_alive) { r->headers_in.keep_alive_n = @@ -2235,7 +2108,7 @@ ngx_http_process_request(ngx_http_request_t *r) } -static ngx_int_t +ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc) { u_char *h, ch; @@ -2326,7 +2199,7 @@ ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc) } -static ngx_int_t +ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host) { ngx_int_t rc; @@ -3744,7 +3617,7 @@ ngx_http_post_action(ngx_http_request_t *r) } -static void +void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_connection_t *c; @@ -3965,15 +3838,15 @@ ngx_http_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr, len -= p - buf; buf = p; - if (r->request_line.data == NULL && r->parse_start) { - for (p = r->parse_start; p < r->header_in->last; p++) { + if (r->request_line.data == NULL && r->request_start) { + for (p = r->request_start; p < r->header_in->last; p++) { if (*p == CR || *p == LF) { break; } } - r->request_line.len = p - r->parse_start; - r->request_line.data = r->parse_start; + r->request_line.len = p - r->request_start; + r->request_line.data = r->request_start; } if (r->request_line.len) { diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 518c2f4d1..4121e3c3b 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -325,6 +325,7 @@ typedef struct { unsigned ssl:1; unsigned proxy_protocol:1; + unsigned http3:1; } ngx_http_connection_t; @@ -581,7 +582,6 @@ struct ngx_http_request_s { * via ngx_http_ephemeral_t */ - u_char *parse_start; u_char *uri_start; u_char *uri_end; u_char *uri_ext; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 89ed9c98a..9f91ff8f1 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -127,17 +127,11 @@ typedef struct { uint64_t next_push_id; uint64_t max_push_id; - ngx_uint_t settings_sent; - /* unsigned settings_sent:1; */ ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; } ngx_http_v3_connection_t; -ngx_int_t ngx_http_v3_init_connection(ngx_connection_t *c); - -ngx_int_t ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b); -ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, - ngx_uint_t allow_underscores); +void ngx_http_v3_init(ngx_connection_t *c); ngx_int_t ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, ngx_http_chunked_t *ctx); @@ -157,6 +151,8 @@ uintptr_t ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index); uintptr_t ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, u_char *data, size_t len); +ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); +void ngx_http_v3_init_uni_stream(ngx_connection_t *c); ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id); ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 4597bc180..09c1ec335 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,8 +10,13 @@ #include +static void ngx_http_v3_process_request(ngx_event_t *rev); +static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, + ngx_str_t *name, ngx_str_t *value); static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); +static ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r); static const struct { @@ -37,230 +42,256 @@ static const struct { }; -ngx_int_t -ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) +void +ngx_http_v3_init(ngx_connection_t *c) { - size_t len; - u_char *p; - ngx_int_t rc, n; - ngx_str_t *name, *value; - ngx_connection_t *c; - ngx_http_v3_parse_headers_t *st; - - c = r->connection; - st = r->h3_parse; - - if (st == NULL) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header"); - - st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_headers_t)); - if (st == NULL) { - goto failed; - } - - r->h3_parse = st; - r->parse_start = b->pos; - r->state = 1; + size_t size; + ngx_buf_t *b; + ngx_event_t *rev; + ngx_http_request_t *r; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + + if (ngx_http_v3_init_session(c) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "internal error"); + ngx_http_close_connection(c); + return; } - while (b->pos < b->last) { - rc = ngx_http_v3_parse_headers(c, st, *b->pos); - - if (rc > 0) { - ngx_http_v3_finalize_connection(c, rc, - "could not parse request headers"); - goto failed; - } - - if (rc == NGX_ERROR) { - goto failed; - } + if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { + ngx_http_v3_init_uni_stream(c); + return; + } - if (rc == NGX_BUSY) { - return NGX_BUSY; - } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init request stream"); - b->pos++; + hc = c->data; - if (rc == NGX_AGAIN) { - continue; - } + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); - name = &st->header_rep.header.name; - value = &st->header_rep.header.value; + size = cscf->client_header_buffer_size; - n = ngx_http_v3_process_pseudo_header(r, name, value); + b = c->buffer; - if (n == NGX_ERROR) { - goto failed; + if (b == NULL) { + b = ngx_create_temp_buf(c->pool, size); + if (b == NULL) { + ngx_http_close_connection(c); + return; } - if (n == NGX_OK && rc == NGX_OK) { - continue; - } + c->buffer = b; - len = r->method_name.len + 1 - + (r->uri_end - r->uri_start) + 1 - + sizeof("HTTP/3.0") - 1; + } else if (b->start == NULL) { - p = ngx_pnalloc(c->pool, len); - if (p == NULL) { - goto failed; + b->start = ngx_palloc(c->pool, size); + if (b->start == NULL) { + ngx_http_close_connection(c); + return; } - r->request_start = p; - - p = ngx_cpymem(p, r->method_name.data, r->method_name.len); - r->method_end = p - 1; - *p++ = ' '; - p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start); - *p++ = ' '; - r->http_protocol.data = p; - p = ngx_cpymem(p, "HTTP/3.0", sizeof("HTTP/3.0") - 1); + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + } - r->request_end = p; - r->state = 0; + c->log->action = "reading client request"; - return NGX_OK; + r = ngx_http_create_request(c); + if (r == NULL) { + ngx_http_close_connection(c); + return; } - return NGX_AGAIN; + r->http_version = NGX_HTTP_VERSION_30; -failed: + c->data = r; - return NGX_HTTP_PARSE_INVALID_REQUEST; + rev = c->read; + rev->handler = ngx_http_v3_process_request; + + ngx_http_v3_process_request(rev); } -ngx_int_t -ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, - ngx_uint_t allow_underscores) +static void +ngx_http_v3_process_request(ngx_event_t *rev) { - u_char ch; + ssize_t n; + ngx_buf_t *b; ngx_int_t rc; - ngx_str_t *name, *value; - ngx_uint_t hash, i, n; ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_core_srv_conf_t *cscf; ngx_http_v3_parse_headers_t *st; - enum { - sw_start = 0, - sw_done, - sw_next, - sw_header - }; - c = r->connection; - st = r->h3_parse; + c = rev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http3 process request"); - switch (r->state) { + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; + } - case sw_start: - r->parse_start = b->pos; + st = r->h3_parse; - if (st->state) { - r->state = sw_next; - goto done; + if (st == NULL) { + st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_headers_t)); + if (st == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return; } - name = &st->header_rep.header.name; + r->h3_parse = st; + } - if (name->len && name->data[0] != ':') { - r->state = sw_done; - goto done; - } + b = r->header_in; - /* fall through */ + for ( ;; ) { - case sw_done: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header done"); - return NGX_HTTP_PARSE_HEADER_DONE; + if (b->pos == b->last) { - case sw_next: - r->parse_start = b->pos; - r->invalid_header = 0; - break; + if (!rev->ready) { + break; + } - case sw_header: - break; - } + n = c->recv(c, b->start, b->end - b->start); + + if (n == NGX_AGAIN) { + if (!rev->timer_set) { + cscf = ngx_http_get_module_srv_conf(r, + ngx_http_core_module); + ngx_add_timer(rev, cscf->client_header_timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + break; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client prematurely closed connection"); + } - while (b->pos < b->last) { - rc = ngx_http_v3_parse_headers(c, st, *b->pos++); + if (n == 0 || n == NGX_ERROR) { + c->error = 1; + c->log->action = "reading client request"; + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + break; + } + + b->pos = b->start; + b->last = b->start + n; + } + + rc = ngx_http_v3_parse_headers(c, st, *b->pos); if (rc > 0) { ngx_http_v3_finalize_connection(c, rc, "could not parse request headers"); - return NGX_HTTP_PARSE_INVALID_HEADER; + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + break; } if (rc == NGX_ERROR) { - return NGX_HTTP_PARSE_INVALID_HEADER; + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "internal error"); + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + break; } - if (rc == NGX_DONE) { - r->state = sw_done; - goto done; + if (rc == NGX_BUSY) { + if (rev->error) { + ngx_http_close_request(r, NGX_HTTP_CLOSE); + break; + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + break; } - if (rc == NGX_OK) { - r->state = sw_next; - goto done; + b->pos++; + r->request_length++; + + if (rc == NGX_AGAIN) { + continue; } - } - r->state = sw_header; - return NGX_AGAIN; + /* rc == NGX_OK || rc == NGX_DONE */ -done: + if (ngx_http_v3_process_header(r, &st->header_rep.header.name, + &st->header_rep.header.value) + != NGX_OK) + { + break; + } - name = &st->header_rep.header.name; - value = &st->header_rep.header.value; + if (rc == NGX_DONE) { + if (ngx_http_v3_process_request_header(r) != NGX_OK) { + break; + } - r->header_name_start = name->data; - r->header_name_end = name->data + name->len; - r->header_start = value->data; - r->header_end = value->data + value->len; + ngx_http_process_request(r); + break; + } + } - hash = 0; - i = 0; + ngx_http_run_posted_requests(c); - for (n = 0; n < name->len; n++) { - ch = name->data[n]; + return; +} - if (ch >= 'A' && ch <= 'Z') { - /* - * A request or response containing uppercase - * header field names MUST be treated as malformed - */ - return NGX_HTTP_PARSE_INVALID_HEADER; - } - if (ch == '\0') { - return NGX_HTTP_PARSE_INVALID_HEADER; - } +static ngx_int_t +ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, + ngx_str_t *value) +{ + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; - if (ch == '_' && !allow_underscores) { - r->invalid_header = 1; - continue; - } + if (name->len && name->data[0] == ':') { + return ngx_http_v3_process_pseudo_header(r, name, value); + } - if ((ch < 'a' || ch > 'z') - && (ch < '0' || ch > '9') - && ch != '-' && ch != '_') - { - r->invalid_header = 1; - continue; - } + if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) { + return NGX_ERROR; + } - hash = ngx_hash(hash, ch); - r->lowcase_header[i++] = ch; - i &= (NGX_HTTP_LC_HEADER_LEN - 1); + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; } - r->header_hash = hash; - r->lowcase_index = i; + h->key = *name; + h->value = *value; + h->lowcase_key = h->key.data; + h->hash = ngx_hash_key(h->key.data, h->key.len); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { + return NGX_ERROR; + } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 header: \"%V: %V\"", name, value); return NGX_OK; } @@ -269,75 +300,210 @@ static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value) { - ngx_uint_t i; - ngx_connection_t *c; + ngx_uint_t i; - if (name->len == 0 || name->data[0] != ':') { - return NGX_DONE; + if (r->request_line.len) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent out of order pseudo-headers"); + goto failed; } - c = r->connection; - if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) { + r->method_name = *value; for (i = 0; i < sizeof(ngx_http_v3_methods) / sizeof(ngx_http_v3_methods[0]); i++) { if (value->len == ngx_http_v3_methods[i].name.len - && ngx_strncmp(value->data, ngx_http_v3_methods[i].name.data, - value->len) == 0) + && ngx_strncmp(value->data, + ngx_http_v3_methods[i].name.data, value->len) + == 0) { r->method = ngx_http_v3_methods[i].method; break; } } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http3 method \"%V\" %ui", value, r->method); return NGX_OK; } if (name->len == 5 && ngx_strncmp(name->data, ":path", 5) == 0) { + r->uri_start = value->data; r->uri_end = value->data + value->len; if (ngx_http_parse_uri(r) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid :path header: \"%V\"", value); - return NGX_ERROR; + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \":path\" header: \"%V\"", + value); + goto failed; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http3 path \"%V\"", value); - return NGX_OK; } if (name->len == 7 && ngx_strncmp(name->data, ":scheme", 7) == 0) { - r->schema_start = value->data; - r->schema_end = value->data + value->len; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 schema \"%V\"", value); + r->schema = *value; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 schema \"%V\"", value); return NGX_OK; } if (name->len == 10 && ngx_strncmp(name->data, ":authority", 10) == 0) { + r->host_start = value->data; r->host_end = value->data + value->len; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http3 authority \"%V\"", value); + return NGX_OK; + } + + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent unknown pseudo-header \"%V\"", name); + +failed: + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; +} + +static ngx_int_t +ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r) +{ + size_t len; + u_char *p; + ngx_int_t rc; + ngx_str_t host; + + if (r->request_line.len) { return NGX_OK; } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 unknown pseudo header \"%V\" \"%V\"", name, value); + len = r->method_name.len + 1 + + (r->uri_end - r->uri_start) + 1 + + sizeof("HTTP/3.0") - 1; + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + r->request_line.data = p; + + p = ngx_cpymem(p, r->method_name.data, r->method_name.len); + *p++ = ' '; + p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start); + *p++ = ' '; + p = ngx_cpymem(p, "HTTP/3.0", sizeof("HTTP/3.0") - 1); + + r->request_line.len = p - r->request_line.data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 request line: \"%V\"", &r->request_line); + + ngx_str_set(&r->http_protocol, "HTTP/3.0"); + + if (ngx_http_process_request_uri(r) != NGX_OK) { + return NGX_ERROR; + } + + if (r->host_end) { + + host.len = r->host_end - r->host_start; + host.data = r->host_start; + + rc = ngx_http_validate_host(&host, r->pool, 0); + + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid host in request line"); + goto failed; + } + + if (rc == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) { + return NGX_ERROR; + } + + r->headers_in.server = host; + } + + if (ngx_list_init(&r->headers_in.headers, r->pool, 20, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } return NGX_OK; + +failed: + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_v3_process_request_header(ngx_http_request_t *r) +{ + if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) { + return NGX_ERROR; + } + + if (r->headers_in.server.len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent neither \":authority\" nor \"Host\" header"); + goto failed; + } + + if (r->headers_in.host) { + if (r->headers_in.host->value.len != r->headers_in.server.len + || ngx_memcmp(r->headers_in.host->value.data, + r->headers_in.server.data, + r->headers_in.server.len) + != 0) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent \":authority\" and \"Host\" headers " + "with different values"); + goto failed; + } + } + + if (r->headers_in.content_length) { + r->headers_in.content_length_n = + ngx_atoof(r->headers_in.content_length->value.data, + r->headers_in.content_length->value.len); + + if (r->headers_in.content_length_n == NGX_ERROR) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \"Content-Length\" header"); + goto failed; + } + } + + return NGX_OK; + +failed: + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; } diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 8ac048715..871914065 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -40,44 +40,49 @@ static ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); ngx_int_t -ngx_http_v3_init_connection(ngx_connection_t *c) +ngx_http_v3_init_session(ngx_connection_t *c) { - ngx_http_connection_t *hc; - ngx_http_v3_uni_stream_t *us; + ngx_connection_t *pc; + ngx_http_connection_t *phc; ngx_http_v3_connection_t *h3c; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init connection"); - - hc = c->data; - - if (c->quic == NULL) { - h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t)); - if (h3c == NULL) { - return NGX_ERROR; - } + pc = c->quic->parent; + phc = pc->data; - h3c->hc = *hc; - - ngx_queue_init(&h3c->blocked); - ngx_queue_init(&h3c->pushing); - - c->data = h3c; + if (phc->http3) { return NGX_OK; } - if (ngx_http_v3_send_settings(c) == NGX_ERROR) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init session"); + + h3c = ngx_pcalloc(pc->pool, sizeof(ngx_http_v3_connection_t)); + if (h3c == NULL) { return NGX_ERROR; } - if ((c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { - return NGX_OK; - } + h3c->hc = *phc; + h3c->hc.http3 = 1; + + ngx_queue_init(&h3c->blocked); + ngx_queue_init(&h3c->pushing); + + pc->data = h3c; + + return ngx_http_v3_send_settings(c); +} + + +void +ngx_http_v3_init_uni_stream(ngx_connection_t *c) +{ + ngx_http_v3_uni_stream_t *us; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - NULL); - return NGX_ERROR; + ngx_http_close_connection(c); + return; } us->index = -1; @@ -88,8 +93,6 @@ ngx_http_v3_init_connection(ngx_connection_t *c) c->write->handler = ngx_http_v3_dummy_write_handler; ngx_http_v3_read_uni_stream_type(c->read); - - return NGX_DONE; } @@ -478,10 +481,6 @@ ngx_http_v3_send_settings(ngx_connection_t *c) h3c = c->quic->parent->data; - if (h3c->settings_sent) { - return NGX_OK; - } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); @@ -512,17 +511,12 @@ ngx_http_v3_send_settings(ngx_connection_t *c) goto failed; } - h3c->settings_sent = 1; - return NGX_OK; failed: ngx_http_v3_close_uni_stream(cc); - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - "could not send settings"); - return NGX_ERROR; } -- cgit From 4e312daa7ec04e52cdefcbc8749ef2f6d366064b Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 22 Jan 2021 15:57:41 +0300 Subject: HTTP/3: client pseudo-headers restrictions. - :method, :path and :scheme are expected exactly once and not empty - :method and :scheme character validation is added - :authority cannot appear more than once --- src/http/v3/ngx_http_v3_request.c | 92 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 09c1ec335..59a8889bf 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -300,6 +300,7 @@ static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value) { + u_char ch, c; ngx_uint_t i; if (r->request_line.len) { @@ -310,6 +311,18 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) { + if (r->method_name.len) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":method\" header"); + goto failed; + } + + if (value->len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent empty \":method\" header"); + goto failed; + } + r->method_name = *value; for (i = 0; i < sizeof(ngx_http_v3_methods) @@ -325,6 +338,16 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, } } + for (i = 0; i < value->len; i++) { + ch = value->data[i]; + + if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid method: \"%V\"", value); + goto failed; + } + } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http3 method \"%V\" %ui", value, r->method); return NGX_OK; @@ -332,6 +355,18 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, if (name->len == 5 && ngx_strncmp(name->data, ":path", 5) == 0) { + if (r->uri_start) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":path\" header"); + goto failed; + } + + if (value->len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent empty \":path\" header"); + goto failed; + } + r->uri_start = value->data; r->uri_end = value->data + value->len; @@ -349,6 +384,39 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, if (name->len == 7 && ngx_strncmp(name->data, ":scheme", 7) == 0) { + if (r->schema.len) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":scheme\" header"); + goto failed; + } + + if (value->len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent empty \":scheme\" header"); + goto failed; + } + + for (i = 0; i < value->len; i++) { + ch = value->data[i]; + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + continue; + } + + if (((ch >= '0' && ch <= '9') + || ch == '+' || ch == '-' || ch == '.') + && i > 0) + { + continue; + } + + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \":scheme\" header: \"%V\"", + value); + goto failed; + } + r->schema = *value; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -358,6 +426,12 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, if (name->len == 10 && ngx_strncmp(name->data, ":authority", 10) == 0) { + if (r->host_start) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":authority\" header"); + goto failed; + } + r->host_start = value->data; r->host_end = value->data + value->len; @@ -388,6 +462,24 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r) return NGX_OK; } + if (r->method_name.len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent no \":method\" header"); + goto failed; + } + + if (r->schema.len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent no \":scheme\" header"); + goto failed; + } + + if (r->uri_start == NULL) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent no \":path\" header"); + goto failed; + } + len = r->method_name.len + 1 + (r->uri_end - r->uri_start) + 1 + sizeof("HTTP/3.0") - 1; -- cgit From 88f6b969e6963d5771b708adeb43d1c8de14db6e Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Jan 2021 21:08:55 +0000 Subject: HTTP/3: added comment. --- src/http/v3/ngx_http_v3.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 9f91ff8f1..0bb2c414b 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -116,6 +116,7 @@ typedef struct { typedef struct { + /* the http connection must be first */ ngx_http_connection_t hc; ngx_http_v3_dynamic_table_t table; -- cgit From 7bac596afb31344cf40c93d8ae1ce87d2b6c76c1 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 18 Jan 2021 13:43:36 +0300 Subject: HTTP/3: client header validation. A header with the name containing null, CR, LF, colon or uppercase characters, is now considered an error. A header with the value containing null, CR or LF, is also considered an error. Also, header is considered invalid unless its name only contains lowercase characters, digits, minus and optionally underscore. Such header can be optionally ignored. --- src/http/v3/ngx_http_v3_request.c | 70 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 59a8889bf..fb1626718 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -13,6 +13,8 @@ static void ngx_http_v3_process_request(ngx_event_t *rev); static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); +static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r, + ngx_str_t *name, ngx_str_t *value); static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); static ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r); @@ -260,8 +262,25 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, { ngx_table_elt_t *h; ngx_http_header_t *hh; + ngx_http_core_srv_conf_t *cscf; ngx_http_core_main_conf_t *cmcf; + if (ngx_http_v3_validate_header(r, name, value) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + + if (r->invalid_header) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (cscf->ignore_invalid_headers) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid header: \"%V\"", name); + + return NGX_OK; + } + } + if (name->len && name->data[0] == ':') { return ngx_http_v3_process_pseudo_header(r, name, value); } @@ -296,6 +315,57 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, } +static ngx_int_t +ngx_http_v3_validate_header(ngx_http_request_t *r, ngx_str_t *name, + ngx_str_t *value) +{ + u_char ch; + ngx_uint_t i; + ngx_http_core_srv_conf_t *cscf; + + r->invalid_header = 0; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + for (i = (name->data[0] == ':'); i != name->len; i++) { + ch = name->data[i]; + + if ((ch >= 'a' && ch <= 'z') + || (ch == '-') + || (ch >= '0' && ch <= '9') + || (ch == '_' && cscf->underscores_in_headers)) + { + continue; + } + + if (ch == '\0' || ch == LF || ch == CR || ch == ':' + || (ch >= 'A' && ch <= 'Z')) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid header name: \"%V\"", name); + + return NGX_ERROR; + } + + r->invalid_header = 1; + } + + for (i = 0; i != value->len; i++) { + ch = value->data[i]; + + if (ch == '\0' || ch == LF || ch == CR) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent header \"%V\" with " + "invalid value: \"%V\"", name, value); + + return NGX_ERROR; + } + } + + return NGX_OK; +} + + static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value) -- cgit From 52d0bf620a964bbb51bd7ff87e778ba72164b802 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 21 Dec 2020 17:35:13 +0000 Subject: HTTP/3: removed HTTP/3-specific code. The ngx_http_set_lingering_close() function is not called for HTTP/3. The change reduces diff to the default branch. --- src/http/ngx_http_request.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index abcc1bba8..b747b0206 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -3451,13 +3451,11 @@ ngx_http_set_lingering_close(ngx_connection_t *c) } } - if (c->fd != NGX_INVALID_FILE) { - if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { - ngx_connection_error(c, ngx_socket_errno, - ngx_shutdown_socket_n " failed"); - ngx_http_close_request(r, 0); - return; - } + if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); + ngx_http_close_request(r, 0); + return; } ngx_add_timer(rev, clcf->lingering_timeout); -- cgit From cd6253430051a823dc31b756e93aeecb5f674af3 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 29 Jan 2021 19:42:47 +0300 Subject: HTTP/3: call ngx_handle_read_event() from client header handler. This function should be called at the end of an event handler to prepare the event for the next handler call. Particularly, the "active" flag is set or cleared depending on data availability. With this call missing in one code path, read handler was not called again after handling the initial part of the client request, if the request was too big to fit into a single STREAM frame. Now ngx_handle_read_event() is called in this code path. Also, read timer is restarted. --- src/http/v3/ngx_http_v3_request.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index fb1626718..0b7954137 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -158,11 +158,12 @@ ngx_http_v3_process_request(ngx_event_t *rev) if (b->pos == b->last) { - if (!rev->ready) { - break; - } + if (rev->ready) { + n = c->recv(c, b->start, b->end - b->start); - n = c->recv(c, b->start, b->end - b->start); + } else { + n = NGX_AGAIN; + } if (n == NGX_AGAIN) { if (!rev->timer_set) { -- cgit From 89dda20510bf7dac952a2dc0b5f29deba376e25f Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Fri, 29 Jan 2021 15:53:47 +0300 Subject: QUIC: stateless retry. Previously, quic connection object was created when Retry packet was sent. This is neither necessary nor convenient, and contradicts the idea of retry: protecting from bad clients and saving server resources. Now, the connection is not created, token is verified cryptographically instead of holding it in connection. --- src/http/modules/ngx_http_quic_module.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 5314af35b..901d1a563 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -346,10 +346,8 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->retry, prev->retry, 0); - if (conf->retry) { - if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) { - return NGX_CONF_ERROR; - } + if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) { + return NGX_CONF_ERROR; } ngx_conf_merge_str_value(conf->sr_token_key, prev->sr_token_key, ""); -- cgit From 6f3c821d1f28c433f778fcc843bb764e45194f5c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 25 Jan 2021 16:16:47 +0300 Subject: HTTP/3: refactored request body parser. The change reduces diff to the default branch for src/http/ngx_http_request_body.c. Also, client Content-Length, if present, is now checked against the real body size sent by client. --- src/http/ngx_http.h | 3 - src/http/ngx_http_request_body.c | 64 ++--- src/http/v3/ngx_http_v3.h | 4 +- src/http/v3/ngx_http_v3_request.c | 503 ++++++++++++++++++++++++++++++++++---- 4 files changed, 488 insertions(+), 86 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 778744d71..a7cd51d53 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -66,9 +66,6 @@ struct ngx_http_chunked_s { ngx_uint_t state; off_t size; off_t length; -#if (NGX_HTTP_V3) - void *h3_parse; -#endif }; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index c0257a21e..05e990bfc 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -87,6 +87,13 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, } #endif +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + rc = ngx_http_v3_read_request_body(r); + goto done; + } +#endif + preread = r->header_in->last - r->header_in->pos; if (preread) { @@ -229,6 +236,18 @@ ngx_http_read_unbuffered_request_body(ngx_http_request_t *r) } #endif +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + rc = ngx_http_v3_read_unbuffered_request_body(r); + + if (rc == NGX_OK) { + r->reading_body = 0; + } + + return rc; + } +#endif + if (r->connection->read->timedout) { r->connection->timedout = 1; return NGX_HTTP_REQUEST_TIME_OUT; @@ -333,10 +352,11 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) } if (n == 0) { - rb->buf->last_buf = 1; + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client prematurely closed connection"); } - if (n == NGX_ERROR) { + if (n == 0 || n == NGX_ERROR) { c->error = 1; return NGX_HTTP_BAD_REQUEST; } @@ -583,8 +603,8 @@ ngx_http_discard_request_body(ngx_http_request_t *r) } #endif -#if (NGX_HTTP_QUIC) - if (r->connection->quic) { +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { return NGX_OK; } #endif @@ -956,15 +976,6 @@ ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) break; } - size = cl->buf->last - cl->buf->pos; - - if (cl->buf->last_buf && (off_t) size < rb->rest) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client prematurely closed connection"); - r->connection->error = 1; - return NGX_HTTP_BAD_REQUEST; - } - tl = ngx_chain_get_free_buf(r->pool, &rb->free); if (tl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -982,6 +993,8 @@ ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) b->end = cl->buf->end; b->flush = r->request_body_no_buffering; + size = cl->buf->last - cl->buf->pos; + if ((off_t) size < rb->rest) { cl->buf->pos = cl->buf->last; rb->rest -= size; @@ -1053,16 +1066,7 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); - switch (r->http_version) { -#if (NGX_HTTP_V3) - case NGX_HTTP_VERSION_30: - rc = ngx_http_v3_parse_request_body(r, cl->buf, rb->chunked); - break; -#endif - - default: /* HTTP/1.x */ - rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked); - } + rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked); if (rc == NGX_OK) { @@ -1146,20 +1150,6 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) continue; } - if (rc == NGX_AGAIN && cl->buf->last_buf) { - - /* last body buffer */ - - if (rb->chunked->length > 0) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client prematurely closed connection"); - r->connection->error = 1; - return NGX_HTTP_BAD_REQUEST; - } - - rc = NGX_DONE; - } - if (rc == NGX_DONE) { /* a whole response has been parsed successfully */ diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 0bb2c414b..510e66bb5 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -133,8 +133,8 @@ typedef struct { void ngx_http_v3_init(ngx_connection_t *c); -ngx_int_t ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, - ngx_http_chunked_t *ctx); +ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r); +ngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r); uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 0b7954137..1c17efadb 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -19,6 +19,10 @@ static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); static ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r); static ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r); +static void ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r, + ngx_chain_t *in); static const struct { @@ -625,12 +629,18 @@ failed: static ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r) { + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + + c = r->connection; + if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) { return NGX_ERROR; } if (r->headers_in.server.len == 0) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent neither \":authority\" nor \"Host\" header"); goto failed; } @@ -642,7 +652,7 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r) r->headers_in.server.len) != 0) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent \":authority\" and \"Host\" headers " "with different values"); goto failed; @@ -655,10 +665,32 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r) r->headers_in.content_length->value.len); if (r->headers_in.content_length_n == NGX_ERROR) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid \"Content-Length\" header"); goto failed; } + + } else { + b = r->header_in; + n = b->last - b->pos; + + if (n == 0) { + n = c->recv(c, b->start, b->end - b->start); + + if (n == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (n > 0) { + b->pos = b->start; + b->last = b->start + n; + } + } + + if (n != 0) { + r->headers_in.chunked = 1; + } } return NGX_OK; @@ -671,74 +703,457 @@ failed: ngx_int_t -ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, - ngx_http_chunked_t *ctx) +ngx_http_v3_read_request_body(ngx_http_request_t *r) +{ + size_t preread; + ngx_int_t rc; + ngx_chain_t *cl, out; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + + rb = r->request_body; + + preread = r->header_in->last - r->header_in->pos; + + if (preread) { + + /* there is the pre-read part of the request body */ + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 client request body preread %uz", preread); + + out.buf = r->header_in; + out.next = NULL; + cl = &out; + + } else { + cl = NULL; + } + + rc = ngx_http_v3_request_body_filter(r, cl); + if (rc != NGX_OK) { + return rc; + } + + if (rb->rest == 0) { + /* the whole request body was pre-read */ + r->request_body_no_buffering = 0; + rb->post_handler(r); + return NGX_OK; + } + + if (rb->rest < 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "negative request body rest"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + rb->buf = ngx_create_temp_buf(r->pool, clcf->client_body_buffer_size); + if (rb->buf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->read_event_handler = ngx_http_v3_read_client_request_body_handler; + r->write_event_handler = ngx_http_request_empty_handler; + + return ngx_http_v3_do_read_client_request_body(r); +} + + +static void +ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + + if (r->connection->read->timedout) { + r->connection->timedout = 1; + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + rc = ngx_http_v3_do_read_client_request_body(r); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + ngx_http_finalize_request(r, rc); + } +} + + +ngx_int_t +ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r) { + ngx_int_t rc; + + if (r->connection->read->timedout) { + r->connection->timedout = 1; + return NGX_HTTP_REQUEST_TIME_OUT; + } + + rc = ngx_http_v3_do_read_client_request_body(r); + + if (rc == NGX_OK) { + r->reading_body = 0; + } + + return rc; +} + + +static ngx_int_t +ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r) +{ + off_t rest; + size_t size; + ssize_t n; ngx_int_t rc; + ngx_chain_t out; ngx_connection_t *c; - ngx_http_v3_parse_data_t *st; - enum { - sw_start = 0, - sw_skip - }; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; c = r->connection; - st = ctx->h3_parse; + rb = r->request_body; - if (st == NULL) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse request body"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 read client request body"); + + for ( ;; ) { + for ( ;; ) { + if (rb->buf->last == rb->buf->end) { + + /* update chains */ + + rc = ngx_http_v3_request_body_filter(r, NULL); + + if (rc != NGX_OK) { + return rc; + } + + if (rb->busy != NULL) { + if (r->request_body_no_buffering) { + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_AGAIN; + } + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "busy buffers after request body flush"); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rb->buf->pos = rb->buf->start; + rb->buf->last = rb->buf->start; + } + + size = rb->buf->end - rb->buf->last; + rest = rb->rest - (rb->buf->last - rb->buf->pos); + + if ((off_t) size > rest) { + size = (size_t) rest; + } + + n = c->recv(c, rb->buf->last, size); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client request body recv %z", n); + + if (n == NGX_AGAIN) { + break; + } - st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_data_t)); + if (n == 0) { + rb->buf->last_buf = 1; + } + + if (n == NGX_ERROR) { + c->error = 1; + return NGX_HTTP_BAD_REQUEST; + } + + rb->buf->last += n; + + /* pass buffer to request body filter chain */ + + out.buf = rb->buf; + out.next = NULL; + + rc = ngx_http_v3_request_body_filter(r, &out); + + if (rc != NGX_OK) { + return rc; + } + + if (rb->rest == 0) { + break; + } + + if (rb->buf->last < rb->buf->end) { + break; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client request body rest %O", rb->rest); + + if (rb->rest == 0) { + break; + } + + if (!c->read->ready) { + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + ngx_add_timer(c->read, clcf->client_body_timeout); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_AGAIN; + } + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (!r->request_body_no_buffering) { + r->read_event_handler = ngx_http_block_reading; + rb->post_handler(r); + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + off_t max; + size_t size; + ngx_int_t rc; + ngx_buf_t *b; + ngx_uint_t last; + ngx_chain_t *cl, *out, *tl, **ll; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + ngx_http_v3_parse_data_t *st; + + rb = r->request_body; + st = r->h3_parse; + + if (rb->rest == -1) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 request body filter"); + + st = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_parse_data_t)); if (st == NULL) { - goto failed; + return NGX_HTTP_INTERNAL_SERVER_ERROR; } - ctx->h3_parse = st; + r->h3_parse = st; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + rb->rest = cscf->large_client_header_buffers.size; } - while (b->pos < b->last && ctx->size == 0) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - rc = ngx_http_v3_parse_data(c, st, *b->pos++); + max = r->headers_in.content_length_n; - if (rc > 0) { - ngx_http_v3_finalize_connection(c, rc, - "could not parse request body"); - goto failed; + if (max == -1 && clcf->client_max_body_size) { + max = clcf->client_max_body_size; + } + + out = NULL; + ll = &out; + last = 0; + + for (cl = in; cl; cl = cl->next) { + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, + "http3 body buf " + "t:%d f:%d %p, pos %p, size: %z file: %O, size: %O", + cl->buf->temporary, cl->buf->in_file, + cl->buf->start, cl->buf->pos, + cl->buf->last - cl->buf->pos, + cl->buf->file_pos, + cl->buf->file_last - cl->buf->file_pos); + + if (cl->buf->last_buf) { + last = 1; } - if (rc == NGX_ERROR) { - goto failed; + b = NULL; + + while (cl->buf->pos < cl->buf->last) { + + if (st->length == 0) { + r->request_length++; + + rc = ngx_http_v3_parse_data(r->connection, st, *cl->buf->pos++); + + if (rc == NGX_AGAIN) { + continue; + } + + if (rc == NGX_DONE) { + last = 1; + goto done; + } + + if (rc > 0) { + ngx_http_v3_finalize_connection(r->connection, rc, + "client sent invalid body"); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client sent invalid body"); + return NGX_HTTP_BAD_REQUEST; + } + + if (rc == NGX_ERROR) { + ngx_http_v3_finalize_connection(r->connection, + NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "internal error"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* rc == NGX_OK */ + } + + if (max != -1 && (uint64_t) (max - rb->received) < st->length) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client intended to send too large " + "body: %O+%uL bytes", + rb->received, st->length); + + return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + if (b + && st->length <= 128 + && (uint64_t) (cl->buf->last - cl->buf->pos) >= st->length) + { + rb->received += st->length; + r->request_length += st->length; + + if (st->length < 8) { + + while (st->length) { + *b->last++ = *cl->buf->pos++; + st->length--; + } + + } else { + ngx_memmove(b->last, cl->buf->pos, st->length); + b->last += st->length; + cl->buf->pos += st->length; + st->length = 0; + } + + continue; + } + + tl = ngx_chain_get_free_buf(r->pool, &rb->free); + if (tl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b = tl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->temporary = 1; + b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; + b->start = cl->buf->pos; + b->pos = cl->buf->pos; + b->last = cl->buf->last; + b->end = cl->buf->end; + b->flush = r->request_body_no_buffering; + + *ll = tl; + ll = &tl->next; + + size = cl->buf->last - cl->buf->pos; + + if (size > st->length) { + cl->buf->pos += (size_t) st->length; + rb->received += st->length; + r->request_length += st->length; + st->length = 0; + + } else { + st->length -= size; + rb->received += size; + r->request_length += size; + cl->buf->pos = cl->buf->last; + } + + b->last = cl->buf->pos; } + } - if (rc == NGX_AGAIN) { - ctx->state = sw_skip; - continue; +done: + + if (last) { + + if (st->length > 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client prematurely closed stream"); + r->connection->error = 1; + return NGX_HTTP_BAD_REQUEST; } - if (rc == NGX_DONE) { - return NGX_DONE; + if (r->headers_in.content_length_n == -1) { + r->headers_in.content_length_n = rb->received; + + } else if (r->headers_in.content_length_n != rb->received) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent less body data than expected: " + "%O out of %O bytes of request body received", + rb->received, r->headers_in.content_length_n); + return NGX_HTTP_BAD_REQUEST; } - /* rc == NGX_OK */ + rb->rest = 0; - ctx->size = st->length; - ctx->state = sw_start; - } + tl = ngx_chain_get_free_buf(r->pool, &rb->free); + if (tl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } - if (ctx->state == sw_skip) { - ctx->length = 1; - return NGX_AGAIN; - } + b = tl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->last_buf = 1; + + *ll = tl; + ll = &tl->next; + + } else { - if (b->pos == b->last) { - ctx->length = ctx->size; - return NGX_AGAIN; + /* set rb->rest, amount of data we want to see next time */ + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + rb->rest = (off_t) cscf->large_client_header_buffers.size; } - return NGX_OK; + rc = ngx_http_top_request_body_filter(r, out); -failed: + ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, + (ngx_buf_tag_t) &ngx_http_read_client_request_body); - return NGX_ERROR; + return rc; } -- cgit From a373d2851b33191e4f82cdec911914b04c4a4f23 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 1 Feb 2021 18:48:18 +0300 Subject: HTTP/3: fixed format specifier. --- src/http/v3/ngx_http_v3_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 1c17efadb..df58f383a 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1034,7 +1034,7 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) if (max != -1 && (uint64_t) (max - rb->received) < st->length) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client intended to send too large " - "body: %O+%uL bytes", + "body: %O+%ui bytes", rb->received, st->length); return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; -- cgit From 365c8b7914033c05fc1e564684dade448fc65671 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 2 Feb 2021 15:09:48 +0300 Subject: HTTP/3: reverted version check for keepalive flag. The flag is used in ngx_http_finalize_connection() to switch client connection to the keepalive mode. Since eaea7dac3292 this code is not executed for HTTP/3 which allows us to revert the change and get back to the default branch code. --- src/http/ngx_http_core_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index ef8b649ef..d4e1910bf 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -819,7 +819,7 @@ ngx_http_handler(ngx_http_request_t *r) if (!r->internal) { switch (r->headers_in.connection_type) { case 0: - r->keepalive = (r->http_version == NGX_HTTP_VERSION_11); + r->keepalive = (r->http_version > NGX_HTTP_VERSION_10); break; case NGX_HTTP_CONNECTION_CLOSE: -- cgit From eab61bfc22b583031b5a6f42f9c277223ce18672 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Mon, 8 Feb 2021 16:49:33 +0300 Subject: QUIC: the "quic_host_key" directive. The token generation in QUIC is reworked. Single host key is used to generate all required keys of needed sizes using HKDF. The "quic_stateless_reset_token_key" directive is removed. Instead, the "quic_host_key" directive is used, which reads key from file, or sets it to random bytes if not specified. --- src/http/modules/ngx_http_quic_module.c | 149 ++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 16 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 901d1a563..8106e3e0a 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -9,6 +9,8 @@ #include #include +#include + static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -20,7 +22,8 @@ static char *ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data); - +static char *ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_conf_post_t ngx_http_quic_max_ack_delay_post = { ngx_http_quic_max_ack_delay }; @@ -125,11 +128,11 @@ static ngx_command_t ngx_http_quic_commands[] = { offsetof(ngx_quic_conf_t, retry), NULL }, - { ngx_string("quic_stateless_reset_token_key"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_str_slot, + { ngx_string("quic_host_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_quic_host_key, NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_quic_conf_t, sr_token_key), + 0, NULL }, ngx_null_command @@ -174,6 +177,8 @@ static ngx_http_variable_t ngx_http_quic_vars[] = { ngx_http_null_variable }; +static ngx_str_t ngx_http_quic_salt = ngx_string("ngx_quic"); + ngx_int_t ngx_http_quic_init(ngx_connection_t *c) @@ -270,7 +275,7 @@ ngx_http_quic_create_srv_conf(ngx_conf_t *cf) * conf->tp.sr_token = { 0 } * conf->tp.sr_enabled = 0 * conf->tp.preferred_address = NULL - * conf->sr_token_key = { 0, NULL } + * conf->host_key = { 0, NULL } */ conf->tp.max_idle_timeout = NGX_CONF_UNSET_MSEC; @@ -346,25 +351,39 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->retry, prev->retry, 0); - if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) { - return NGX_CONF_ERROR; - } - - ngx_conf_merge_str_value(conf->sr_token_key, prev->sr_token_key, ""); + ngx_conf_merge_str_value(conf->host_key, prev->host_key, ""); - if (conf->sr_token_key.len == 0) { - conf->sr_token_key.len = NGX_QUIC_DEFAULT_SRT_KEY_LEN; + if (conf->host_key.len == 0) { - conf->sr_token_key.data = ngx_pnalloc(cf->pool, conf->sr_token_key.len); - if (conf->sr_token_key.data == NULL) { + conf->host_key.len = NGX_QUIC_DEFAULT_HOST_KEY_LEN; + conf->host_key.data = ngx_palloc(cf->pool, conf->host_key.len); + if (conf->host_key.data == NULL) { return NGX_CONF_ERROR; } - if (RAND_bytes(conf->sr_token_key.data, conf->sr_token_key.len) <= 0) { + if (RAND_bytes(conf->host_key.data, NGX_QUIC_DEFAULT_HOST_KEY_LEN) + <= 0) + { return NGX_CONF_ERROR; } } + if (ngx_quic_derive_key(cf->log, "av_token_key", + &conf->host_key, &ngx_http_quic_salt, + conf->av_token_key, NGX_QUIC_AV_KEY_LEN) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_quic_derive_key(cf->log, "sr_token_key", + &conf->host_key, &ngx_http_quic_salt, + conf->sr_token_key, NGX_QUIC_SR_KEY_LEN) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); conf->ssl = &sscf->ssl; @@ -407,3 +426,101 @@ ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data) return NGX_CONF_OK; } + + +static char * +ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_quic_conf_t *qcf = conf; + + u_char *buf; + size_t size; + ssize_t n; + ngx_str_t *value; + ngx_file_t file; + ngx_file_info_t fi; + + if (qcf->host_key.len) { + return "is duplicate"; + } + + buf = NULL; +#if (NGX_SUPPRESS_WARN) + size = 0; +#endif + + value = cf->args->elts; + + if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&file, sizeof(ngx_file_t)); + file.name = value[1]; + file.log = cf->log; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + + if (file.fd == NGX_INVALID_FILE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + ngx_open_file_n " \"%V\" failed", &file.name); + return NGX_CONF_ERROR; + } + + if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_fd_info_n " \"%V\" failed", &file.name); + goto failed; + } + + size = ngx_file_size(&fi); + + if (size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" zero key size", &file.name); + goto failed; + } + + buf = ngx_pnalloc(cf->pool, size); + if (buf == NULL) { + goto failed; + } + + n = ngx_read_file(&file, buf, size, 0); + + if (n == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_read_file_n " \"%V\" failed", &file.name); + goto failed; + } + + if ((size_t) n != size) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, 0, + ngx_read_file_n " \"%V\" returned only " + "%z bytes instead of %uz", &file.name, n, size); + goto failed; + } + + qcf->host_key.data = buf; + qcf->host_key.len = n; + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%V\" failed", &file.name); + } + + return NGX_CONF_OK; + +failed: + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%V\" failed", &file.name); + } + + if (buf) { + ngx_explicit_memzero(buf, size); + } + + return NGX_CONF_ERROR; +} -- cgit From c83be09720cf8dff041db6581d9df26c88bd3463 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 16 Feb 2021 18:50:01 +0300 Subject: HTTP/3: removed http3_max_field_size. Instead, size of one large_client_header_buffers buffer is used. --- src/http/v3/ngx_http_v3.h | 2 -- src/http/v3/ngx_http_v3_module.c | 12 ------------ src/http/v3/ngx_http_v3_parse.c | 10 +++++----- 3 files changed, 5 insertions(+), 19 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 510e66bb5..5223d8f75 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -46,7 +46,6 @@ #define NGX_HTTP_V3_STREAM_SERVER_DECODER 5 #define NGX_HTTP_V3_MAX_KNOWN_STREAM 6 -#define NGX_HTTP_V3_DEFAULT_MAX_FIELD_SIZE 4096 #define NGX_HTTP_V3_DEFAULT_MAX_TABLE_CAPACITY 16384 #define NGX_HTTP_V3_DEFAULT_MAX_BLOCKED_STREAMS 16 #define NGX_HTTP_V3_DEFAULT_MAX_CONCURRENT_PUSHES 10 @@ -86,7 +85,6 @@ typedef struct { ngx_quic_tp_t quic; - size_t max_field_size; size_t max_table_capacity; ngx_uint_t max_blocked_streams; ngx_uint_t max_concurrent_pushes; diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 89748f5f4..18da90aab 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -24,13 +24,6 @@ static char *ngx_http_v3_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_command_t ngx_http_v3_commands[] = { - { ngx_string("http3_max_field_size"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, max_field_size), - NULL }, - { ngx_string("http3_max_table_capacity"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, @@ -157,7 +150,6 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) return NULL; } - h3scf->max_field_size = NGX_CONF_UNSET_SIZE; h3scf->max_table_capacity = NGX_CONF_UNSET_SIZE; h3scf->max_blocked_streams = NGX_CONF_UNSET_UINT; h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT; @@ -172,10 +164,6 @@ 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_size_value(conf->max_field_size, - prev->max_field_size, - NGX_HTTP_V3_DEFAULT_MAX_FIELD_SIZE); - ngx_conf_merge_size_value(conf->max_table_capacity, prev->max_table_capacity, NGX_HTTP_V3_DEFAULT_MAX_TABLE_CAPACITY); diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 7c22b482c..b7cf242ba 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -488,8 +488,8 @@ static ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, u_char ch) { - ngx_uint_t n; - ngx_http_v3_srv_conf_t *h3scf; + ngx_uint_t n; + ngx_http_core_srv_conf_t *cscf; enum { sw_start = 0, sw_value @@ -505,11 +505,11 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, n = st->length; - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + cscf = ngx_http_v3_get_module_srv_conf(c, ngx_http_core_module); - if (n > h3scf->max_field_size) { + if (n > cscf->large_client_header_buffers.size) { ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client exceeded http3_max_field_size limit"); + "client sent too large header field"); return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; } -- cgit From ffb099bf52e70c0cbdb1ed5555645f12ec6b2322 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 17 Feb 2021 15:56:34 +0300 Subject: HTTP/3: introduced ngx_http_v3_parse_t structure. The structure is used to parse an HTTP/3 request. An object of this type is added to ngx_http_request_t instead of h3_parse generic pointer. Also, the new field is located outside of the request ephemeral zone to keep it safe after request headers are parsed. --- src/http/ngx_http.h | 1 + src/http/ngx_http_request.h | 5 +---- src/http/v3/ngx_http_v3.h | 6 ++++++ src/http/v3/ngx_http_v3_request.c | 27 ++++++++------------------- 4 files changed, 16 insertions(+), 23 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index a7cd51d53..f5d772e65 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -20,6 +20,7 @@ typedef struct ngx_http_file_cache_s ngx_http_file_cache_t; typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; typedef struct ngx_http_chunked_s ngx_http_chunked_t; typedef struct ngx_http_v2_stream_s ngx_http_v2_stream_t; +typedef struct ngx_http_v3_parse_s ngx_http_v3_parse_t; typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 4121e3c3b..5231ad6f2 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -447,6 +447,7 @@ struct ngx_http_request_s { ngx_http_connection_t *http_connection; ngx_http_v2_stream_t *stream; + ngx_http_v3_parse_t *v3_parse; ngx_http_log_handler_pt log_handler; @@ -596,10 +597,6 @@ struct ngx_http_request_s { u_char *port_start; u_char *port_end; -#if (NGX_HTTP_V3) - void *h3_parse; -#endif - unsigned http_minor:16; unsigned http_major:16; }; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 5223d8f75..2b0693975 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -98,6 +98,12 @@ typedef struct { } ngx_http_v3_loc_conf_t; +struct ngx_http_v3_parse_s { + ngx_http_v3_parse_headers_t headers; + ngx_http_v3_parse_data_t body; +}; + + typedef struct { ngx_str_t name; ngx_str_t value; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index df58f383a..ef3053689 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -112,6 +112,12 @@ ngx_http_v3_init(ngx_connection_t *c) r->http_version = NGX_HTTP_VERSION_30; + r->v3_parse = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_parse_t)); + if (r->v3_parse == NULL) { + ngx_http_close_connection(c); + return; + } + c->data = r; rev = c->read; @@ -144,17 +150,7 @@ ngx_http_v3_process_request(ngx_event_t *rev) return; } - st = r->h3_parse; - - if (st == NULL) { - st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_headers_t)); - if (st == NULL) { - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return; - } - - r->h3_parse = st; - } + st = &r->v3_parse->headers; b = r->header_in; @@ -949,20 +945,13 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_http_v3_parse_data_t *st; rb = r->request_body; - st = r->h3_parse; + st = &r->v3_parse->body; if (rb->rest == -1) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http3 request body filter"); - st = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_parse_data_t)); - if (st == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - r->h3_parse = st; - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); rb->rest = cscf->large_client_header_buffers.size; -- cgit From e0425791d484b8e1e77cf39f6ca4da33b5c6e3a3 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 17 Feb 2021 11:58:32 +0300 Subject: HTTP/3: limited client header size. The limit is the size of all large client header buffers. Client header size is the total size of all client header names and values. --- src/http/v3/ngx_http_v3.h | 1 + src/http/v3/ngx_http_v3_request.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 2b0693975..4c5c8e66c 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -99,6 +99,7 @@ typedef struct { struct ngx_http_v3_parse_s { + size_t header_limit; ngx_http_v3_parse_headers_t headers; ngx_http_v3_parse_data_t body; }; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index ef3053689..689d9fc61 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -118,6 +118,9 @@ ngx_http_v3_init(ngx_connection_t *c) return; } + r->v3_parse->header_limit = cscf->large_client_header_buffers.size + * cscf->large_client_header_buffers.num; + c->data = r; rev = c->read; @@ -261,11 +264,23 @@ static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value) { + size_t len; ngx_table_elt_t *h; ngx_http_header_t *hh; ngx_http_core_srv_conf_t *cscf; ngx_http_core_main_conf_t *cmcf; + len = name->len + value->len; + + if (len > r->v3_parse->header_limit) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent too large header"); + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); + return NGX_ERROR; + } + + r->v3_parse->header_limit -= len; + if (ngx_http_v3_validate_header(r, name, value) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return NGX_ERROR; -- cgit From 7f348b2d1f3bca54227c3d24fb017ec8787cd8a2 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 10 Mar 2021 17:56:34 +0300 Subject: HTTP/3: fixed server push. --- src/http/v3/ngx_http_v3_filter_module.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 9a2313a14..8d601c978 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -884,6 +884,7 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, ngx_str_set(&r->http_protocol, "HTTP/3.0"); + r->http_version = NGX_HTTP_VERSION_30; r->method_name = ngx_http_core_get_method; r->method = NGX_HTTP_GET; -- cgit From 780de6de44b066a253a455a061efb09ff8060d09 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 16 Mar 2021 13:48:28 +0300 Subject: QUIC: fixed hq ALPN id for the final draft. It was an agreement to use "hq-interop"[1] for interoperability testing. [1] https://github.com/quicwg/base-drafts/wiki/ALPN-IDs-used-with-QUIC --- src/http/modules/ngx_http_quic_module.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.h b/src/http/modules/ngx_http_quic_module.h index bc75dd501..21d4a40a1 100644 --- a/src/http/modules/ngx_http_quic_module.h +++ b/src/http/modules/ngx_http_quic_module.h @@ -14,7 +14,7 @@ #include -#define NGX_HTTP_QUIC_ALPN_ADVERTISE "\x02hq" +#define NGX_HTTP_QUIC_ALPN_ADVERTISE "\x0Ahq-interop" #define NGX_HTTP_QUIC_ALPN_DRAFT_FMT "\x05hq-%02uD" -- cgit From e522bb69f9e4fa480dfe875a559445ec7831cec9 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 16 Mar 2021 13:48:29 +0300 Subject: HTTP/3: do not push until a MAX_PUSH_ID frame is received. Fixes interop with quic-go that doesn't send MAX_PUSH_ID. --- src/http/v3/ngx_http_v3_filter_module.c | 6 ++++-- src/http/v3/ngx_http_v3_streams.c | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 8d601c978..30e38fcfb 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -787,7 +787,7 @@ ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 push \"%V\" pushing:%ui/%ui id:%uL/%uL", + "http3 push \"%V\" pushing:%ui/%ui id:%uL/%L", path, h3c->npushing, h3scf->max_concurrent_pushes, h3c->next_push_id, h3c->max_push_id); @@ -797,7 +797,9 @@ ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, return NGX_DECLINED; } - if (h3c->next_push_id > h3c->max_push_id) { + if (h3c->max_push_id == (uint64_t) -1 + || h3c->next_push_id > h3c->max_push_id) + { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 abort pushes due to max_push_id"); return NGX_ABORT; diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 871914065..c27fa16dc 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -62,6 +62,7 @@ ngx_http_v3_init_session(ngx_connection_t *c) h3c->hc = *phc; h3c->hc.http3 = 1; + h3c->max_push_id = (uint64_t) -1; ngx_queue_init(&h3c->blocked); ngx_queue_init(&h3c->pushing); @@ -762,7 +763,7 @@ ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 MAX_PUSH_ID:%uL", max_push_id); - if (max_push_id < h3c->max_push_id) { + if (h3c->max_push_id != (uint64_t) -1 && max_push_id < h3c->max_push_id) { return NGX_HTTP_V3_ERR_ID_ERROR; } -- cgit From 190b5d961c0c9b0942dd1a2d8cd609416d0d5114 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 15 Mar 2021 19:26:04 +0300 Subject: HTTP/3: send GOAWAY when last request is accepted. The last request in connection is determined according to the keepalive_requests directive. Requests beyond keepalive_requests are rejected. --- src/http/v3/ngx_http_v3.h | 1 + src/http/v3/ngx_http_v3_request.c | 21 +++++++++++++++++++++ src/http/v3/ngx_http_v3_streams.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 4c5c8e66c..a8a5c5cd4 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -161,6 +161,7 @@ ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); void ngx_http_v3_init_uni_stream(ngx_connection_t *c); ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id); +ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value); ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 689d9fc61..0c055ba0e 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -52,10 +52,12 @@ void ngx_http_v3_init(ngx_connection_t *c) { size_t size; + uint64_t n; ngx_buf_t *b; ngx_event_t *rev; ngx_http_request_t *r; ngx_http_connection_t *hc; + ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; if (ngx_http_v3_init_session(c) != NGX_OK) { @@ -74,6 +76,25 @@ ngx_http_v3_init(ngx_connection_t *c) hc = c->data; + clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + + n = c->quic->id >> 2; + + if (n >= clcf->keepalive_requests) { + ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_REQUEST_REJECTED); + ngx_http_close_connection(c); + return; + } + + if (n + 1 == clcf->keepalive_requests) { + if (ngx_http_v3_send_goaway(c, (n + 1) << 2) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "goaway error"); + ngx_http_close_connection(c); + return; + } + } + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); size = cscf->client_header_buffer_size; diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index c27fa16dc..e09556c93 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -522,6 +522,40 @@ failed: } +ngx_int_t +ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3]; + size_t n; + ngx_connection_t *cc; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send goaway %uL", id); + + cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); + if (cc == NULL) { + return NGX_DECLINED; + } + + n = ngx_http_v3_encode_varlen_int(NULL, id); + p = (u_char *) ngx_http_v3_encode_varlen_int(buf, NGX_HTTP_V3_FRAME_GOAWAY); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, id); + n = p - buf; + + if (cc->send(cc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_http_v3_close_uni_stream(cc); + + return NGX_ERROR; +} + + ngx_int_t ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value) -- cgit From 9533df5b728833dd516f44d18953a3731c29e787 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 15 Mar 2021 16:39:33 +0300 Subject: QUIC: connection shutdown. The function ngx_quic_shutdown_connection() waits until all non-cancelable streams are closed, and then closes the connection. In HTTP/3 cancelable streams are all unidirectional streams except push streams. The function is called from HTTP/3 when client reaches keepalive_requests. --- src/http/v3/ngx_http_v3.h | 3 +++ src/http/v3/ngx_http_v3_request.c | 3 +++ src/http/v3/ngx_http_v3_streams.c | 4 ++++ 3 files changed, 10 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index a8a5c5cd4..be0ed127d 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -82,6 +82,9 @@ #define ngx_http_v3_finalize_connection(c, code, reason) \ ngx_quic_finalize_connection(c->quic->parent, code, reason) +#define ngx_http_v3_shutdown_connection(c, code, reason) \ + ngx_quic_shutdown_connection(c->quic->parent, code, reason) + typedef struct { ngx_quic_tp_t quic; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 0c055ba0e..d4a5faccf 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -93,6 +93,9 @@ ngx_http_v3_init(ngx_connection_t *c) ngx_http_close_connection(c); return; } + + ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "reached maximum number of requests"); } cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index e09556c93..eac49a659 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -80,6 +80,8 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); + c->quic->cancelable = 1; + us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { ngx_http_close_connection(c); @@ -436,6 +438,8 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) return NULL; } + sc->quic->cancelable = 1; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create uni stream, type:%ui", type); -- cgit From f4ab680bcb34d832a1dfd086e3641bf78c0a207c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 30 Mar 2021 16:48:38 +0300 Subject: HTTP/3: keepalive timeout. This timeout limits the time when no client request streams exist. --- src/http/v3/ngx_http_v3.h | 8 ++++++++ src/http/v3/ngx_http_v3_request.c | 36 ++++++++++++++++++++++++++++++++++ src/http/v3/ngx_http_v3_streams.c | 41 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index be0ed127d..7fba09056 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -74,6 +74,11 @@ #define NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR 0x202 +#define ngx_http_v3_get_module_loc_conf(c, module) \ + ngx_http_get_module_loc_conf( \ + ((ngx_http_v3_connection_t *) c->quic->parent->data)->hc.conf_ctx, \ + module) + #define ngx_http_v3_get_module_srv_conf(c, module) \ ngx_http_get_module_srv_conf( \ ((ngx_http_v3_connection_t *) c->quic->parent->data)->hc.conf_ctx, \ @@ -128,6 +133,9 @@ typedef struct { ngx_http_connection_t hc; ngx_http_v3_dynamic_table_t table; + ngx_event_t keepalive; + ngx_uint_t nrequests; + ngx_queue_t blocked; ngx_uint_t nblocked; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index d4a5faccf..b997c29a1 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,6 +10,7 @@ #include +static void ngx_http_v3_cleanup_request(void *data); static void ngx_http_v3_process_request(ngx_event_t *rev); static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); @@ -55,8 +56,10 @@ ngx_http_v3_init(ngx_connection_t *c) uint64_t n; ngx_buf_t *b; ngx_event_t *rev; + ngx_pool_cleanup_t *cln; ngx_http_request_t *r; ngx_http_connection_t *hc; + ngx_http_v3_connection_t *h3c; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; @@ -98,6 +101,22 @@ ngx_http_v3_init(ngx_connection_t *c) "reached maximum number of requests"); } + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + ngx_http_close_connection(c); + return; + } + + cln->handler = ngx_http_v3_cleanup_request; + cln->data = c; + + h3c = c->quic->parent->data; + h3c->nrequests++; + + if (h3c->keepalive.timer_set) { + ngx_del_timer(&h3c->keepalive); + } + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); size = cscf->client_header_buffer_size; @@ -154,6 +173,23 @@ ngx_http_v3_init(ngx_connection_t *c) } +static void +ngx_http_v3_cleanup_request(void *data) +{ + ngx_connection_t *c = data; + + ngx_http_core_loc_conf_t *clcf; + ngx_http_v3_connection_t *h3c; + + h3c = c->quic->parent->data; + + if (--h3c->nrequests == 0) { + clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); + ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout); + } +} + + static void ngx_http_v3_process_request(ngx_event_t *rev) { diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index eac49a659..f2036982f 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -29,6 +29,8 @@ typedef struct { } ngx_http_v3_push_t; +static void ngx_http_v3_keepalive_handler(ngx_event_t *ev); +static void ngx_http_v3_cleanup_session(void *data); static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); static void ngx_http_v3_read_uni_stream_type(ngx_event_t *rev); static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); @@ -43,6 +45,7 @@ ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c) { ngx_connection_t *pc; + ngx_pool_cleanup_t *cln; ngx_http_connection_t *phc; ngx_http_v3_connection_t *h3c; @@ -67,12 +70,50 @@ ngx_http_v3_init_session(ngx_connection_t *c) ngx_queue_init(&h3c->blocked); ngx_queue_init(&h3c->pushing); + h3c->keepalive.log = pc->log; + h3c->keepalive.data = pc; + h3c->keepalive.handler = ngx_http_v3_keepalive_handler; + h3c->keepalive.cancelable = 1; + + cln = ngx_pool_cleanup_add(pc->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_v3_cleanup_session; + cln->data = h3c; + pc->data = h3c; return ngx_http_v3_send_settings(c); } +static void +ngx_http_v3_keepalive_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 keepalive handler"); + + ngx_quic_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "keepalive timeout"); +} + + +static void +ngx_http_v3_cleanup_session(void *data) +{ + ngx_http_v3_connection_t *h3c = data; + + if (h3c->keepalive.timer_set) { + ngx_del_timer(&h3c->keepalive); + } +} + + void ngx_http_v3_init_uni_stream(ngx_connection_t *c) { -- cgit From 25a74b52d14fa0e0bbac4c0d1b3e432bbb31ca97 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 22 Mar 2021 15:51:14 +0300 Subject: HTTP/3: set initial_max_streams_uni default value to 3. The maximum number of HTTP/3 unidirectional client streams we can handle is 3: control, decode and encode. These streams are never closed. --- src/http/modules/ngx_http_quic_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 8106e3e0a..5282bf4bb 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -337,7 +337,7 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) prev->tp.initial_max_streams_bidi, 16); ngx_conf_merge_uint_value(conf->tp.initial_max_streams_uni, - prev->tp.initial_max_streams_uni, 16); + prev->tp.initial_max_streams_uni, 3); ngx_conf_merge_uint_value(conf->tp.ack_delay_exponent, prev->tp.ack_delay_exponent, -- cgit From 7d1cf8ffb442727bc8e54630dd565c8139cead67 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 15 Mar 2021 16:25:54 +0300 Subject: HTTP/3: fixed $connection_requests. Previously, the value was always "1". --- src/http/v3/ngx_http_v3_request.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index b997c29a1..4dc673078 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -165,6 +165,7 @@ ngx_http_v3_init(ngx_connection_t *c) * cscf->large_client_header_buffers.num; c->data = r; + c->requests = n + 1; rev = c->read; rev->handler = ngx_http_v3_process_request; -- cgit From 8ba7adf037cc32300e19034e371a7add5222e47e Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 12 Apr 2021 12:30:30 +0300 Subject: HTTP/3: removed h3scf->quic leftover after 0d2b2664b41c. --- src/http/v3/ngx_http_v3.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 7fba09056..18b7a7636 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -92,7 +92,6 @@ typedef struct { - ngx_quic_tp_t quic; size_t max_table_capacity; ngx_uint_t max_blocked_streams; ngx_uint_t max_concurrent_pushes; -- cgit From 2fd50ca589d37b4dd66006254d99918f44617cf0 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 16 Apr 2021 19:42:03 +0300 Subject: HTTP/3: keepalive_time support. --- src/http/v3/ngx_http_v3.h | 2 ++ src/http/v3/ngx_http_v3_request.c | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 18b7a7636..45d1a3671 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -143,6 +143,8 @@ typedef struct { uint64_t next_push_id; uint64_t max_push_id; + ngx_uint_t goaway; /* unsigned goaway:1; */ + ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; } ngx_http_v3_connection_t; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 4dc673078..c459efef5 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -81,15 +81,22 @@ ngx_http_v3_init(ngx_connection_t *c) clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); - n = c->quic->id >> 2; + h3c = c->quic->parent->data; - if (n >= clcf->keepalive_requests) { + if (h3c->goaway) { ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_REQUEST_REJECTED); ngx_http_close_connection(c); return; } - if (n + 1 == clcf->keepalive_requests) { + n = c->quic->id >> 2; + + if (n + 1 == clcf->keepalive_requests + || ngx_current_msec - c->quic->parent->start_time + > clcf->keepalive_time) + { + h3c->goaway = 1; + if (ngx_http_v3_send_goaway(c, (n + 1) << 2) != NGX_OK) { ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, "goaway error"); @@ -110,7 +117,6 @@ ngx_http_v3_init(ngx_connection_t *c) cln->handler = ngx_http_v3_cleanup_request; cln->data = c; - h3c = c->quic->parent->data; h3c->nrequests++; if (h3c->keepalive.timer_set) { -- cgit From 2f5bcafdde60c722fe5cd28965613abafb451acb Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 13 Apr 2021 12:38:34 +0300 Subject: QUIC: normalize header inclusion. Stop including QUIC headers with no user-serviceable parts inside. This allows to provide a much cleaner QUIC interface. To cope with that, ngx_quic_derive_key() is now explicitly exported for v3 and quic modules. Additionally, this completely hides the ngx_quic_keys_t internal type. --- src/http/modules/ngx_http_quic_module.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 5282bf4bb..b7661b42c 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -9,8 +9,6 @@ #include #include -#include - static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); -- cgit From a8acca865bc63efd0ae99284af37ed6bb246923b Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Thu, 22 Apr 2021 13:49:18 +0300 Subject: HTTP/3: adjusted control stream parsing. 7.2.1: If a DATA frame is received on a control stream, the recipient MUST respond with a connection error of type H3_FRAME_UNEXPECTED; 7.2.2: If a HEADERS frame is received on a control stream, the recipient MUST respond with a connection error (Section 8) of type H3_FRAME_UNEXPECTED. --- src/http/v3/ngx_http_v3_parse.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index b7cf242ba..18255a677 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -1069,6 +1069,10 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) st->state = sw_max_push_id; break; + case NGX_HTTP_V3_FRAME_DATA: + case NGX_HTTP_V3_FRAME_HEADERS: + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + default: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse skip unknown frame"); -- cgit From acc3ad0060d31609a359b3ace61bc4a0ebb780f5 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 5 May 2021 13:28:05 +0300 Subject: HTTP/3: reject empty DATA and HEADERS frames on control stream. Previously only non-empty frames were rejected. --- src/http/v3/ngx_http_v3_parse.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 18255a677..3d0b09197 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -1026,7 +1026,10 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse frame type:%ui", st->type); - if (ngx_http_v3_is_v2_frame(st->type)) { + if (ngx_http_v3_is_v2_frame(st->type) + || st->type == NGX_HTTP_V3_FRAME_DATA + || st->type == NGX_HTTP_V3_FRAME_HEADERS) + { return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; } @@ -1069,10 +1072,6 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) st->state = sw_max_push_id; break; - case NGX_HTTP_V3_FRAME_DATA: - case NGX_HTTP_V3_FRAME_HEADERS: - return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; - default: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse skip unknown frame"); -- cgit From bbbc80465bae44c4f9a1236683addbc51e7a8710 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 4 May 2021 13:38:59 +0300 Subject: HTTP/3: fixed decoder stream stubs. Now ngx_http_v3_ack_header() and ngx_http_v3_inc_insert_count() always generate decoder error. Our implementation does not use dynamic tables and does not expect client to send Section Acknowledgement or Insert Count Increment. Stream Cancellation, on the other hand, is allowed to be sent anyway. This is why ngx_http_v3_cancel_stream() does not return an error. --- src/http/v3/ngx_http_v3_tables.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index 5389d0e2f..200e8a396 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -390,9 +390,9 @@ ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 ack header %ui", stream_id); - /* XXX */ + /* we do not use dynamic tables */ - return NGX_OK; + return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; } @@ -402,7 +402,7 @@ ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 cancel stream %ui", stream_id); - /* XXX */ + /* we do not use dynamic tables */ return NGX_OK; } @@ -414,9 +414,9 @@ ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 increment insert count %ui", inc); - /* XXX */ + /* we do not use dynamic tables */ - return NGX_OK; + return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; } -- cgit From a40fa4aa9640a574cc6413dabada256f837d8cb9 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 5 May 2021 15:15:17 +0300 Subject: HTTP/3: moved Stream Cancellation stub to ngx_http_v3_streams.c. --- src/http/v3/ngx_http_v3_streams.c | 12 ++++++++++++ src/http/v3/ngx_http_v3_tables.c | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index f2036982f..b300dcc01 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -891,3 +891,15 @@ ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) return NGX_OK; } + + +ngx_int_t +ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 cancel stream %ui", stream_id); + + /* we do not use dynamic tables */ + + return NGX_OK; +} diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index 200e8a396..1b4a73ab0 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -396,18 +396,6 @@ ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) } -ngx_int_t -ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) -{ - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 cancel stream %ui", stream_id); - - /* we do not use dynamic tables */ - - return NGX_OK; -} - - ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) { -- cgit From 82f8734935ef28fbda4450fd88410b7d1f359c62 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 30 Apr 2021 19:10:11 +0300 Subject: HTTP/3: ngx_http_v3_get_session() macro. It's used instead of accessing c->quic->parent->data directly. Apart from being simpler, it allows to change the way session is stored in the future by changing the macro. --- src/http/v3/ngx_http_v3.h | 3 +++ src/http/v3/ngx_http_v3_filter_module.c | 4 ++-- src/http/v3/ngx_http_v3_request.c | 4 ++-- src/http/v3/ngx_http_v3_streams.c | 14 +++++++------- src/http/v3/ngx_http_v3_tables.c | 21 ++++++++++----------- 5 files changed, 24 insertions(+), 22 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 45d1a3671..9ee809fa1 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -74,6 +74,9 @@ #define NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR 0x202 +#define ngx_http_v3_get_session(c) \ + ((ngx_http_v3_connection_t *) (c)->quic->parent->data) + #define ngx_http_v3_get_module_loc_conf(c, module) \ ngx_http_get_module_loc_conf( \ ((ngx_http_v3_connection_t *) c->quic->parent->data)->hc.conf_ctx, \ diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 30e38fcfb..b8b439c24 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -783,7 +783,7 @@ ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, ngx_http_v3_connection_t *h3c; c = r->connection; - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -858,7 +858,7 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, goto failed; } - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); ngx_memcpy(hc, h3c, sizeof(ngx_http_connection_t)); c->data = hc; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index c459efef5..23b827aed 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -81,7 +81,7 @@ ngx_http_v3_init(ngx_connection_t *c) clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); if (h3c->goaway) { ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_REQUEST_REJECTED); @@ -188,7 +188,7 @@ ngx_http_v3_cleanup_request(void *data) ngx_http_core_loc_conf_t *clcf; ngx_http_v3_connection_t *h3c; - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); if (--h3c->nrequests == 0) { clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index b300dcc01..513738469 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -148,7 +148,7 @@ ngx_http_v3_close_uni_stream(ngx_connection_t *c) ngx_http_v3_uni_stream_t *us; us = c->data; - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 close stream"); @@ -178,7 +178,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) c = rev->data; us = c->data; - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read stream type"); @@ -410,7 +410,7 @@ ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) goto failed; } - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); h3c->npushing++; cln->handler = ngx_http_v3_push_cleanup; @@ -466,7 +466,7 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) index = -1; } - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); if (index >= 0) { if (h3c->known_streams[index]) { @@ -525,7 +525,7 @@ ngx_http_v3_send_settings(ngx_connection_t *c) ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_connection_t *h3c; - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); @@ -837,7 +837,7 @@ ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) { ngx_http_v3_connection_t *h3c; - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 MAX_PUSH_ID:%uL", max_push_id); @@ -860,7 +860,7 @@ ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) ngx_http_v3_push_t *push; ngx_http_v3_connection_t *h3c; - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 CANCEL_PUSH:%uL", push_id); diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index 1b4a73ab0..c2ce14660 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -198,7 +198,7 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); dt = &h3c->table; ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -250,8 +250,7 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 set capacity %ui", capacity); - pc = c->quic->parent; - h3c = pc->data; + h3c = ngx_http_v3_get_session(c); h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); if (capacity > h3scf->max_table_capacity) { @@ -278,6 +277,8 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) } if (dt->elts == NULL) { + pc = c->quic->parent; + cln = ngx_pool_cleanup_add(pc->pool, 0); if (cln == NULL) { return NGX_ERROR; @@ -324,7 +325,7 @@ ngx_http_v3_evict(ngx_connection_t *c, size_t need) ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); dt = &h3c->table; if (need > dt->capacity) { @@ -367,7 +368,7 @@ ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); dt = &h3c->table; if (dt->base + dt->nelts <= index) { @@ -451,7 +452,7 @@ ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); dt = &h3c->table; if (index < dt->base || index - dt->base >= dt->nelts) { @@ -494,7 +495,7 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) return NGX_OK; } - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); dt = &h3c->table; h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); @@ -536,15 +537,13 @@ ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) { size_t n; - ngx_connection_t *pc; ngx_pool_cleanup_t *cln; ngx_http_v3_block_t *block; ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; - pc = c->quic->parent; - h3c = pc->data; + h3c = ngx_http_v3_get_session(c); dt = &h3c->table; n = dt->base + dt->nelts; @@ -624,7 +623,7 @@ ngx_http_v3_new_header(ngx_connection_t *c) ngx_http_v3_block_t *block; ngx_http_v3_connection_t *h3c; - h3c = c->quic->parent->data; + h3c = ngx_http_v3_get_session(c); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 new dynamic header, blocked:%ui", h3c->nblocked); -- cgit From 38773a3c1130d34715f1cce24527a10258447354 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 5 May 2021 14:53:36 +0300 Subject: HTTP/3: reference h3c directly from ngx_http_connection_t. Previously, an ngx_http_v3_connection_t object was created for HTTP/3 and then assinged to c->data instead of the generic ngx_http_connection_t object. Now a direct reference is added to ngx_http_connection_t, which is less confusing and does not require a flag for http3. --- src/http/modules/ngx_http_quic_module.c | 2 +- src/http/modules/ngx_http_quic_module.h | 4 ++++ src/http/ngx_http.h | 2 ++ src/http/ngx_http_request.h | 5 ++++- src/http/v3/ngx_http_v3.h | 19 +++++++------------ src/http/v3/ngx_http_v3_streams.c | 23 +++++++++-------------- src/http/v3/ngx_http_v3_tables.c | 7 +++---- 7 files changed, 30 insertions(+), 32 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index b7661b42c..2354dfd8b 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -201,7 +201,7 @@ ngx_http_quic_init(ngx_connection_t *c) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http init quic stream"); - phc = c->quic->parent->data; + phc = ngx_http_quic_get_connection(c); if (phc->ssl_servername) { hc->ssl_servername = phc->ssl_servername; diff --git a/src/http/modules/ngx_http_quic_module.h b/src/http/modules/ngx_http_quic_module.h index 21d4a40a1..8cadfbb87 100644 --- a/src/http/modules/ngx_http_quic_module.h +++ b/src/http/modules/ngx_http_quic_module.h @@ -18,6 +18,10 @@ #define NGX_HTTP_QUIC_ALPN_DRAFT_FMT "\x05hq-%02uD" +#define ngx_http_quic_get_connection(c) \ + ((ngx_http_connection_t *) (c)->quic->parent->data) + + ngx_int_t ngx_http_quic_init(ngx_connection_t *c); diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index f5d772e65..6f2d38d8b 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -21,6 +21,8 @@ typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; typedef struct ngx_http_chunked_s ngx_http_chunked_t; typedef struct ngx_http_v2_stream_s ngx_http_v2_stream_t; typedef struct ngx_http_v3_parse_s ngx_http_v3_parse_t; +typedef struct ngx_http_v3_connection_s + ngx_http_v3_connection_t; typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 5231ad6f2..01ae716fe 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -318,6 +318,10 @@ typedef struct { #endif #endif +#if (NGX_HTTP_V3 || NGX_COMPAT) + ngx_http_v3_connection_t *v3_session; +#endif + ngx_chain_t *busy; ngx_int_t nbusy; @@ -325,7 +329,6 @@ typedef struct { unsigned ssl:1; unsigned proxy_protocol:1; - unsigned http3:1; } ngx_http_connection_t; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 9ee809fa1..10ea94592 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -74,18 +74,15 @@ #define NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR 0x202 -#define ngx_http_v3_get_session(c) \ - ((ngx_http_v3_connection_t *) (c)->quic->parent->data) +#define ngx_http_v3_get_session(c) ngx_http_quic_get_connection(c)->v3_session #define ngx_http_v3_get_module_loc_conf(c, module) \ - ngx_http_get_module_loc_conf( \ - ((ngx_http_v3_connection_t *) c->quic->parent->data)->hc.conf_ctx, \ - module) + ngx_http_get_module_loc_conf(ngx_http_quic_get_connection(c)->conf_ctx, \ + module) #define ngx_http_v3_get_module_srv_conf(c, module) \ - ngx_http_get_module_srv_conf( \ - ((ngx_http_v3_connection_t *) c->quic->parent->data)->hc.conf_ctx, \ - module) + ngx_http_get_module_srv_conf(ngx_http_quic_get_connection(c)->conf_ctx, \ + module) #define ngx_http_v3_finalize_connection(c, code, reason) \ ngx_quic_finalize_connection(c->quic->parent, code, reason) @@ -130,9 +127,7 @@ typedef struct { } ngx_http_v3_dynamic_table_t; -typedef struct { - /* the http connection must be first */ - ngx_http_connection_t hc; +struct ngx_http_v3_connection_s { ngx_http_v3_dynamic_table_t table; ngx_event_t keepalive; @@ -149,7 +144,7 @@ typedef struct { ngx_uint_t goaway; /* unsigned goaway:1; */ ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; -} ngx_http_v3_connection_t; +}; void ngx_http_v3_init(ngx_connection_t *c); diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 513738469..cf3204edd 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -46,13 +46,13 @@ ngx_http_v3_init_session(ngx_connection_t *c) { ngx_connection_t *pc; ngx_pool_cleanup_t *cln; - ngx_http_connection_t *phc; + ngx_http_connection_t *hc; ngx_http_v3_connection_t *h3c; pc = c->quic->parent; - phc = pc->data; + hc = pc->data; - if (phc->http3) { + if (hc->v3_session) { return NGX_OK; } @@ -63,8 +63,6 @@ ngx_http_v3_init_session(ngx_connection_t *c) return NGX_ERROR; } - h3c->hc = *phc; - h3c->hc.http3 = 1; h3c->max_push_id = (uint64_t) -1; ngx_queue_init(&h3c->blocked); @@ -83,7 +81,7 @@ ngx_http_v3_init_session(ngx_connection_t *c) cln->handler = ngx_http_v3_cleanup_session; cln->data = h3c; - pc->data = h3c; + hc->v3_session = h3c; return ngx_http_v3_send_settings(c); } @@ -519,13 +517,10 @@ failed: static ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c) { - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; - size_t n; - ngx_connection_t *cc; - ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_connection_t *h3c; - - h3c = ngx_http_v3_get_session(c); + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; + size_t n; + ngx_connection_t *cc; + ngx_http_v3_srv_conf_t *h3scf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); @@ -534,7 +529,7 @@ ngx_http_v3_send_settings(ngx_connection_t *c) return NGX_DECLINED; } - h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); n = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index c2ce14660..46dcc6734 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -251,7 +251,7 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) "http3 set capacity %ui", capacity); h3c = ngx_http_v3_get_session(c); - h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); if (capacity > h3scf->max_table_capacity) { ngx_log_error(NGX_LOG_INFO, c->log, 0, @@ -498,7 +498,7 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) h3c = ngx_http_v3_get_session(c); dt = &h3c->table; - h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); max_entries = h3scf->max_table_capacity / 32; full_range = 2 * max_entries; @@ -582,8 +582,7 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) } if (block->queue.prev == NULL) { - h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, - ngx_http_v3_module); + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); if (h3c->nblocked == h3scf->max_blocked_streams) { ngx_log_error(NGX_LOG_INFO, c->log, 0, -- cgit From 0ea300d35eb066631a21b2042cfe9cd2ea94b15f Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 5 May 2021 12:54:10 +0300 Subject: HTTP/3: renamed ngx_http_v3_connection_t to ngx_http_v3_session_t. --- src/http/ngx_http.h | 3 +-- src/http/ngx_http_request.h | 2 +- src/http/v3/ngx_http_v3.h | 2 +- src/http/v3/ngx_http_v3_filter_module.c | 14 +++++++------- src/http/v3/ngx_http_v3_request.c | 4 ++-- src/http/v3/ngx_http_v3_streams.c | 34 ++++++++++++++++----------------- src/http/v3/ngx_http_v3_tables.c | 22 ++++++++++----------- 7 files changed, 40 insertions(+), 41 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 6f2d38d8b..70109adc2 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -21,8 +21,7 @@ typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; typedef struct ngx_http_chunked_s ngx_http_chunked_t; typedef struct ngx_http_v2_stream_s ngx_http_v2_stream_t; typedef struct ngx_http_v3_parse_s ngx_http_v3_parse_t; -typedef struct ngx_http_v3_connection_s - ngx_http_v3_connection_t; +typedef struct ngx_http_v3_session_s ngx_http_v3_session_t; typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 01ae716fe..60ef4fec1 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -319,7 +319,7 @@ typedef struct { #endif #if (NGX_HTTP_V3 || NGX_COMPAT) - ngx_http_v3_connection_t *v3_session; + ngx_http_v3_session_t *v3_session; #endif ngx_chain_t *busy; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 10ea94592..d936b4389 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -127,7 +127,7 @@ typedef struct { } ngx_http_v3_dynamic_table_t; -struct ngx_http_v3_connection_s { +struct ngx_http_v3_session_s { ngx_http_v3_dynamic_table_t table; ngx_event_t keepalive; diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index b8b439c24..ce0b478c2 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -775,12 +775,12 @@ static ngx_int_t ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, ngx_chain_t ***ll) { - uint64_t push_id; - ngx_int_t rc; - ngx_chain_t *cl; - ngx_connection_t *c; - ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_connection_t *h3c; + uint64_t push_id; + ngx_int_t rc; + ngx_chain_t *cl; + ngx_connection_t *c; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; c = r->connection; h3c = ngx_http_v3_get_session(c); @@ -838,8 +838,8 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; ngx_http_connection_t *hc; + ngx_http_v3_session_t *h3c; ngx_http_core_srv_conf_t *cscf; - ngx_http_v3_connection_t *h3c; pc = pr->connection; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 23b827aed..9d7ca952d 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -59,7 +59,7 @@ ngx_http_v3_init(ngx_connection_t *c) ngx_pool_cleanup_t *cln; ngx_http_request_t *r; ngx_http_connection_t *hc; - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; @@ -185,8 +185,8 @@ ngx_http_v3_cleanup_request(void *data) { ngx_connection_t *c = data; + ngx_http_v3_session_t *h3c; ngx_http_core_loc_conf_t *clcf; - ngx_http_v3_connection_t *h3c; h3c = ngx_http_v3_get_session(c); diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index cf3204edd..4c0da6917 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -47,7 +47,7 @@ ngx_http_v3_init_session(ngx_connection_t *c) ngx_connection_t *pc; ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; pc = c->quic->parent; hc = pc->data; @@ -58,7 +58,7 @@ ngx_http_v3_init_session(ngx_connection_t *c) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init session"); - h3c = ngx_pcalloc(pc->pool, sizeof(ngx_http_v3_connection_t)); + h3c = ngx_pcalloc(pc->pool, sizeof(ngx_http_v3_session_t)); if (h3c == NULL) { return NGX_ERROR; } @@ -104,7 +104,7 @@ ngx_http_v3_keepalive_handler(ngx_event_t *ev) static void ngx_http_v3_cleanup_session(void *data) { - ngx_http_v3_connection_t *h3c = data; + ngx_http_v3_session_t *h3c = data; if (h3c->keepalive.timer_set) { ngx_del_timer(&h3c->keepalive); @@ -142,7 +142,7 @@ static void ngx_http_v3_close_uni_stream(ngx_connection_t *c) { ngx_pool_t *pool; - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; ngx_http_v3_uni_stream_t *us; us = c->data; @@ -171,7 +171,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) ssize_t n; ngx_int_t index, rc; ngx_connection_t *c; - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; ngx_http_v3_uni_stream_t *us; c = rev->data; @@ -379,12 +379,12 @@ ngx_http_v3_dummy_write_handler(ngx_event_t *wev) ngx_connection_t * ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) { - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 2]; - size_t n; - ngx_connection_t *sc; - ngx_pool_cleanup_t *cln; - ngx_http_v3_push_t *push; - ngx_http_v3_connection_t *h3c; + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 2]; + size_t n; + ngx_connection_t *sc; + ngx_pool_cleanup_t *cln; + ngx_http_v3_push_t *push; + ngx_http_v3_session_t *h3c; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create push stream id:%uL", push_id); @@ -447,7 +447,7 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) size_t n; ngx_int_t index; ngx_connection_t *sc; - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; ngx_http_v3_uni_stream_t *us; switch (type) { @@ -830,7 +830,7 @@ ngx_http_v3_client_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) { - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; h3c = ngx_http_v3_get_session(c); @@ -850,10 +850,10 @@ ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) { - ngx_queue_t *q; - ngx_http_request_t *r; - ngx_http_v3_push_t *push; - ngx_http_v3_connection_t *h3c; + ngx_queue_t *q; + ngx_http_request_t *r; + ngx_http_v3_push_t *push; + ngx_http_v3_session_t *h3c; h3c = ngx_http_v3_get_session(c); diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index 46dcc6734..c7bdc2685 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -189,7 +189,7 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) u_char *p; size_t size; ngx_http_v3_header_t *h; - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; ngx_http_v3_dynamic_table_t *dt; size = ngx_http_v3_table_entry_size(name, value); @@ -243,8 +243,8 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) ngx_connection_t *pc; ngx_pool_cleanup_t *cln; ngx_http_v3_header_t **elts; + ngx_http_v3_session_t *h3c; ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -322,7 +322,7 @@ ngx_http_v3_evict(ngx_connection_t *c, size_t need) size_t size, target; ngx_uint_t n; ngx_http_v3_header_t *h; - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; ngx_http_v3_dynamic_table_t *dt; h3c = ngx_http_v3_get_session(c); @@ -363,7 +363,7 @@ ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) { ngx_str_t name, value; - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; ngx_http_v3_dynamic_table_t *dt; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); @@ -449,7 +449,7 @@ ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value) { ngx_http_v3_header_t *h; - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; ngx_http_v3_dynamic_table_t *dt; h3c = ngx_http_v3_get_session(c); @@ -486,7 +486,7 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) ngx_uint_t max_entries, full_range, max_value, max_wrapped, req_insert_count; ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_connection_t *h3c; + ngx_http_v3_session_t *h3c; ngx_http_v3_dynamic_table_t *dt; /* QPACK 4.5.1.1. Required Insert Count */ @@ -539,8 +539,8 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) size_t n; ngx_pool_cleanup_t *cln; ngx_http_v3_block_t *block; + ngx_http_v3_session_t *h3c; ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_connection_t *h3c; ngx_http_v3_dynamic_table_t *dt; h3c = ngx_http_v3_get_session(c); @@ -617,10 +617,10 @@ ngx_http_v3_unblock(void *data) static ngx_int_t ngx_http_v3_new_header(ngx_connection_t *c) { - ngx_queue_t *q; - ngx_connection_t *bc; - ngx_http_v3_block_t *block; - ngx_http_v3_connection_t *h3c; + ngx_queue_t *q; + ngx_connection_t *bc; + ngx_http_v3_block_t *block; + ngx_http_v3_session_t *h3c; h3c = ngx_http_v3_get_session(c); -- cgit From 891fedf52d2c4a85e724e9abbb403334ec55c861 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 27 Apr 2021 21:32:50 +0300 Subject: HTTP/3: renamed ngx_http_v3_client_XXX() functions. The functions are renamed to ngx_http_v3_send_XXX() similar to ngx_http_v3_send_settings() and ngx_http_v3_send_goaway(). --- src/http/v3/ngx_http_v3.h | 14 +++++++------- src/http/v3/ngx_http_v3_parse.c | 2 +- src/http/v3/ngx_http_v3_streams.c | 15 +++++++-------- src/http/v3/ngx_http_v3_tables.c | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index d936b4389..32d1fd509 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -195,18 +195,18 @@ ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id); ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); -ngx_int_t ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, +ngx_int_t ngx_http_v3_send_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value); -ngx_int_t ngx_http_v3_client_insert(ngx_connection_t *c, ngx_str_t *name, +ngx_int_t ngx_http_v3_send_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value); -ngx_int_t ngx_http_v3_client_set_capacity(ngx_connection_t *c, +ngx_int_t ngx_http_v3_send_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); -ngx_int_t ngx_http_v3_client_duplicate(ngx_connection_t *c, ngx_uint_t index); -ngx_int_t ngx_http_v3_client_ack_header(ngx_connection_t *c, +ngx_int_t ngx_http_v3_send_duplicate(ngx_connection_t *c, ngx_uint_t index); +ngx_int_t ngx_http_v3_send_ack_header(ngx_connection_t *c, ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_client_cancel_stream(ngx_connection_t *c, +ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_client_inc_insert_count(ngx_connection_t *c, +ngx_int_t ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 3d0b09197..def9aadc7 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -309,7 +309,7 @@ done: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers done"); if (st->prefix.insert_count > 0) { - if (ngx_http_v3_client_ack_header(c, c->quic->id) != NGX_OK) { + if (ngx_http_v3_send_ack_header(c, c->quic->id) != NGX_OK) { return NGX_ERROR; } } diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 4c0da6917..a20008c19 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -597,7 +597,7 @@ failed: ngx_int_t -ngx_http_v3_client_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, +ngx_http_v3_send_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value) { u_char *p, buf[NGX_HTTP_V3_PREFIX_INT_LEN * 2]; @@ -643,8 +643,7 @@ failed: ngx_int_t -ngx_http_v3_client_insert(ngx_connection_t *c, ngx_str_t *name, - ngx_str_t *value) +ngx_http_v3_send_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) { u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; size_t n; @@ -693,7 +692,7 @@ failed: ngx_int_t -ngx_http_v3_client_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) +ngx_http_v3_send_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) { u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; size_t n; @@ -720,7 +719,7 @@ ngx_http_v3_client_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) ngx_int_t -ngx_http_v3_client_duplicate(ngx_connection_t *c, ngx_uint_t index) +ngx_http_v3_send_duplicate(ngx_connection_t *c, ngx_uint_t index) { u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; size_t n; @@ -747,7 +746,7 @@ ngx_http_v3_client_duplicate(ngx_connection_t *c, ngx_uint_t index) ngx_int_t -ngx_http_v3_client_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) +ngx_http_v3_send_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) { u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; size_t n; @@ -774,7 +773,7 @@ ngx_http_v3_client_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) ngx_int_t -ngx_http_v3_client_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) +ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) { u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; size_t n; @@ -801,7 +800,7 @@ ngx_http_v3_client_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) ngx_int_t -ngx_http_v3_client_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) +ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) { u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; size_t n; diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index c7bdc2685..f83236c01 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -224,7 +224,7 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) /* TODO increment can be sent less often */ - if (ngx_http_v3_client_inc_insert_count(c, 1) != NGX_OK) { + if (ngx_http_v3_send_inc_insert_count(c, 1) != NGX_OK) { return NGX_ERROR; } -- cgit From 32f98ecbb13acc9c22db4dd7e9bec20eb30b945a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 5 May 2021 15:00:17 +0300 Subject: HTTP/3: moved parsing uni stream type to ngx_http_v3_parse.c. Previously it was parsed in ngx_http_v3_streams.c, while the streams were parsed in ngx_http_v3_parse.c. Now all parsing is done in one file. This simplifies parsing API and cleans up ngx_http_v3_streams.c. --- src/http/v3/ngx_http_v3.h | 1 + src/http/v3/ngx_http_v3_parse.c | 123 ++++++++++++++++++++++++++++---- src/http/v3/ngx_http_v3_parse.h | 17 +++-- src/http/v3/ngx_http_v3_streams.c | 143 ++++++++++---------------------------- 4 files changed, 159 insertions(+), 125 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 32d1fd509..736d448b5 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -169,6 +169,7 @@ uintptr_t ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); void ngx_http_v3_init_uni_stream(ngx_connection_t *c); +ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type); ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id); ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index def9aadc7..fcb39b209 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -14,9 +14,6 @@ ((type) == 0x02 || (type) == 0x06 || (type) == 0x08 || (type) == 0x09) -static ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, - ngx_http_v3_parse_settings_t *st, u_char ch); - static ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, ngx_http_v3_parse_varlen_int_t *st, u_char ch); static ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, @@ -39,11 +36,21 @@ static ngx_int_t ngx_http_v3_parse_header_pbi(ngx_connection_t *c, static ngx_int_t ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, + ngx_http_v3_parse_control_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, + ngx_http_v3_parse_settings_t *st, u_char ch); + +static ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, + ngx_http_v3_parse_encoder_t *st, u_char ch); static ngx_int_t ngx_http_v3_parse_header_inr(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch); static ngx_int_t ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, + ngx_http_v3_parse_decoder_t *st, u_char ch); + static ngx_int_t ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value); @@ -986,11 +993,10 @@ ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, } -ngx_int_t -ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) +static ngx_int_t +ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, + u_char ch) { - ngx_http_v3_parse_control_t *st = data; - ngx_int_t rc; enum { sw_start = 0, @@ -1208,11 +1214,10 @@ done: } -ngx_int_t -ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) +static ngx_int_t +ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, + u_char ch) { - ngx_http_v3_parse_encoder_t *st = data; - ngx_int_t rc; enum { sw_start = 0, @@ -1500,11 +1505,10 @@ done: } -ngx_int_t -ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) +static ngx_int_t +ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, + u_char ch) { - ngx_http_v3_parse_decoder_t *st = data; - ngx_int_t rc; enum { sw_start = 0, @@ -1674,3 +1678,92 @@ done: st->state = sw_start; return NGX_DONE; } + + +ngx_int_t +ngx_http_v3_parse_uni(ngx_connection_t *c, ngx_http_v3_parse_uni_t *st, + u_char ch) +{ + ngx_int_t rc; + enum { + sw_start = 0, + sw_type, + sw_control, + sw_encoder, + sw_decoder, + sw_unknown + }; + + switch (st->state) { + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse uni"); + + st->state = sw_type; + + /* fall through */ + + case sw_type: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_register_uni_stream(c, st->vlint.value); + if (rc != NGX_OK) { + return rc; + } + + switch (st->vlint.value) { + case NGX_HTTP_V3_STREAM_CONTROL: + st->state = sw_control; + break; + + case NGX_HTTP_V3_STREAM_ENCODER: + st->state = sw_encoder; + break; + + case NGX_HTTP_V3_STREAM_DECODER: + st->state = sw_decoder; + break; + + default: + st->state = sw_unknown; + } + + break; + + case sw_control: + + rc = ngx_http_v3_parse_control(c, &st->u.control, ch); + if (rc != NGX_OK) { + return rc; + } + + break; + + case sw_encoder: + + rc = ngx_http_v3_parse_encoder(c, &st->u.encoder, ch); + if (rc != NGX_OK) { + return rc; + } + + break; + + case sw_decoder: + + rc = ngx_http_v3_parse_decoder(c, &st->u.decoder, ch); + if (rc != NGX_OK) { + return rc; + } + + break; + + case sw_unknown: + break; + } + + return NGX_AGAIN; +} diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h index f039ac28d..c187b7bc2 100644 --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -106,6 +106,17 @@ typedef struct { } ngx_http_v3_parse_control_t; +typedef struct { + ngx_uint_t state; + ngx_http_v3_parse_varlen_int_t vlint; + union { + ngx_http_v3_parse_encoder_t encoder; + ngx_http_v3_parse_decoder_t decoder; + ngx_http_v3_parse_control_t control; + } u; +} ngx_http_v3_parse_uni_t; + + typedef struct { ngx_uint_t state; ngx_uint_t type; @@ -128,10 +139,8 @@ ngx_int_t ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, u_char ch); ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, u_char ch); - -ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch); -ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch); -ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch); +ngx_int_t ngx_http_v3_parse_uni(ngx_connection_t *c, + ngx_http_v3_parse_uni_t *st, u_char ch); #endif /* _NGX_HTTP_V3_PARSE_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index a20008c19..292e6653b 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -10,13 +10,8 @@ #include -typedef ngx_int_t (*ngx_http_v3_handler_pt)(ngx_connection_t *c, void *data, - u_char ch); - - typedef struct { - ngx_http_v3_handler_pt handler; - void *data; + ngx_http_v3_parse_uni_t parse; ngx_int_t index; } ngx_http_v3_uni_stream_t; @@ -32,7 +27,6 @@ typedef struct { static void ngx_http_v3_keepalive_handler(ngx_event_t *ev); static void ngx_http_v3_cleanup_session(void *data); static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); -static void ngx_http_v3_read_uni_stream_type(ngx_event_t *rev); static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); static void ngx_http_v3_push_cleanup(void *data); @@ -131,10 +125,10 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) c->data = us; - c->read->handler = ngx_http_v3_read_uni_stream_type; + c->read->handler = ngx_http_v3_uni_read_handler; c->write->handler = ngx_http_v3_dummy_write_handler; - ngx_http_v3_read_uni_stream_type(c->read); + ngx_http_v3_uni_read_handler(c->read); } @@ -164,118 +158,59 @@ ngx_http_v3_close_uni_stream(ngx_connection_t *c) } -static void -ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) +ngx_int_t +ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type) { - u_char ch; - ssize_t n; - ngx_int_t index, rc; - ngx_connection_t *c; + ngx_int_t index; ngx_http_v3_session_t *h3c; ngx_http_v3_uni_stream_t *us; - c = rev->data; - us = c->data; - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read stream type"); - - while (rev->ready) { - - n = c->recv(c, &ch, 1); - - if (n == NGX_AGAIN) { - break; - } - - if (n == 0) { - rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; - goto failed; - } - - if (n != 1) { - rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; - goto failed; - } - - switch (ch) { - - case NGX_HTTP_V3_STREAM_ENCODER: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 encoder stream"); - - index = NGX_HTTP_V3_STREAM_CLIENT_ENCODER; - us->handler = ngx_http_v3_parse_encoder; - n = sizeof(ngx_http_v3_parse_encoder_t); - - break; - - case NGX_HTTP_V3_STREAM_DECODER: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 decoder stream"); + switch (type) { - index = NGX_HTTP_V3_STREAM_CLIENT_DECODER; - us->handler = ngx_http_v3_parse_decoder; - n = sizeof(ngx_http_v3_parse_decoder_t); + case NGX_HTTP_V3_STREAM_ENCODER: - break; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 encoder stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_ENCODER; + break; - case NGX_HTTP_V3_STREAM_CONTROL: + case NGX_HTTP_V3_STREAM_DECODER: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 control stream"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 decoder stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_DECODER; + break; - index = NGX_HTTP_V3_STREAM_CLIENT_CONTROL; - us->handler = ngx_http_v3_parse_control; - n = sizeof(ngx_http_v3_parse_control_t); + case NGX_HTTP_V3_STREAM_CONTROL: - break; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 control stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_CONTROL; - default: + break; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 stream 0x%02xi", (ngx_int_t) ch); - index = -1; - n = 0; - } + default: - if (index >= 0) { - if (h3c->known_streams[index]) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); - rc = NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; - goto failed; - } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 stream 0x%02xL", type); + index = -1; + } - us->index = index; - h3c->known_streams[index] = c; - } + if (index >= 0) { + h3c = ngx_http_v3_get_session(c); - if (n) { - us->data = ngx_pcalloc(c->pool, n); - if (us->data == NULL) { - rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; - goto failed; - } + if (h3c->known_streams[index]) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); + return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; } - rev->handler = ngx_http_v3_uni_read_handler; - ngx_http_v3_uni_read_handler(rev); - return; - } + h3c->known_streams[index] = c; - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; - goto failed; + us = c->data; + us->index = index; } - return; - -failed: - - ngx_http_v3_finalize_connection(c, rc, "could not read stream type"); - ngx_http_v3_close_uni_stream(c); + return NGX_OK; } @@ -317,13 +252,9 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) break; } - if (us->handler == NULL) { - continue; - } - for (i = 0; i < n; i++) { - rc = us->handler(c, us->data, buf[i]); + rc = ngx_http_v3_parse_uni(c, &us->parse, buf[i]); if (rc == NGX_DONE) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, -- cgit From de75c7e3e21822a909391a4b3ccaac7914c8641d Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 5 May 2021 15:09:23 +0300 Subject: HTTP/3: separate header files for existing source files. --- src/http/v3/ngx_http_v3.h | 80 +++------------------------------------ src/http/v3/ngx_http_v3_encode.h | 34 +++++++++++++++++ src/http/v3/ngx_http_v3_streams.h | 43 +++++++++++++++++++++ src/http/v3/ngx_http_v3_tables.h | 52 +++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 74 deletions(-) create mode 100644 src/http/v3/ngx_http_v3_encode.h create mode 100644 src/http/v3/ngx_http_v3_streams.h create mode 100644 src/http/v3/ngx_http_v3_tables.h (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 736d448b5..4c25a806a 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -12,7 +12,11 @@ #include #include #include + #include +#include +#include +#include #define NGX_HTTP_V3_ALPN_ADVERTISE "\x02h3" @@ -112,21 +116,6 @@ struct ngx_http_v3_parse_s { }; -typedef struct { - ngx_str_t name; - ngx_str_t value; -} ngx_http_v3_header_t; - - -typedef struct { - ngx_http_v3_header_t **elts; - ngx_uint_t nelts; - ngx_uint_t base; - size_t size; - size_t capacity; -} ngx_http_v3_dynamic_table_t; - - struct ngx_http_v3_session_s { ngx_http_v3_dynamic_table_t table; @@ -148,68 +137,11 @@ struct ngx_http_v3_session_s { void ngx_http_v3_init(ngx_connection_t *c); +ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); + ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r); ngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r); -uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); -uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, - ngx_uint_t prefix); - -uintptr_t ngx_http_v3_encode_header_block_prefix(u_char *p, - ngx_uint_t insert_count, ngx_uint_t sign, ngx_uint_t delta_base); -uintptr_t ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic, - ngx_uint_t index); -uintptr_t ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, - ngx_uint_t index, u_char *data, size_t len); -uintptr_t ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, - ngx_str_t *value); -uintptr_t ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index); -uintptr_t ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, - u_char *data, size_t len); - -ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); -void ngx_http_v3_init_uni_stream(ngx_connection_t *c); -ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type); -ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, - uint64_t push_id); -ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); -ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, - ngx_uint_t index, ngx_str_t *value); -ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, - ngx_str_t *value); -ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); -ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index); -ngx_int_t ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); -ngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, - ngx_str_t *name, ngx_str_t *value); -ngx_int_t ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, - ngx_str_t *name, ngx_str_t *value); -ngx_int_t ngx_http_v3_decode_insert_count(ngx_connection_t *c, - ngx_uint_t *insert_count); -ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, - ngx_uint_t insert_count); -ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, - uint64_t value); -ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, - uint64_t max_push_id); -ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); - -ngx_int_t ngx_http_v3_send_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, - ngx_uint_t index, ngx_str_t *value); -ngx_int_t ngx_http_v3_send_insert(ngx_connection_t *c, ngx_str_t *name, - ngx_str_t *value); -ngx_int_t ngx_http_v3_send_set_capacity(ngx_connection_t *c, - ngx_uint_t capacity); -ngx_int_t ngx_http_v3_send_duplicate(ngx_connection_t *c, ngx_uint_t index); -ngx_int_t ngx_http_v3_send_ack_header(ngx_connection_t *c, - ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, - ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, - ngx_uint_t inc); - extern ngx_module_t ngx_http_v3_module; diff --git a/src/http/v3/ngx_http_v3_encode.h b/src/http/v3/ngx_http_v3_encode.h new file mode 100644 index 000000000..583c5675b --- /dev/null +++ b/src/http/v3/ngx_http_v3_encode.h @@ -0,0 +1,34 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_ENCODE_H_INCLUDED_ +#define _NGX_HTTP_V3_ENCODE_H_INCLUDED_ + + +#include +#include +#include + + +uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); +uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, + ngx_uint_t prefix); + +uintptr_t ngx_http_v3_encode_header_block_prefix(u_char *p, + ngx_uint_t insert_count, ngx_uint_t sign, ngx_uint_t delta_base); +uintptr_t ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic, + ngx_uint_t index); +uintptr_t ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, + ngx_uint_t index, u_char *data, size_t len); +uintptr_t ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, + ngx_str_t *value); +uintptr_t ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index); +uintptr_t ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, + u_char *data, size_t len); + + +#endif /* _NGX_HTTP_V3_ENCODE_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_streams.h b/src/http/v3/ngx_http_v3_streams.h new file mode 100644 index 000000000..c48e6425c --- /dev/null +++ b/src/http/v3/ngx_http_v3_streams.h @@ -0,0 +1,43 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_STREAMS_H_INCLUDED_ +#define _NGX_HTTP_V3_STREAMS_H_INCLUDED_ + + +#include +#include +#include + + +void ngx_http_v3_init_uni_stream(ngx_connection_t *c); +ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type); + +ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, + uint64_t push_id); +ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, + uint64_t max_push_id); +ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); +ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); + +ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); +ngx_int_t ngx_http_v3_send_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value); +ngx_int_t ngx_http_v3_send_insert(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value); +ngx_int_t ngx_http_v3_send_set_capacity(ngx_connection_t *c, + ngx_uint_t capacity); +ngx_int_t ngx_http_v3_send_duplicate(ngx_connection_t *c, ngx_uint_t index); +ngx_int_t ngx_http_v3_send_ack_header(ngx_connection_t *c, + ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, + ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, + ngx_uint_t inc); + + +#endif /* _NGX_HTTP_V3_STREAMS_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_tables.h b/src/http/v3/ngx_http_v3_tables.h new file mode 100644 index 000000000..6cf247541 --- /dev/null +++ b/src/http/v3/ngx_http_v3_tables.h @@ -0,0 +1,52 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_TABLES_H_INCLUDED_ +#define _NGX_HTTP_V3_TABLES_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_str_t name; + ngx_str_t value; +} ngx_http_v3_header_t; + + +typedef struct { + ngx_http_v3_header_t **elts; + ngx_uint_t nelts; + ngx_uint_t base; + size_t size; + size_t capacity; +} ngx_http_v3_dynamic_table_t; + + +ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value); +ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value); +ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); +ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index); +ngx_int_t ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); +ngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_v3_decode_insert_count(ngx_connection_t *c, + ngx_uint_t *insert_count); +ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, + ngx_uint_t insert_count); +ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, + uint64_t value); + + +#endif /* _NGX_HTTP_V3_TABLES_H_INCLUDED_ */ -- cgit From 9e05c357cc20c6673480e45e554a5b1c69b0e413 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 5 May 2021 15:15:48 +0300 Subject: HTTP/3: moved session initialization to a separate file. Previously it was in ngx_http_v3_streams.c, but it's unrelated to streams. --- src/http/v3/ngx_http_v3.c | 85 +++++++++++++++++++++++++++++++++++++++ src/http/v3/ngx_http_v3_streams.c | 76 +--------------------------------- src/http/v3/ngx_http_v3_streams.h | 1 + 3 files changed, 87 insertions(+), 75 deletions(-) create mode 100644 src/http/v3/ngx_http_v3.c (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c new file mode 100644 index 000000000..461388bae --- /dev/null +++ b/src/http/v3/ngx_http_v3.c @@ -0,0 +1,85 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static void ngx_http_v3_keepalive_handler(ngx_event_t *ev); +static void ngx_http_v3_cleanup_session(void *data); + + +ngx_int_t +ngx_http_v3_init_session(ngx_connection_t *c) +{ + ngx_connection_t *pc; + ngx_pool_cleanup_t *cln; + ngx_http_connection_t *hc; + ngx_http_v3_session_t *h3c; + + pc = c->quic->parent; + hc = pc->data; + + if (hc->v3_session) { + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init session"); + + h3c = ngx_pcalloc(pc->pool, sizeof(ngx_http_v3_session_t)); + if (h3c == NULL) { + return NGX_ERROR; + } + + h3c->max_push_id = (uint64_t) -1; + + ngx_queue_init(&h3c->blocked); + ngx_queue_init(&h3c->pushing); + + h3c->keepalive.log = pc->log; + h3c->keepalive.data = pc; + h3c->keepalive.handler = ngx_http_v3_keepalive_handler; + h3c->keepalive.cancelable = 1; + + cln = ngx_pool_cleanup_add(pc->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_v3_cleanup_session; + cln->data = h3c; + + hc->v3_session = h3c; + + return ngx_http_v3_send_settings(c); +} + + +static void +ngx_http_v3_keepalive_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 keepalive handler"); + + ngx_quic_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "keepalive timeout"); +} + + +static void +ngx_http_v3_cleanup_session(void *data) +{ + ngx_http_v3_session_t *h3c = data; + + if (h3c->keepalive.timer_set) { + ngx_del_timer(&h3c->keepalive); + } +} diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 292e6653b..0e55dbe0d 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -24,86 +24,12 @@ typedef struct { } ngx_http_v3_push_t; -static void ngx_http_v3_keepalive_handler(ngx_event_t *ev); -static void ngx_http_v3_cleanup_session(void *data); static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); static void ngx_http_v3_push_cleanup(void *data); static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type); -static ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); - - -ngx_int_t -ngx_http_v3_init_session(ngx_connection_t *c) -{ - ngx_connection_t *pc; - ngx_pool_cleanup_t *cln; - ngx_http_connection_t *hc; - ngx_http_v3_session_t *h3c; - - pc = c->quic->parent; - hc = pc->data; - - if (hc->v3_session) { - return NGX_OK; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init session"); - - h3c = ngx_pcalloc(pc->pool, sizeof(ngx_http_v3_session_t)); - if (h3c == NULL) { - return NGX_ERROR; - } - - h3c->max_push_id = (uint64_t) -1; - - ngx_queue_init(&h3c->blocked); - ngx_queue_init(&h3c->pushing); - - h3c->keepalive.log = pc->log; - h3c->keepalive.data = pc; - h3c->keepalive.handler = ngx_http_v3_keepalive_handler; - h3c->keepalive.cancelable = 1; - - cln = ngx_pool_cleanup_add(pc->pool, 0); - if (cln == NULL) { - return NGX_ERROR; - } - - cln->handler = ngx_http_v3_cleanup_session; - cln->data = h3c; - - hc->v3_session = h3c; - - return ngx_http_v3_send_settings(c); -} - - -static void -ngx_http_v3_keepalive_handler(ngx_event_t *ev) -{ - ngx_connection_t *c; - - c = ev->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 keepalive handler"); - - ngx_quic_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, - "keepalive timeout"); -} - - -static void -ngx_http_v3_cleanup_session(void *data) -{ - ngx_http_v3_session_t *h3c = data; - - if (h3c->keepalive.timer_set) { - ngx_del_timer(&h3c->keepalive); - } -} void @@ -445,7 +371,7 @@ failed: } -static ngx_int_t +ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c) { u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; diff --git a/src/http/v3/ngx_http_v3_streams.h b/src/http/v3/ngx_http_v3_streams.h index c48e6425c..75325f5d4 100644 --- a/src/http/v3/ngx_http_v3_streams.h +++ b/src/http/v3/ngx_http_v3_streams.h @@ -24,6 +24,7 @@ ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); ngx_int_t ngx_http_v3_send_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value); -- cgit From 541feb5bd913294c0ef068b03293b67d0da7ef21 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 28 Apr 2021 11:30:27 +0300 Subject: HTTP/3: clean up table from session cleanup handler. Previously table had a separate cleanup handler. --- src/http/v3/ngx_http_v3.c | 2 ++ src/http/v3/ngx_http_v3_tables.c | 29 ++++++++++------------------- src/http/v3/ngx_http_v3_tables.h | 1 + 3 files changed, 13 insertions(+), 19 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index 461388bae..a1638c504 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -79,6 +79,8 @@ ngx_http_v3_cleanup_session(void *data) { ngx_http_v3_session_t *h3c = data; + ngx_http_v3_cleanup_table(h3c); + if (h3c->keepalive.timer_set) { ngx_del_timer(&h3c->keepalive); } diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index f83236c01..e07332c96 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -14,7 +14,6 @@ static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); -static void ngx_http_v3_cleanup_table(void *data); static void ngx_http_v3_unblock(void *data); static ngx_int_t ngx_http_v3_new_header(ngx_connection_t *c); @@ -240,8 +239,6 @@ ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) { ngx_uint_t max, prev_max; - ngx_connection_t *pc; - ngx_pool_cleanup_t *cln; ngx_http_v3_header_t **elts; ngx_http_v3_session_t *h3c; ngx_http_v3_srv_conf_t *h3scf; @@ -276,18 +273,7 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) return NGX_ERROR; } - if (dt->elts == NULL) { - pc = c->quic->parent; - - cln = ngx_pool_cleanup_add(pc->pool, 0); - if (cln == NULL) { - return NGX_ERROR; - } - - cln->handler = ngx_http_v3_cleanup_table; - cln->data = dt; - - } else { + if (dt->elts) { ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *)); ngx_free(dt->elts); } @@ -301,12 +287,17 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) } -static void -ngx_http_v3_cleanup_table(void *data) +void +ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c) { - ngx_http_v3_dynamic_table_t *dt = data; + ngx_uint_t n; + ngx_http_v3_dynamic_table_t *dt; + + dt = &h3c->table; - ngx_uint_t n; + if (dt->elts == NULL) { + return; + } for (n = 0; n < dt->nelts; n++) { ngx_free(dt->elts[n]); diff --git a/src/http/v3/ngx_http_v3_tables.h b/src/http/v3/ngx_http_v3_tables.h index 6cf247541..36589f171 100644 --- a/src/http/v3/ngx_http_v3_tables.h +++ b/src/http/v3/ngx_http_v3_tables.h @@ -29,6 +29,7 @@ typedef struct { } ngx_http_v3_dynamic_table_t; +void ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c); ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value); ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, -- cgit From 66f736391ea5d0bc459c3f389a8cbfbb36fa9c86 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 18 May 2021 18:17:25 +0300 Subject: HTTP/3: fixed server push after 9ec3e71f8a61. When using server push, a segfault occured because ngx_http_v3_create_push_request() accessed ngx_http_v3_session_t object the old way. Prior to 9ec3e71f8a61, HTTP/3 session was stored directly in c->data. Now it's referenced by the v3_session field of ngx_http_connection_t. --- src/http/v3/ngx_http_v3_filter_module.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index ce0b478c2..93f3318cb 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -837,8 +837,7 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, ngx_connection_t *c, *pc; ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; - ngx_http_connection_t *hc; - ngx_http_v3_session_t *h3c; + ngx_http_connection_t *hc, *phc; ngx_http_core_srv_conf_t *cscf; pc = pr->connection; @@ -858,8 +857,8 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, goto failed; } - h3c = ngx_http_v3_get_session(c); - ngx_memcpy(hc, h3c, sizeof(ngx_http_connection_t)); + phc = ngx_http_quic_get_connection(pc); + ngx_memcpy(hc, phc, sizeof(ngx_http_connection_t)); c->data = hc; ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); -- cgit From 03fcff287db0d6b620f837de95116ad3a3b7e1e9 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 27 May 2021 13:29:00 +0300 Subject: HTTP/3: fixed Insert With Name Reference index processing. Based on a patch by Zhiyong Sun. --- src/http/v3/ngx_http_v3_tables.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index e07332c96..8f4e28edd 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -159,12 +159,23 @@ ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value) { - ngx_str_t name; + ngx_str_t name; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; if (dynamic) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 ref insert dynamic[%ui] \"%V\"", index, value); + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->base + dt->nelts <= index) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + index = dt->base + dt->nelts - 1 - index; + if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) { return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } -- cgit From e8a76252699339b2fa615ec0e3f01ae1afbb36d8 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 31 May 2021 11:54:47 +0300 Subject: HTTP/3: removed $http3 that served its purpose. To specify final protocol version by hand: add_header Alt-Svc h3=":443"; --- src/http/v3/ngx_http_v3_module.c | 51 +--------------------------------------- 1 file changed, 1 insertion(+), 50 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 18da90aab..3b651f044 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -10,9 +10,6 @@ #include -static ngx_int_t ngx_http_variable_http3(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data); -static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); 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); @@ -64,7 +61,7 @@ static ngx_command_t ngx_http_v3_commands[] = { static ngx_http_module_t ngx_http_v3_module_ctx = { - ngx_http_v3_add_variables, /* preconfiguration */ + NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ @@ -94,52 +91,6 @@ ngx_module_t ngx_http_v3_module = { }; -static ngx_http_variable_t ngx_http_v3_vars[] = { - - { ngx_string("http3"), NULL, ngx_http_variable_http3, 0, 0, 0 }, - - ngx_http_null_variable -}; - - -static ngx_int_t -ngx_http_variable_http3(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data) -{ - v->valid = 1; - v->no_cacheable = 1; - v->not_found = 0; - - v->data = ngx_pnalloc(r->pool, sizeof("h3-xx") - 1); - if (v->data == NULL) { - return NGX_ERROR; - } - - v->len = ngx_sprintf(v->data, "h3-%d", NGX_QUIC_DRAFT_VERSION) - v->data; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_v3_add_variables(ngx_conf_t *cf) -{ - ngx_http_variable_t *var, *v; - - for (v = ngx_http_v3_vars; v->name.len; v++) { - var = ngx_http_add_variable(cf, &v->name, v->flags); - if (var == NULL) { - return NGX_ERROR; - } - - var->get_handler = v->get_handler; - var->data = v->data; - } - - return NGX_OK; -} - - static void * ngx_http_v3_create_srv_conf(ngx_conf_t *cf) { -- cgit From 1f85c660cb3e94b60a0f2ec8495f72a43cddb378 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 1 Jun 2021 11:41:38 +0300 Subject: HTTP/3: fixed parsing encoder insertions with empty header value. When starting processing a new encoder instruction, the header state is not memzero'ed because generally it's burdensome. If the header value is empty, this resulted in inserting a stale value left from the previous instruction. Based on a patch by Zhiyong Sun. --- src/http/v3/ngx_http_v3_parse.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index fcb39b209..40366e665 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -1366,6 +1366,7 @@ ngx_http_v3_parse_header_inr(ngx_connection_t *c, st->literal.length = st->pint.value; if (st->literal.length == 0) { + st->value.len = 0; goto done; } @@ -1470,6 +1471,7 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, st->literal.length = st->pint.value; if (st->literal.length == 0) { + st->value.len = 0; goto done; } -- cgit From dcdf62549f25f030b6cf518b9adb3d2a84313ea5 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 1 Jun 2021 12:02:08 +0300 Subject: HTTP/3: undo 5a92523e50d3 after parser refactoring (e1eb7f4ca9f1). This is no longer needed after HTTP/3 request processing has moved into its own function ngx_http_v3_process_header(). --- src/http/v3/ngx_http_v3_parse.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 40366e665..21c73a24d 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -783,7 +783,6 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, st->literal.length = st->pint.value; if (st->literal.length == 0) { - st->value.data = (u_char *) ""; goto done; } -- cgit From 9cf6426f6a517919863d0618e6c514f3f180cb8f Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 11 Jun 2021 10:56:51 +0300 Subject: HTTP/3: reordered H3_MISSING_SETTINGS and H3_FRAME_UNEXPECTED. The quic-http-34 is ambiguous as to what error should be generated for the first frame in control stream: Each side MUST initiate a single control stream at the beginning of the connection and send its SETTINGS frame as the first frame on this stream. If the first frame of the control stream is any other frame type, this MUST be treated as a connection error of type H3_MISSING_SETTINGS. If a DATA frame is received on a control stream, the recipient MUST respond with a connection error of type H3_FRAME_UNEXPECTED. If a HEADERS frame is received on a control stream, the recipient MUST respond with a connection error of type H3_FRAME_UNEXPECTED. Previously, H3_FRAME_UNEXPECTED had priority, but now H3_MISSING_SETTINGS has. The arguments in the spec sound more compelling for H3_MISSING_SETTINGS. --- src/http/v3/ngx_http_v3_parse.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 21c73a24d..bd5289631 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -1031,6 +1031,12 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse frame type:%ui", st->type); + if (st->state == sw_first_type + && st->type != NGX_HTTP_V3_FRAME_SETTINGS) + { + return NGX_HTTP_V3_ERR_MISSING_SETTINGS; + } + if (ngx_http_v3_is_v2_frame(st->type) || st->type == NGX_HTTP_V3_FRAME_DATA || st->type == NGX_HTTP_V3_FRAME_HEADERS) @@ -1038,12 +1044,6 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; } - if (st->state == sw_first_type - && st->type != NGX_HTTP_V3_FRAME_SETTINGS) - { - return NGX_HTTP_V3_ERR_MISSING_SETTINGS; - } - st->state = sw_length; break; -- cgit From 80a5227617924f666a39f8debf556ab525f420c9 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 11 Jun 2021 12:11:08 +0300 Subject: HTTP/3: generate more H3_FRAME_UNEXPECTED. As per quic-http-34, these are the cases when this error should be generated: If an endpoint receives a second SETTINGS frame on the control stream, the endpoint MUST respond with a connection error of type H3_FRAME_UNEXPECTED SETTINGS frames MUST NOT be sent on any stream other than the control stream. If an endpoint receives a SETTINGS frame on a different stream, the endpoint MUST respond with a connection error of type H3_FRAME_UNEXPECTED. A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the receipt of a PUSH_PROMISE frame as a connection error of type H3_FRAME_UNEXPECTED; see Section 8. The MAX_PUSH_ID frame is always sent on the control stream. Receipt of a MAX_PUSH_ID frame on any other stream MUST be treated as a connection error of type H3_FRAME_UNEXPECTED. Receipt of an invalid sequence of frames MUST be treated as a connection error of type H3_FRAME_UNEXPECTED; see Section 8. In particular, a DATA frame before any HEADERS frame, or a HEADERS or DATA frame after the trailing HEADERS frame, is considered invalid. A CANCEL_PUSH frame is sent on the control stream. Receiving a CANCEL_PUSH frame on a stream other than the control stream MUST be treated as a connection error of type H3_FRAME_UNEXPECTED. The GOAWAY frame is always sent on the control stream. --- src/http/v3/ngx_http_v3_parse.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index bd5289631..7bd9e6327 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -224,7 +224,14 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, st->type = st->vlint.value; - if (ngx_http_v3_is_v2_frame(st->type)) { + if (ngx_http_v3_is_v2_frame(st->type) + || st->type == NGX_HTTP_V3_FRAME_DATA + || st->type == NGX_HTTP_V3_FRAME_GOAWAY + || st->type == NGX_HTTP_V3_FRAME_SETTINGS + || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID + || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH + || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) + { return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; } @@ -1037,9 +1044,16 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, return NGX_HTTP_V3_ERR_MISSING_SETTINGS; } + if (st->state != sw_first_type + && st->type == NGX_HTTP_V3_FRAME_SETTINGS) + { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } + if (ngx_http_v3_is_v2_frame(st->type) || st->type == NGX_HTTP_V3_FRAME_DATA - || st->type == NGX_HTTP_V3_FRAME_HEADERS) + || st->type == NGX_HTTP_V3_FRAME_HEADERS + || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) { return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; } @@ -1633,7 +1647,13 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, goto done; } - if (ngx_http_v3_is_v2_frame(st->type)) { + if (ngx_http_v3_is_v2_frame(st->type) + || st->type == NGX_HTTP_V3_FRAME_GOAWAY + || st->type == NGX_HTTP_V3_FRAME_SETTINGS + || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID + || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH + || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) + { return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; } -- cgit From 96e1db1c34a0c206463b86fb3400545f0147f476 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 11 Jun 2021 13:24:24 +0300 Subject: HTTP/3: client GOAWAY support. --- src/http/v3/ngx_http_v3.c | 1 + src/http/v3/ngx_http_v3.h | 1 + src/http/v3/ngx_http_v3_filter_module.c | 6 ++++++ src/http/v3/ngx_http_v3_parse.c | 25 +++++++++++++++++++++++++ src/http/v3/ngx_http_v3_streams.c | 15 +++++++++++++++ src/http/v3/ngx_http_v3_streams.h | 1 + 6 files changed, 49 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index a1638c504..2c838f4b5 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -37,6 +37,7 @@ ngx_http_v3_init_session(ngx_connection_t *c) } h3c->max_push_id = (uint64_t) -1; + h3c->goaway_push_id = (uint64_t) -1; ngx_queue_init(&h3c->blocked); ngx_queue_init(&h3c->pushing); diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 4c25a806a..e693af7d8 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -129,6 +129,7 @@ struct ngx_http_v3_session_s { ngx_uint_t npushing; uint64_t next_push_id; uint64_t max_push_id; + uint64_t goaway_push_id; ngx_uint_t goaway; /* unsigned goaway:1; */ diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 93f3318cb..db40e3737 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -805,6 +805,12 @@ ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, return NGX_ABORT; } + if (h3c->goaway_push_id != (uint64_t) -1) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 abort pushes due to goaway"); + return NGX_ABORT; + } + if (h3c->npushing >= h3scf->max_concurrent_pushes) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 abort pushes due to max_concurrent_pushes"); diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 7bd9e6327..5951dff10 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -1012,6 +1012,7 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, sw_cancel_push, sw_settings, sw_max_push_id, + sw_goaway, sw_skip }; @@ -1091,6 +1092,10 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, st->state = sw_max_push_id; break; + case NGX_HTTP_V3_FRAME_GOAWAY: + st->state = sw_goaway; + break; + default: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse skip unknown frame"); @@ -1157,6 +1162,26 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, st->state = sw_type; break; + case sw_goaway: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + + if (--st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } + + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_goaway(c, st->vlint.value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_type; + break; + case sw_skip: if (--st->length == 0) { diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 0e55dbe0d..8b2047f43 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -703,6 +703,21 @@ ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) } +ngx_int_t +ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id) +{ + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 GOAWAY:%uL", push_id); + + h3c->goaway_push_id = push_id; + + return NGX_OK; +} + + ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) { diff --git a/src/http/v3/ngx_http_v3_streams.h b/src/http/v3/ngx_http_v3_streams.h index 75325f5d4..a2126a7f8 100644 --- a/src/http/v3/ngx_http_v3_streams.h +++ b/src/http/v3/ngx_http_v3_streams.h @@ -21,6 +21,7 @@ ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id); ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id); +ngx_int_t ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id); ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); -- cgit From d54d551e2a880c7a7691a83d212eb707eb0f82ba Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 30 Jun 2021 13:47:38 +0300 Subject: QUIC: consider max_ack_delay=16384 invalid. As per RFC 9000: Values of 2^14 or greater are invalid. --- src/http/modules/ngx_http_quic_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 2354dfd8b..d933dd1f9 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -394,7 +394,7 @@ ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, void *data) { ngx_msec_t *sp = data; - if (*sp > 16384) { + if (*sp >= 16384) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"quic_max_ack_delay\" must be less than 16384"); -- cgit From a85084fea109c019d1ad7466ed063afa7961acba Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 1 Jul 2021 15:37:53 +0300 Subject: HTTP/3: quic-qpack term updates. Renamed header -> field per quic-qpack naming convention, in particular: - Header Field -> Field Line - Header Block -> (Encoded) Field Section - Without Name Reference -> With Literal Name - Header Acknowledgement -> Section Acknowledgment --- src/http/v3/ngx_http_v3_encode.c | 22 ++-- src/http/v3/ngx_http_v3_encode.h | 12 +- src/http/v3/ngx_http_v3_filter_module.c | 134 ++++++++++----------- src/http/v3/ngx_http_v3_parse.c | 200 ++++++++++++++++---------------- src/http/v3/ngx_http_v3_parse.h | 14 +-- src/http/v3/ngx_http_v3_request.c | 4 +- src/http/v3/ngx_http_v3_streams.c | 4 +- src/http/v3/ngx_http_v3_streams.h | 2 +- src/http/v3/ngx_http_v3_tables.c | 66 +++++------ src/http/v3/ngx_http_v3_tables.h | 6 +- 10 files changed, 232 insertions(+), 232 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_encode.c b/src/http/v3/ngx_http_v3_encode.c index b7bc3dd7c..2740ccffa 100644 --- a/src/http/v3/ngx_http_v3_encode.c +++ b/src/http/v3/ngx_http_v3_encode.c @@ -100,7 +100,7 @@ ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix) uintptr_t -ngx_http_v3_encode_header_block_prefix(u_char *p, ngx_uint_t insert_count, +ngx_http_v3_encode_field_section_prefix(u_char *p, ngx_uint_t insert_count, ngx_uint_t sign, ngx_uint_t delta_base) { if (p == NULL) { @@ -119,9 +119,9 @@ ngx_http_v3_encode_header_block_prefix(u_char *p, ngx_uint_t insert_count, uintptr_t -ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index) +ngx_http_v3_encode_field_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index) { - /* Indexed Header Field */ + /* Indexed Field Line */ if (p == NULL) { return ngx_http_v3_encode_prefix_int(NULL, index, 6); @@ -134,10 +134,10 @@ ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index) uintptr_t -ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, +ngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, u_char *data, size_t len) { - /* Literal Header Field With Name Reference */ + /* Literal Field Line With Name Reference */ if (p == NULL) { return ngx_http_v3_encode_prefix_int(NULL, index, 4) @@ -160,9 +160,9 @@ ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, uintptr_t -ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, ngx_str_t *value) +ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, ngx_str_t *value) { - /* Literal Header Field Without Name Reference */ + /* Literal Field Line With Literal Name */ if (p == NULL) { return ngx_http_v3_encode_prefix_int(NULL, name->len, 3) @@ -187,9 +187,9 @@ ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, ngx_str_t *value) uintptr_t -ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index) +ngx_http_v3_encode_field_pbi(u_char *p, ngx_uint_t index) { - /* Indexed Header Field With Post-Base Index */ + /* Indexed Field Line With Post-Base Index */ if (p == NULL) { return ngx_http_v3_encode_prefix_int(NULL, index, 4); @@ -202,10 +202,10 @@ ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index) uintptr_t -ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, u_char *data, +ngx_http_v3_encode_field_lpbi(u_char *p, ngx_uint_t index, u_char *data, size_t len) { - /* Literal Header Field With Post-Base Name Reference */ + /* Literal Field Line With Post-Base Name Reference */ if (p == NULL) { return ngx_http_v3_encode_prefix_int(NULL, index, 3) diff --git a/src/http/v3/ngx_http_v3_encode.h b/src/http/v3/ngx_http_v3_encode.h index 583c5675b..fca376da5 100644 --- a/src/http/v3/ngx_http_v3_encode.h +++ b/src/http/v3/ngx_http_v3_encode.h @@ -18,16 +18,16 @@ uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix); -uintptr_t ngx_http_v3_encode_header_block_prefix(u_char *p, +uintptr_t ngx_http_v3_encode_field_section_prefix(u_char *p, ngx_uint_t insert_count, ngx_uint_t sign, ngx_uint_t delta_base); -uintptr_t ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic, +uintptr_t ngx_http_v3_encode_field_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index); -uintptr_t ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, +uintptr_t ngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, u_char *data, size_t len); -uintptr_t ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, +uintptr_t ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, ngx_str_t *value); -uintptr_t ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index); -uintptr_t ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, +uintptr_t ngx_http_v3_encode_field_pbi(u_char *p, ngx_uint_t index); +uintptr_t ngx_http_v3_encode_field_lpbi(u_char *p, ngx_uint_t index, u_char *data, size_t len); diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index db40e3737..52db65736 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -159,16 +159,16 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) } } - len = ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); + len = ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0); if (r->headers_out.status == NGX_HTTP_OK) { - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_STATUS_200); + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_STATUS_200); } else { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_STATUS_200, - NULL, 3); + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_STATUS_200, + NULL, 3); } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -184,14 +184,14 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) n = sizeof("nginx") - 1; } - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_SERVER, - NULL, n); + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_SERVER, + NULL, n); } if (r->headers_out.date == NULL) { - len += ngx_http_v3_encode_header_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE, - NULL, ngx_cached_http_time.len); + len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE, + NULL, ngx_cached_http_time.len); } if (r->headers_out.content_type.len) { @@ -203,19 +203,19 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) n += sizeof("; charset=") - 1 + r->headers_out.charset.len; } - len += ngx_http_v3_encode_header_lri(NULL, 0, + len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, NULL, n); } if (r->headers_out.content_length == NULL) { if (r->headers_out.content_length_n > 0) { - len += ngx_http_v3_encode_header_lri(NULL, 0, + len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, NULL, NGX_OFF_T_LEN); } else if (r->headers_out.content_length_n == 0) { - len += ngx_http_v3_encode_header_ri(NULL, 0, + len += ngx_http_v3_encode_field_ri(NULL, 0, NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); } } @@ -223,7 +223,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { - len += ngx_http_v3_encode_header_lri(NULL, 0, + len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); } @@ -267,7 +267,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) n += sizeof(":65535") - 1; } - len += ngx_http_v3_encode_header_lri(NULL, 0, + len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_LOCATION, NULL, n); } else { @@ -278,7 +278,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) #if (NGX_HTTP_GZIP) if (r->gzip_vary) { if (clcf->gzip_vary) { - len += ngx_http_v3_encode_header_ri(NULL, 0, + len += ngx_http_v3_encode_field_ri(NULL, 0, NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); } else { @@ -306,8 +306,8 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) continue; } - len += ngx_http_v3_encode_header_l(NULL, &header[i].key, - &header[i].value); + len += ngx_http_v3_encode_field_l(NULL, &header[i].key, + &header[i].value); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); @@ -317,15 +317,15 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) return NGX_ERROR; } - b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, - 0, 0, 0); + b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last, + 0, 0, 0); if (r->headers_out.status == NGX_HTTP_OK) { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, NGX_HTTP_V3_HEADER_STATUS_200); } else { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_STATUS_200, NULL, 3); b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); @@ -345,13 +345,13 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) n = sizeof("nginx") - 1; } - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_SERVER, p, n); } if (r->headers_out.date == NULL) { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_DATE, ngx_cached_http_time.data, ngx_cached_http_time.len); @@ -366,7 +366,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) n += sizeof("; charset=") - 1 + r->headers_out.charset.len; } - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, NULL, n); @@ -394,7 +394,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) p = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); n = p - b->last; - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, NULL, n); @@ -402,7 +402,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) r->headers_out.content_length_n); } else if (r->headers_out.content_length_n == 0) { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); } } @@ -410,7 +410,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); @@ -425,7 +425,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) n += ngx_sprintf(b->last, ":%ui", port) - b->last; } - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_LOCATION, NULL, n); @@ -449,7 +449,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) #if (NGX_HTTP_GZIP) if (r->gzip_vary) { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); } #endif @@ -473,9 +473,9 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) continue; } - b->last = (u_char *) ngx_http_v3_encode_header_l(b->last, - &header[i].key, - &header[i].value); + b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, + &header[i].key, + &header[i].value); } if (r->header_only) { @@ -1065,55 +1065,55 @@ ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, len = ngx_http_v3_encode_varlen_int(NULL, push_id); - len += ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); + len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0); - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_METHOD_GET); + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_METHOD_GET); - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_AUTHORITY, - NULL, r->headers_in.server.len); + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_AUTHORITY, + NULL, r->headers_in.server.len); if (path->len == 1 && path->data[0] == '/') { - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_PATH_ROOT); + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT); } else { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_PATH_ROOT, - NULL, path->len); + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT, + NULL, path->len); } if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTPS); + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTPS); } else if (r->schema.len == 4 && ngx_strncmp(r->schema.data, "http", 4) == 0) { - len += ngx_http_v3_encode_header_ri(NULL, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTP); + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTP); } else { - len += ngx_http_v3_encode_header_lri(NULL, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTP, - NULL, r->schema.len); + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTP, + NULL, r->schema.len); } if (r->headers_in.accept_encoding) { - len += ngx_http_v3_encode_header_lri(NULL, 0, + len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, NULL, r->headers_in.accept_encoding->value.len); } if (r->headers_in.accept_language) { - len += ngx_http_v3_encode_header_lri(NULL, 0, + len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, NULL, r->headers_in.accept_language->value.len); } if (r->headers_in.user_agent) { - len += ngx_http_v3_encode_header_lri(NULL, 0, + len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_USER_AGENT, NULL, r->headers_in.user_agent->value.len); } @@ -1125,59 +1125,59 @@ ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, push_id); - b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, - 0, 0, 0); + b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last, + 0, 0, 0); - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, NGX_HTTP_V3_HEADER_METHOD_GET); - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_AUTHORITY, r->headers_in.server.data, r->headers_in.server.len); if (path->len == 1 && path->data[0] == '/') { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, NGX_HTTP_V3_HEADER_PATH_ROOT); } else { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_PATH_ROOT, path->data, path->len); } if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, NGX_HTTP_V3_HEADER_SCHEME_HTTPS); } else if (r->schema.len == 4 && ngx_strncmp(r->schema.data, "http", 4) == 0) { - b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, NGX_HTTP_V3_HEADER_SCHEME_HTTP); } else { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_SCHEME_HTTP, r->schema.data, r->schema.len); } if (r->headers_in.accept_encoding) { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, r->headers_in.accept_encoding->value.data, r->headers_in.accept_encoding->value.len); } if (r->headers_in.accept_language) { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, r->headers_in.accept_language->value.data, r->headers_in.accept_language->value.len); } if (r->headers_in.user_agent) { - b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_USER_AGENT, r->headers_in.user_agent->value.data, r->headers_in.user_agent->value.len); diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 5951dff10..c5fc24482 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -19,22 +19,22 @@ static ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, static ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch); -static ngx_int_t ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, - ngx_http_v3_parse_header_block_prefix_t *st, u_char ch); -static ngx_int_t ngx_http_v3_parse_header_rep(ngx_connection_t *c, - ngx_http_v3_parse_header_rep_t *st, ngx_uint_t base, u_char ch); +static ngx_int_t ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c, + ngx_http_v3_parse_field_section_prefix_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_field_rep(ngx_connection_t *c, + ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, u_char ch); static ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, u_char ch); -static ngx_int_t ngx_http_v3_parse_header_ri(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); -static ngx_int_t ngx_http_v3_parse_header_lri(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); -static ngx_int_t ngx_http_v3_parse_header_l(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); -static ngx_int_t ngx_http_v3_parse_header_pbi(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); -static ngx_int_t ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_field_ri(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_field_lri(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_field_l(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_field_pbi(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_field_lpbi(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch); static ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, u_char ch); @@ -43,10 +43,10 @@ static ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, static ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, u_char ch); -static ngx_int_t ngx_http_v3_parse_header_inr(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); -static ngx_int_t ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_field_inr(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch); +static ngx_int_t ngx_http_v3_parse_field_iln(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch); static ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, u_char ch); @@ -201,7 +201,7 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, sw_skip, sw_prefix, sw_verify, - sw_header_rep, + sw_field_rep, sw_done }; @@ -277,7 +277,7 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, return NGX_HTTP_V3_ERR_FRAME_ERROR; } - rc = ngx_http_v3_parse_header_block_prefix(c, &st->prefix, ch); + rc = ngx_http_v3_parse_field_section_prefix(c, &st->prefix, ch); if (rc != NGX_DONE) { return rc; } @@ -292,14 +292,14 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, return rc; } - st->state = sw_header_rep; + st->state = sw_field_rep; /* fall through */ - case sw_header_rep: + case sw_field_rep: - rc = ngx_http_v3_parse_header_rep(c, &st->header_rep, st->prefix.base, - ch); + rc = ngx_http_v3_parse_field_rep(c, &st->field_rep, st->prefix.base, + ch); if (--st->length == 0 && rc == NGX_AGAIN) { return NGX_HTTP_V3_ERR_FRAME_ERROR; @@ -323,7 +323,7 @@ done: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers done"); if (st->prefix.insert_count > 0) { - if (ngx_http_v3_send_ack_header(c, c->quic->id) != NGX_OK) { + if (ngx_http_v3_send_ack_section(c, c->quic->id) != NGX_OK) { return NGX_ERROR; } } @@ -334,8 +334,8 @@ done: static ngx_int_t -ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, - ngx_http_v3_parse_header_block_prefix_t *st, u_char ch) +ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c, + ngx_http_v3_parse_field_section_prefix_t *st, u_char ch) { ngx_int_t rc; enum { @@ -350,7 +350,7 @@ ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, case sw_start: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header block prefix"); + "http3 parse field section prefix"); st->state = sw_req_insert_count; @@ -401,7 +401,7 @@ done: } ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header block prefix done " + "http3 parse field section prefix done " "insert_count:%ui, sign:%ui, delta_base:%ui, base:%ui", st->insert_count, st->sign, st->delta_base, st->base); @@ -411,75 +411,75 @@ done: static ngx_int_t -ngx_http_v3_parse_header_rep(ngx_connection_t *c, - ngx_http_v3_parse_header_rep_t *st, ngx_uint_t base, u_char ch) +ngx_http_v3_parse_field_rep(ngx_connection_t *c, + ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, u_char ch) { ngx_int_t rc; enum { sw_start = 0, - sw_header_ri, - sw_header_lri, - sw_header_l, - sw_header_pbi, - sw_header_lpbi + sw_field_ri, + sw_field_lri, + sw_field_l, + sw_field_pbi, + sw_field_lpbi }; if (st->state == sw_start) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header representation"); + "http3 parse field representation"); - ngx_memzero(&st->header, sizeof(ngx_http_v3_parse_header_t)); + ngx_memzero(&st->field, sizeof(ngx_http_v3_parse_field_t)); - st->header.base = base; + st->field.base = base; if (ch & 0x80) { - /* Indexed Header Field */ + /* Indexed Field Line */ - st->state = sw_header_ri; + st->state = sw_field_ri; } else if (ch & 0x40) { - /* Literal Header Field With Name Reference */ + /* Literal Field Line With Name Reference */ - st->state = sw_header_lri; + st->state = sw_field_lri; } else if (ch & 0x20) { - /* Literal Header Field Without Name Reference */ + /* Literal Field Line With Literal Name */ - st->state = sw_header_l; + st->state = sw_field_l; } else if (ch & 0x10) { - /* Indexed Header Field With Post-Base Index */ + /* Indexed Field Line With Post-Base Index */ - st->state = sw_header_pbi; + st->state = sw_field_pbi; } else { - /* Literal Header Field With Post-Base Name Reference */ + /* Literal Field Line With Post-Base Name Reference */ - st->state = sw_header_lpbi; + st->state = sw_field_lpbi; } } switch (st->state) { - case sw_header_ri: - rc = ngx_http_v3_parse_header_ri(c, &st->header, ch); + case sw_field_ri: + rc = ngx_http_v3_parse_field_ri(c, &st->field, ch); break; - case sw_header_lri: - rc = ngx_http_v3_parse_header_lri(c, &st->header, ch); + case sw_field_lri: + rc = ngx_http_v3_parse_field_lri(c, &st->field, ch); break; - case sw_header_l: - rc = ngx_http_v3_parse_header_l(c, &st->header, ch); + case sw_field_l: + rc = ngx_http_v3_parse_field_l(c, &st->field, ch); break; - case sw_header_pbi: - rc = ngx_http_v3_parse_header_pbi(c, &st->header, ch); + case sw_field_pbi: + rc = ngx_http_v3_parse_field_pbi(c, &st->field, ch); break; - case sw_header_lpbi: - rc = ngx_http_v3_parse_header_lpbi(c, &st->header, ch); + case sw_field_lpbi: + rc = ngx_http_v3_parse_field_lpbi(c, &st->field, ch); break; default: @@ -491,7 +491,7 @@ ngx_http_v3_parse_header_rep(ngx_connection_t *c, } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header representation done"); + "http3 parse field representation done"); st->state = sw_start; return NGX_DONE; @@ -523,7 +523,7 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, if (n > cscf->large_client_header_buffers.size) { ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent too large header field"); + "client sent too large field line"); return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; } @@ -578,7 +578,7 @@ done: static ngx_int_t -ngx_http_v3_parse_header_ri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, +ngx_http_v3_parse_field_ri(ngx_connection_t *c, ngx_http_v3_parse_field_t *st, u_char ch) { ngx_int_t rc; @@ -591,7 +591,7 @@ ngx_http_v3_parse_header_ri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, case sw_start: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header ri"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field ri"); st->dynamic = (ch & 0x40) ? 0 : 1; st->state = sw_index; @@ -614,7 +614,7 @@ ngx_http_v3_parse_header_ri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, done: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header ri done %s%ui]", + "http3 parse field ri done %s%ui]", st->dynamic ? "dynamic[-" : "static[", st->index); if (st->dynamic) { @@ -633,8 +633,8 @@ done: static ngx_int_t -ngx_http_v3_parse_header_lri(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch) +ngx_http_v3_parse_field_lri(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch) { ngx_int_t rc; enum { @@ -649,7 +649,7 @@ ngx_http_v3_parse_header_lri(ngx_connection_t *c, case sw_start: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header lri"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field lri"); st->dynamic = (ch & 0x10) ? 0 : 1; st->state = sw_index; @@ -705,7 +705,7 @@ ngx_http_v3_parse_header_lri(ngx_connection_t *c, done: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header lri done %s%ui] \"%V\"", + "http3 parse field lri done %s%ui] \"%V\"", st->dynamic ? "dynamic[-" : "static[", st->index, &st->value); @@ -724,8 +724,8 @@ done: static ngx_int_t -ngx_http_v3_parse_header_l(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch) +ngx_http_v3_parse_field_l(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch) { ngx_int_t rc; enum { @@ -741,7 +741,7 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, case sw_start: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header l"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field l"); st->literal.huffman = (ch & 0x08) ? 1 : 0; st->state = sw_name_len; @@ -812,7 +812,7 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, done: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header l done \"%V\" \"%V\"", + "http3 parse field l done \"%V\" \"%V\"", &st->name, &st->value); st->state = sw_start; @@ -821,8 +821,8 @@ done: static ngx_int_t -ngx_http_v3_parse_header_pbi(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch) +ngx_http_v3_parse_field_pbi(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch) { ngx_int_t rc; enum { @@ -834,7 +834,7 @@ ngx_http_v3_parse_header_pbi(ngx_connection_t *c, case sw_start: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header pbi"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field pbi"); st->state = sw_index; @@ -856,7 +856,7 @@ ngx_http_v3_parse_header_pbi(ngx_connection_t *c, done: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header pbi done dynamic[+%ui]", st->index); + "http3 parse field pbi done dynamic[+%ui]", st->index); rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, &st->value); @@ -870,8 +870,8 @@ done: static ngx_int_t -ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch) +ngx_http_v3_parse_field_lpbi(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch) { ngx_int_t rc; enum { @@ -887,7 +887,7 @@ ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, case sw_start: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header lpbi"); + "http3 parse field lpbi"); st->state = sw_index; @@ -942,7 +942,7 @@ ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, done: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header lpbi done dynamic[+%ui] \"%V\"", + "http3 parse field lpbi done dynamic[+%ui] \"%V\"", st->index, &st->value); rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, NULL); @@ -1260,7 +1260,7 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, enum { sw_start = 0, sw_inr, - sw_iwnr, + sw_iln, sw_capacity, sw_duplicate }; @@ -1276,12 +1276,12 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, st->state = sw_inr; } else if (ch & 0x40) { - /* Insert Without Name Reference */ + /* Insert With Literal Name */ - st->state = sw_iwnr; + st->state = sw_iln; } else if (ch & 0x20) { - /* Set Dynamic Table Capacity */ + /* Set Dynamic Table Capacity */ st->state = sw_capacity; @@ -1296,16 +1296,16 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, case sw_inr: - rc = ngx_http_v3_parse_header_inr(c, &st->header, ch); + rc = ngx_http_v3_parse_field_inr(c, &st->field, ch); if (rc != NGX_DONE) { return rc; } goto done; - case sw_iwnr: + case sw_iln: - rc = ngx_http_v3_parse_header_iwnr(c, &st->header, ch); + rc = ngx_http_v3_parse_field_iln(c, &st->field, ch); if (rc != NGX_DONE) { return rc; } @@ -1354,8 +1354,8 @@ done: static ngx_int_t -ngx_http_v3_parse_header_inr(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch) +ngx_http_v3_parse_field_inr(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch) { ngx_int_t rc; enum { @@ -1370,7 +1370,7 @@ ngx_http_v3_parse_header_inr(ngx_connection_t *c, case sw_start: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header inr"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field inr"); st->dynamic = (ch & 0x40) ? 0 : 1; st->state = sw_name_index; @@ -1427,7 +1427,7 @@ ngx_http_v3_parse_header_inr(ngx_connection_t *c, done: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header inr done %s[%ui] \"%V\"", + "http3 parse field inr done %s[%ui] \"%V\"", st->dynamic ? "dynamic" : "static", st->index, &st->value); @@ -1442,8 +1442,8 @@ done: static ngx_int_t -ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, - ngx_http_v3_parse_header_t *st, u_char ch) +ngx_http_v3_parse_field_iln(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, u_char ch) { ngx_int_t rc; enum { @@ -1460,7 +1460,7 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, case sw_start: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header iwnr"); + "http3 parse field iln"); st->literal.huffman = (ch & 0x20) ? 1 : 0; st->state = sw_name_len; @@ -1532,7 +1532,7 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, done: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header iwnr done \"%V\":\"%V\"", + "http3 parse field iln done \"%V\":\"%V\"", &st->name, &st->value); rc = ngx_http_v3_insert(c, &st->name, &st->value); @@ -1552,7 +1552,7 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, ngx_int_t rc; enum { sw_start = 0, - sw_ack_header, + sw_ack_section, sw_cancel_stream, sw_inc_insert_count }; @@ -1563,9 +1563,9 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, "http3 parse decoder instruction"); if (ch & 0x80) { - /* Header Acknowledgement */ + /* Section Acknowledgment */ - st->state = sw_ack_header; + st->state = sw_ack_section; } else if (ch & 0x40) { /* Stream Cancellation */ @@ -1581,14 +1581,14 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, switch (st->state) { - case sw_ack_header: + case sw_ack_section: rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); if (rc != NGX_DONE) { return rc; } - rc = ngx_http_v3_ack_header(c, st->pint.value); + rc = ngx_http_v3_ack_section(c, st->pint.value); if (rc != NGX_OK) { return rc; } diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h index c187b7bc2..5ca3a410f 100644 --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -41,7 +41,7 @@ typedef struct { ngx_uint_t sign; ngx_uint_t base; ngx_http_v3_parse_prefix_int_t pint; -} ngx_http_v3_parse_header_block_prefix_t; +} ngx_http_v3_parse_field_section_prefix_t; typedef struct { @@ -65,13 +65,13 @@ typedef struct { ngx_http_v3_parse_prefix_int_t pint; ngx_http_v3_parse_literal_t literal; -} ngx_http_v3_parse_header_t; +} ngx_http_v3_parse_field_t; typedef struct { ngx_uint_t state; - ngx_http_v3_parse_header_t header; -} ngx_http_v3_parse_header_rep_t; + ngx_http_v3_parse_field_t field; +} ngx_http_v3_parse_field_rep_t; typedef struct { @@ -79,14 +79,14 @@ typedef struct { ngx_uint_t type; ngx_uint_t length; ngx_http_v3_parse_varlen_int_t vlint; - ngx_http_v3_parse_header_block_prefix_t prefix; - ngx_http_v3_parse_header_rep_t header_rep; + ngx_http_v3_parse_field_section_prefix_t prefix; + ngx_http_v3_parse_field_rep_t field_rep; } ngx_http_v3_parse_headers_t; typedef struct { ngx_uint_t state; - ngx_http_v3_parse_header_t header; + ngx_http_v3_parse_field_t field; ngx_http_v3_parse_prefix_int_t pint; } ngx_http_v3_parse_encoder_t; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 9d7ca952d..5fc6e233b 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -304,8 +304,8 @@ ngx_http_v3_process_request(ngx_event_t *rev) /* rc == NGX_OK || rc == NGX_DONE */ - if (ngx_http_v3_process_header(r, &st->header_rep.header.name, - &st->header_rep.header.value) + if (ngx_http_v3_process_header(r, &st->field_rep.field.name, + &st->field_rep.field.value) != NGX_OK) { break; diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 8b2047f43..fa39be78a 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -603,14 +603,14 @@ ngx_http_v3_send_duplicate(ngx_connection_t *c, ngx_uint_t index) ngx_int_t -ngx_http_v3_send_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) +ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) { u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; size_t n; ngx_connection_t *dc; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 client ack header %ui", stream_id); + "http3 client ack section %ui", stream_id); dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); if (dc == NULL) { diff --git a/src/http/v3/ngx_http_v3_streams.h b/src/http/v3/ngx_http_v3_streams.h index a2126a7f8..4f7290e4c 100644 --- a/src/http/v3/ngx_http_v3_streams.h +++ b/src/http/v3/ngx_http_v3_streams.h @@ -34,7 +34,7 @@ ngx_int_t ngx_http_v3_send_insert(ngx_connection_t *c, ngx_str_t *name, ngx_int_t ngx_http_v3_send_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); ngx_int_t ngx_http_v3_send_duplicate(ngx_connection_t *c, ngx_uint_t index); -ngx_int_t ngx_http_v3_send_ack_header(ngx_connection_t *c, +ngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id); ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index 8f4e28edd..348088774 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -15,7 +15,7 @@ static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); static void ngx_http_v3_unblock(void *data); -static ngx_int_t ngx_http_v3_new_header(ngx_connection_t *c); +static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c); typedef struct { @@ -25,7 +25,7 @@ typedef struct { } ngx_http_v3_block_t; -static ngx_http_v3_header_t ngx_http_v3_static_table[] = { +static ngx_http_v3_field_t ngx_http_v3_static_table[] = { { ngx_string(":authority"), ngx_string("") }, { ngx_string(":path"), ngx_string("/") }, @@ -198,7 +198,7 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) { u_char *p; size_t size; - ngx_http_v3_header_t *h; + ngx_http_v3_field_t *field; ngx_http_v3_session_t *h3c; ngx_http_v3_dynamic_table_t *dt; @@ -215,21 +215,21 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) "http3 insert [%ui] \"%V\":\"%V\", size:%uz", dt->base + dt->nelts, name, value, size); - p = ngx_alloc(sizeof(ngx_http_v3_header_t) + name->len + value->len, + p = ngx_alloc(sizeof(ngx_http_v3_field_t) + name->len + value->len, c->log); if (p == NULL) { return NGX_ERROR; } - h = (ngx_http_v3_header_t *) p; + field = (ngx_http_v3_field_t *) p; - h->name.data = p + sizeof(ngx_http_v3_header_t); - h->name.len = name->len; - h->value.data = ngx_cpymem(h->name.data, name->data, name->len); - h->value.len = value->len; - ngx_memcpy(h->value.data, value->data, value->len); + field->name.data = p + sizeof(ngx_http_v3_field_t); + field->name.len = name->len; + field->value.data = ngx_cpymem(field->name.data, name->data, name->len); + field->value.len = value->len; + ngx_memcpy(field->value.data, value->data, value->len); - dt->elts[dt->nelts++] = h; + dt->elts[dt->nelts++] = field; dt->size += size; /* TODO increment can be sent less often */ @@ -238,7 +238,7 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) return NGX_ERROR; } - if (ngx_http_v3_new_header(c) != NGX_OK) { + if (ngx_http_v3_new_entry(c) != NGX_OK) { return NGX_ERROR; } @@ -250,7 +250,7 @@ ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) { ngx_uint_t max, prev_max; - ngx_http_v3_header_t **elts; + ngx_http_v3_field_t **elts; ngx_http_v3_session_t *h3c; ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_dynamic_table_t *dt; @@ -323,7 +323,7 @@ ngx_http_v3_evict(ngx_connection_t *c, size_t need) { size_t size, target; ngx_uint_t n; - ngx_http_v3_header_t *h; + ngx_http_v3_field_t *field; ngx_http_v3_session_t *h3c; ngx_http_v3_dynamic_table_t *dt; @@ -340,14 +340,14 @@ ngx_http_v3_evict(ngx_connection_t *c, size_t need) n = 0; while (dt->size > target) { - h = dt->elts[n++]; - size = ngx_http_v3_table_entry_size(&h->name, &h->value); + field = dt->elts[n++]; + size = ngx_http_v3_table_entry_size(&field->name, &field->value); ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 evict [%ui] \"%V\":\"%V\" size:%uz", - dt->base, &h->name, &h->value, size); + dt->base, &field->name, &field->value, size); - ngx_free(h); + ngx_free(field); dt->size -= size; } @@ -388,10 +388,10 @@ ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) ngx_int_t -ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) +ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 ack header %ui", stream_id); + "http3 ack section %ui", stream_id); /* we do not use dynamic tables */ @@ -415,8 +415,8 @@ ngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value) { - ngx_uint_t nelts; - ngx_http_v3_header_t *h; + ngx_uint_t nelts; + ngx_http_v3_field_t *field; nelts = sizeof(ngx_http_v3_static_table) / sizeof(ngx_http_v3_static_table[0]); @@ -428,18 +428,18 @@ ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, return NGX_ERROR; } - h = &ngx_http_v3_static_table[index]; + field = &ngx_http_v3_static_table[index]; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 static[%ui] lookup \"%V\":\"%V\"", - index, &h->name, &h->value); + index, &field->name, &field->value); if (name) { - *name = h->name; + *name = field->name; } if (value) { - *value = h->value; + *value = field->value; } return NGX_OK; @@ -450,7 +450,7 @@ ngx_int_t ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value) { - ngx_http_v3_header_t *h; + ngx_http_v3_field_t *field; ngx_http_v3_session_t *h3c; ngx_http_v3_dynamic_table_t *dt; @@ -464,18 +464,18 @@ ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, return NGX_ERROR; } - h = dt->elts[index - dt->base]; + field = dt->elts[index - dt->base]; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dynamic[%ui] lookup \"%V\":\"%V\"", - index, &h->name, &h->value); + index, &field->name, &field->value); if (name) { - *name = h->name; + *name = field->name; } if (value) { - *value = h->value; + *value = field->value; } return NGX_OK; @@ -617,7 +617,7 @@ ngx_http_v3_unblock(void *data) static ngx_int_t -ngx_http_v3_new_header(ngx_connection_t *c) +ngx_http_v3_new_entry(ngx_connection_t *c) { ngx_queue_t *q; ngx_connection_t *bc; @@ -627,7 +627,7 @@ ngx_http_v3_new_header(ngx_connection_t *c) h3c = ngx_http_v3_get_session(c); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 new dynamic header, blocked:%ui", h3c->nblocked); + "http3 new dynamic entry, blocked:%ui", h3c->nblocked); while (!ngx_queue_empty(&h3c->blocked)) { q = ngx_queue_head(&h3c->blocked); diff --git a/src/http/v3/ngx_http_v3_tables.h b/src/http/v3/ngx_http_v3_tables.h index 36589f171..991cc12c9 100644 --- a/src/http/v3/ngx_http_v3_tables.h +++ b/src/http/v3/ngx_http_v3_tables.h @@ -17,11 +17,11 @@ typedef struct { ngx_str_t name; ngx_str_t value; -} ngx_http_v3_header_t; +} ngx_http_v3_field_t; typedef struct { - ngx_http_v3_header_t **elts; + ngx_http_v3_field_t **elts; ngx_uint_t nelts; ngx_uint_t base; size_t size; @@ -36,7 +36,7 @@ ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value); ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index); -ngx_int_t ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id); ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); ngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value); -- cgit From 6157d0b5c1b3a6be7928748df2cda19838889f4f Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Tue, 20 Jul 2021 12:37:12 +0300 Subject: QUIC: the "quic_gso" directive. The directive enables usage of UDP segmentation offloading by quic. By default, gso is disabled since it is not always operational when detected (depends on interface configuration). --- src/http/modules/ngx_http_quic_module.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index d933dd1f9..ab84583f2 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -126,6 +126,13 @@ static ngx_command_t ngx_http_quic_commands[] = { offsetof(ngx_quic_conf_t, retry), NULL }, + { ngx_string("quic_gso"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_quic_conf_t, gso_enabled), + NULL }, + { ngx_string("quic_host_key"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_http_quic_host_key, @@ -290,6 +297,7 @@ ngx_http_quic_create_srv_conf(ngx_conf_t *cf) conf->tp.active_connection_id_limit = NGX_CONF_UNSET_UINT; conf->retry = NGX_CONF_UNSET; + conf->gso_enabled = NGX_CONF_UNSET; conf->require_alpn = 1; return conf; @@ -348,6 +356,7 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) prev->tp.active_connection_id_limit, 2); ngx_conf_merge_value(conf->retry, prev->retry, 0); + ngx_conf_merge_value(conf->gso_enabled, prev->gso_enabled, 0); ngx_conf_merge_str_value(conf->host_key, prev->host_key, ""); -- cgit From fc2311137fdb61f4a9c4cfdd8654ac0155ff85c9 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 13 Jul 2021 22:44:03 +0300 Subject: HTTP/3: response trailers support. --- src/http/v3/ngx_http_v3_filter_module.c | 134 ++++++++++++++++++++++++++++---- 1 file changed, 118 insertions(+), 16 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 52db65736..8f2e3ff43 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -49,7 +49,8 @@ static ngx_chain_t *ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, uint64_t push_id); static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in); -static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r); +static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r, + ngx_http_v3_filter_ctx_t *ctx); static ngx_int_t ngx_http_v3_filter_init(ngx_conf_t *cf); @@ -515,7 +516,9 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) *ll = hl; ll = &cl->next; - if (r->headers_out.content_length_n >= 0 && !r->header_only) { + if (r->headers_out.content_length_n >= 0 + && !r->header_only && !r->expect_trailers) + { len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA) + ngx_http_v3_encode_varlen_int(NULL, r->headers_out.content_length_n); @@ -1303,7 +1306,7 @@ ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) } if (cl->buf->last_buf) { - tl = ngx_http_v3_create_trailers(r); + tl = ngx_http_v3_create_trailers(r, ctx); if (tl == NULL) { return NGX_ERROR; } @@ -1326,32 +1329,131 @@ ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) static ngx_chain_t * -ngx_http_v3_create_trailers(ngx_http_request_t *r) +ngx_http_v3_create_trailers(ngx_http_request_t *r, + ngx_http_v3_filter_ctx_t *ctx) { - ngx_buf_t *b; - ngx_chain_t *cl; + size_t len, n; + u_char *p; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *cl, *hl; + ngx_list_part_t *part; + ngx_table_elt_t *header; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 create trailers"); + len = 0; - /* XXX */ + part = &r->headers_out.trailers.part; + header = part->elts; - b = ngx_calloc_buf(r->pool); - if (b == NULL) { + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += ngx_http_v3_encode_field_l(NULL, &header[i].key, + &header[i].value); + } + + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { return NULL; } + b = cl->buf; + + b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module; + b->memory = 0; b->last_buf = 1; - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { + if (len == 0) { + b->temporary = 0; + b->pos = b->last = NULL; + return cl; + } + + b->temporary = 1; + + len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0); + + b->pos = ngx_palloc(r->pool, len); + if (b->pos == NULL) { return NULL; } - cl->buf = b; - cl->next = NULL; + b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->pos, + 0, 0, 0); + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { - return cl; + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 trailer: \"%V: %V\"", + &header[i].key, &header[i].value); + + b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, + &header[i].key, + &header[i].value); + } + + n = b->last - b->pos; + + hl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (hl == NULL) { + return NULL; + } + + b = hl->buf; + p = b->start; + + if (p == NULL) { + p = ngx_palloc(r->pool, NGX_HTTP_V3_VARLEN_INT_LEN * 2); + if (p == NULL) { + return NULL; + } + + b->start = p; + b->end = p + NGX_HTTP_V3_VARLEN_INT_LEN * 2; + } + + b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module; + b->memory = 0; + b->temporary = 1; + b->pos = p; + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(p, + NGX_HTTP_V3_FRAME_HEADERS); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); + + hl->next = cl; + + return hl; } -- cgit From 245a15ed2738ad5e7ca971f2ae3c34d2288dc4f9 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 16 Jul 2021 15:43:01 +0300 Subject: HTTP/3: use request pool instead of connection pool. In several parts of ngx_http_v3_header_filter() connection pool was used for request-related data. --- src/http/v3/ngx_http_v3_filter_module.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 8f2e3ff43..71aa839e1 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -483,7 +483,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) b->last_buf = 1; } - cl = ngx_alloc_chain_link(c->pool); + cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } @@ -496,7 +496,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) + ngx_http_v3_encode_varlen_int(NULL, n); - b = ngx_create_temp_buf(c->pool, len); + b = ngx_create_temp_buf(r->pool, len); if (b == NULL) { return NGX_ERROR; } @@ -505,7 +505,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) NGX_HTTP_V3_FRAME_HEADERS); b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); - hl = ngx_alloc_chain_link(c->pool); + hl = ngx_alloc_chain_link(r->pool); if (hl == NULL) { return NGX_ERROR; } @@ -523,7 +523,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) + ngx_http_v3_encode_varlen_int(NULL, r->headers_out.content_length_n); - b = ngx_create_temp_buf(c->pool, len); + b = ngx_create_temp_buf(r->pool, len); if (b == NULL) { return NGX_ERROR; } @@ -533,7 +533,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, r->headers_out.content_length_n); - cl = ngx_alloc_chain_link(c->pool); + cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } -- cgit From 5bb45c98a73044ce2b7f389c7764cc10cc869bed Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 29 Jul 2021 10:03:36 +0300 Subject: HTTP/3: require mandatory uni streams before additional ones. As per quic-http-34: Endpoints SHOULD create the HTTP control stream as well as the unidirectional streams required by mandatory extensions (such as the QPACK encoder and decoder streams) first, and then create additional streams as allowed by their peer. Previously, client could create and destroy additional uni streams unlimited number of times before creating mandatory streams. --- src/http/v3/ngx_http_v3_streams.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index fa39be78a..693225b89 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -91,6 +91,8 @@ ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type) ngx_http_v3_session_t *h3c; ngx_http_v3_uni_stream_t *us; + h3c = ngx_http_v3_get_session(c); + switch (type) { case NGX_HTTP_V3_STREAM_ENCODER: @@ -119,12 +121,19 @@ ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 stream 0x%02xL", type); + + if (h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_ENCODER] == NULL + || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_DECODER] == NULL + || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_CONTROL] == NULL) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "missing mandatory stream"); + return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; + } + index = -1; } if (index >= 0) { - h3c = ngx_http_v3_get_session(c); - if (h3c->known_streams[index]) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; -- cgit From 2f833198b8229842dbbc57f7e86b00b19ed3b294 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 29 Jul 2021 12:17:56 +0300 Subject: HTTP/3: http3_max_uni_streams directive. The directive limits the number of uni streams client is allowed to create. --- src/http/v3/ngx_http_v3.h | 2 ++ src/http/v3/ngx_http_v3_module.c | 12 ++++++++++++ src/http/v3/ngx_http_v3_streams.c | 14 ++++++++++++++ 3 files changed, 28 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index e693af7d8..bea560864 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -53,6 +53,7 @@ #define NGX_HTTP_V3_DEFAULT_MAX_TABLE_CAPACITY 16384 #define NGX_HTTP_V3_DEFAULT_MAX_BLOCKED_STREAMS 16 #define NGX_HTTP_V3_DEFAULT_MAX_CONCURRENT_PUSHES 10 +#define NGX_HTTP_V3_DEFAULT_MAX_UNI_STREAMS 3 /* HTTP/3 errors */ #define NGX_HTTP_V3_ERR_NO_ERROR 0x100 @@ -99,6 +100,7 @@ typedef struct { size_t max_table_capacity; ngx_uint_t max_blocked_streams; ngx_uint_t max_concurrent_pushes; + ngx_uint_t max_uni_streams; } ngx_http_v3_srv_conf_t; diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 3b651f044..873ebb2f3 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -42,6 +42,13 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, max_concurrent_pushes), NULL }, + { ngx_string("http3_max_uni_streams"), + 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, max_uni_streams), + NULL }, + { ngx_string("http3_push"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_v3_push, @@ -104,6 +111,7 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) h3scf->max_table_capacity = NGX_CONF_UNSET_SIZE; h3scf->max_blocked_streams = NGX_CONF_UNSET_UINT; h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT; + h3scf->max_uni_streams = NGX_CONF_UNSET_UINT; return h3scf; } @@ -127,6 +135,10 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) prev->max_concurrent_pushes, NGX_HTTP_V3_DEFAULT_MAX_CONCURRENT_PUSHES); + ngx_conf_merge_uint_value(conf->max_uni_streams, + prev->max_uni_streams, + NGX_HTTP_V3_DEFAULT_MAX_UNI_STREAMS); + return NGX_CONF_OK; } diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 693225b89..1b0f91454 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -35,10 +35,24 @@ static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, void ngx_http_v3_init_uni_stream(ngx_connection_t *c) { + uint64_t n; + ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_uni_stream_t *us; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + n = c->quic->id >> 2; + + if (n >= h3scf->max_uni_streams) { + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "reached maximum number of uni streams"); + ngx_http_close_connection(c); + return; + } + c->quic->cancelable = 1; us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); -- cgit From b93ae5d0670f265bec60cd616dd42b0ce96d8e2d Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 2 Aug 2021 15:48:21 +0300 Subject: QUIC: stream limits in "hq" mode. The "hq" mode is HTTP/0.9-1.1 over QUIC. The following limits are introduced: - uni streams are not allowed - keepalive_requests is enforced - keepalive_time is enforced In case of error, QUIC connection is finalized with 0x101 code. This code corresponds to HTTP/3 General Protocol Error. --- src/http/modules/ngx_http_quic_module.c | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index ab84583f2..b41c069b6 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -188,6 +188,7 @@ static ngx_str_t ngx_http_quic_salt = ngx_string("ngx_quic"); ngx_int_t ngx_http_quic_init(ngx_connection_t *c) { + uint64_t n; ngx_quic_conf_t *qcf; ngx_http_connection_t *hc, *phc; ngx_http_core_loc_conf_t *clcf; @@ -208,6 +209,40 @@ ngx_http_quic_init(ngx_connection_t *c) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http init quic stream"); +#if (NGX_HTTP_V3) + if (!hc->addr_conf->http3) +#endif + { + /* Use HTTP/3 General Protocol Error Code 0x101 for finalization */ + + if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { + ngx_quic_finalize_connection(c->quic->parent, 0x101, + "unexpected uni stream"); + ngx_http_close_connection(c); + return NGX_DONE; + } + + clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + + n = c->quic->id >> 2; + + if (n >= clcf->keepalive_requests) { + ngx_quic_finalize_connection(c->quic->parent, 0x101, + "reached maximum number of requests"); + ngx_http_close_connection(c); + return NGX_DONE; + } + + if (ngx_current_msec - c->quic->parent->start_time + > clcf->keepalive_time) + { + ngx_quic_finalize_connection(c->quic->parent, 0x101, + "reached maximum time for requests"); + ngx_http_close_connection(c); + return NGX_DONE; + } + } + phc = ngx_http_quic_get_connection(c); if (phc->ssl_servername) { -- cgit From e1ad576f960ab2b455b4d12869f69cb648feba42 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 29 Jul 2021 16:01:37 +0300 Subject: HTTP/3: close connection on keepalive_requests * 2. After receiving GOAWAY, client is not supposed to create new streams. However, until client reads this frame, we allow it to create new streams, which are gracefully rejected. To prevent client from abusing this algorithm, a new limit is introduced. Upon reaching keepalive_requests * 2, server now closes the entire QUIC connection claiming excessive load. --- src/http/v3/ngx_http_v3_request.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 5fc6e233b..f45a7b95e 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -81,6 +81,15 @@ ngx_http_v3_init(ngx_connection_t *c) clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + n = c->quic->id >> 2; + + if (n >= clcf->keepalive_requests * 2) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "too many requests per connection"); + ngx_http_close_connection(c); + return; + } + h3c = ngx_http_v3_get_session(c); if (h3c->goaway) { @@ -89,8 +98,6 @@ ngx_http_v3_init(ngx_connection_t *c) return; } - n = c->quic->id >> 2; - if (n + 1 == clcf->keepalive_requests || ngx_current_msec - c->quic->parent->start_time > clcf->keepalive_time) -- cgit From af83b3c32c26646f2b3bf1b8f097eb175f6b5bb5 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 4 Aug 2021 17:35:11 +0300 Subject: HTTP/3: replaced macros with values. --- src/http/v3/ngx_http_v3.h | 5 ----- src/http/v3/ngx_http_v3_module.c | 12 ++++-------- 2 files changed, 4 insertions(+), 13 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index bea560864..9076b6ff5 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -50,11 +50,6 @@ #define NGX_HTTP_V3_STREAM_SERVER_DECODER 5 #define NGX_HTTP_V3_MAX_KNOWN_STREAM 6 -#define NGX_HTTP_V3_DEFAULT_MAX_TABLE_CAPACITY 16384 -#define NGX_HTTP_V3_DEFAULT_MAX_BLOCKED_STREAMS 16 -#define NGX_HTTP_V3_DEFAULT_MAX_CONCURRENT_PUSHES 10 -#define NGX_HTTP_V3_DEFAULT_MAX_UNI_STREAMS 3 - /* HTTP/3 errors */ #define NGX_HTTP_V3_ERR_NO_ERROR 0x100 #define NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR 0x101 diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 873ebb2f3..4b59b097c 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -124,20 +124,16 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_v3_srv_conf_t *conf = child; ngx_conf_merge_size_value(conf->max_table_capacity, - prev->max_table_capacity, - NGX_HTTP_V3_DEFAULT_MAX_TABLE_CAPACITY); + prev->max_table_capacity, 16384); ngx_conf_merge_uint_value(conf->max_blocked_streams, - prev->max_blocked_streams, - NGX_HTTP_V3_DEFAULT_MAX_BLOCKED_STREAMS); + prev->max_blocked_streams, 16); ngx_conf_merge_uint_value(conf->max_concurrent_pushes, - prev->max_concurrent_pushes, - NGX_HTTP_V3_DEFAULT_MAX_CONCURRENT_PUSHES); + prev->max_concurrent_pushes, 10); ngx_conf_merge_uint_value(conf->max_uni_streams, - prev->max_uni_streams, - NGX_HTTP_V3_DEFAULT_MAX_UNI_STREAMS); + prev->max_uni_streams, 3); return NGX_CONF_OK; } -- cgit From d895a831ae76d97999de777d9289e5e6e29c5741 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Thu, 5 Aug 2021 11:09:13 +0300 Subject: HTTP/3: got rid of HTTP/2 module dependency. The Huffman encoder/decoder now can be built separately from HTTP/2 module. --- src/http/ngx_http.h | 6 ++++++ src/http/v2/ngx_http_v2.h | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 70109adc2..fb4157715 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -180,6 +180,12 @@ ngx_int_t ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types, ngx_uint_t ngx_http_degraded(ngx_http_request_t *); #endif +#if (NGX_HTTP_V2 || NGX_HTTP_V3) +ngx_int_t ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len, + u_char **dst, ngx_uint_t last, ngx_log_t *log); +size_t ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst, + ngx_uint_t lower); +#endif extern ngx_module_t ngx_http_module; diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index 349229711..65fc65812 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -312,12 +312,6 @@ ngx_int_t ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c, ngx_int_t ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size); -ngx_int_t ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len, - u_char **dst, ngx_uint_t last, ngx_log_t *log); -size_t ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst, - ngx_uint_t lower); - - #define ngx_http_v2_prefix(bits) ((1 << (bits)) - 1) -- cgit From 6fb9bdad6a811f00388044a89d322fbdfd072606 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Aug 2021 12:35:12 +0300 Subject: HTTP/3: disabled control characters and space in header names. This is a follow up to 41f4bd4c51f1. --- src/http/v3/ngx_http_v3_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index f45a7b95e..1fcbad1de 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -428,7 +428,7 @@ ngx_http_v3_validate_header(ngx_http_request_t *r, ngx_str_t *name, continue; } - if (ch == '\0' || ch == LF || ch == CR || ch == ':' + if (ch <= 0x20 || ch == 0x7f || ch == ':' || (ch >= 'A' && ch <= 'Z')) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, -- cgit From 2ff0af368d08ade41cb164eb54f621361c9f1bec Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 24 Aug 2021 13:03:48 +0300 Subject: HTTP/3: fixed dead store assignment. Found by Clang Static Analyzer. --- src/http/v3/ngx_http_v3_request.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 1fcbad1de..0bd585317 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1217,7 +1217,6 @@ done: b->last_buf = 1; *ll = tl; - ll = &tl->next; } else { -- cgit From 68d4325de08053f4cb0db590dc72ef9494c33bd6 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 8 Jul 2021 21:52:47 +0300 Subject: HTTP/3: bulk parse functions. Previously HTTP/3 streams were parsed by one character. Now all parse functions receive buffers. This should optimize parsing time and CPU load. --- src/http/v3/ngx_http_v3_parse.c | 1882 ++++++++++++++++++++----------------- src/http/v3/ngx_http_v3_parse.h | 6 +- src/http/v3/ngx_http_v3_request.c | 15 +- src/http/v3/ngx_http_v3_streams.c | 35 +- 4 files changed, 1068 insertions(+), 870 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index c5fc24482..3f676b4bb 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -14,51 +14,91 @@ ((type) == 0x02 || (type) == 0x06 || (type) == 0x08 || (type) == 0x09) +static void ngx_http_v3_parse_start_local(ngx_buf_t *b, ngx_buf_t *loc, + ngx_uint_t n); +static void ngx_http_v3_parse_end_local(ngx_buf_t *b, ngx_buf_t *loc, + ngx_uint_t *n); +static ngx_int_t ngx_http_v3_parse_skip(ngx_buf_t *b, ngx_uint_t *length); + static ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, - ngx_http_v3_parse_varlen_int_t *st, u_char ch); + ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, - ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch); + ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c, - ngx_http_v3_parse_field_section_prefix_t *st, u_char ch); + ngx_http_v3_parse_field_section_prefix_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_rep(ngx_connection_t *c, - ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, u_char ch); + ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, - ngx_http_v3_parse_literal_t *st, u_char ch); + ngx_http_v3_parse_literal_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_ri(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch); + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_lri(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch); + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_l(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch); + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_pbi(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch); + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_lpbi(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch); + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, - ngx_http_v3_parse_control_t *st, u_char ch); + ngx_http_v3_parse_control_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, - ngx_http_v3_parse_settings_t *st, u_char ch); + ngx_http_v3_parse_settings_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, - ngx_http_v3_parse_encoder_t *st, u_char ch); + ngx_http_v3_parse_encoder_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_inr(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch); + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_iln(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch); + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, - ngx_http_v3_parse_decoder_t *st, u_char ch); + ngx_http_v3_parse_decoder_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value); +static void +ngx_http_v3_parse_start_local(ngx_buf_t *b, ngx_buf_t *loc, ngx_uint_t n) +{ + *loc = *b; + + if ((size_t) (loc->last - loc->pos) > n) { + loc->last = loc->pos + n; + } +} + + +static void +ngx_http_v3_parse_end_local(ngx_buf_t *b, ngx_buf_t *loc, ngx_uint_t *pn) +{ + *pn -= loc->pos - b->pos; + b->pos = loc->pos; +} + + +static ngx_int_t +ngx_http_v3_parse_skip(ngx_buf_t *b, ngx_uint_t *length) +{ + if ((size_t) (b->last - b->pos) < *length) { + *length -= b->last - b->pos; + b->pos = b->last; + return NGX_AGAIN; + } + + b->pos += *length; + return NGX_DONE; +} + + static ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, - ngx_http_v3_parse_varlen_int_t *st, u_char ch) + ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b) { + u_char ch; enum { sw_start = 0, sw_length_2, @@ -70,57 +110,64 @@ ngx_http_v3_parse_varlen_int(ngx_connection_t *c, sw_length_8 }; - switch (st->state) { - - case sw_start: + for ( ;; ) { - st->value = ch; - if (st->value & 0xc0) { - st->state = sw_length_2; - break; + if (b->pos == b->last) { + return NGX_AGAIN; } - goto done; + ch = *b->pos++; + + switch (st->state) { + + case sw_start: - case sw_length_2: + st->value = ch; + if (st->value & 0xc0) { + st->state = sw_length_2; + break; + } - st->value = (st->value << 8) + ch; - if ((st->value & 0xc000) == 0x4000) { - st->value &= 0x3fff; goto done; - } - st->state = sw_length_3; - break; + case sw_length_2: - case sw_length_4: + st->value = (st->value << 8) + ch; + if ((st->value & 0xc000) == 0x4000) { + st->value &= 0x3fff; + goto done; + } - st->value = (st->value << 8) + ch; - if ((st->value & 0xc0000000) == 0x80000000) { - st->value &= 0x3fffffff; - goto done; - } + st->state = sw_length_3; + break; - st->state = sw_length_5; - break; + case sw_length_4: - case sw_length_3: - case sw_length_5: - case sw_length_6: - case sw_length_7: + st->value = (st->value << 8) + ch; + if ((st->value & 0xc0000000) == 0x80000000) { + st->value &= 0x3fffffff; + goto done; + } - st->value = (st->value << 8) + ch; - st->state++; - break; + st->state = sw_length_5; + break; - case sw_length_8: + case sw_length_3: + case sw_length_5: + case sw_length_6: + case sw_length_7: - st->value = (st->value << 8) + ch; - st->value &= 0x3fffffffffffffff; - goto done; - } + st->value = (st->value << 8) + ch; + st->state++; + break; + + case sw_length_8: - return NGX_AGAIN; + st->value = (st->value << 8) + ch; + st->value &= 0x3fffffffffffffff; + goto done; + } + } done: @@ -134,50 +181,58 @@ done: static ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, - ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch) + ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, ngx_buf_t *b) { + u_char ch; ngx_uint_t mask; enum { sw_start = 0, sw_value }; - switch (st->state) { - - case sw_start: - - mask = (1 << prefix) - 1; - st->value = ch & mask; + for ( ;; ) { - if (st->value != mask) { - goto done; + if (b->pos == b->last) { + return NGX_AGAIN; } - st->shift = 0; - st->state = sw_value; - break; + ch = *b->pos++; - case sw_value: + switch (st->state) { - st->value += (uint64_t) (ch & 0x7f) << st->shift; + case sw_start: - if (st->shift == 56 - && ((ch & 0x80) || (st->value & 0xc000000000000000))) - { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client exceeded integer size limit"); - return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; - } + mask = (1 << prefix) - 1; + st->value = ch & mask; - if (ch & 0x80) { - st->shift += 7; + if (st->value != mask) { + goto done; + } + + st->shift = 0; + st->state = sw_value; break; - } - goto done; - } + case sw_value: + + st->value += (uint64_t) (ch & 0x7f) << st->shift; + + if (st->shift == 56 + && ((ch & 0x80) || (st->value & 0xc000000000000000))) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded integer size limit"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } - return NGX_AGAIN; + if (ch & 0x80) { + st->shift += 7; + break; + } + + goto done; + } + } done: @@ -191,8 +246,9 @@ done: ngx_int_t ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, - u_char ch) + ngx_buf_t *b) { + ngx_buf_t loc; ngx_int_t rc; enum { sw_start = 0, @@ -205,118 +261,131 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, sw_done }; - switch (st->state) { + for ( ;; ) { + + switch (st->state) { + + case sw_start: - case sw_start: + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse headers"); - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers"); + st->state = sw_type; - st->state = sw_type; + /* fall through */ - /* fall through */ + case sw_type: - case sw_type: + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); - if (rc != NGX_DONE) { - return rc; - } + st->type = st->vlint.value; - st->type = st->vlint.value; - - if (ngx_http_v3_is_v2_frame(st->type) - || st->type == NGX_HTTP_V3_FRAME_DATA - || st->type == NGX_HTTP_V3_FRAME_GOAWAY - || st->type == NGX_HTTP_V3_FRAME_SETTINGS - || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID - || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH - || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) - { - return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; - } + if (ngx_http_v3_is_v2_frame(st->type) + || st->type == NGX_HTTP_V3_FRAME_DATA + || st->type == NGX_HTTP_V3_FRAME_GOAWAY + || st->type == NGX_HTTP_V3_FRAME_SETTINGS + || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID + || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH + || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) + { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } - st->state = sw_length; - break; + st->state = sw_length; + break; - case sw_length: + case sw_length: - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); - if (rc != NGX_DONE) { - return rc; - } + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } - st->length = st->vlint.value; + st->length = st->vlint.value; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse headers type:%ui, len:%ui", - st->type, st->length); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse headers type:%ui, len:%ui", + st->type, st->length); - if (st->type != NGX_HTTP_V3_FRAME_HEADERS) { - st->state = st->length > 0 ? sw_skip : sw_type; - break; - } + if (st->type != NGX_HTTP_V3_FRAME_HEADERS) { + st->state = st->length > 0 ? sw_skip : sw_type; + break; + } - if (st->length == 0) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } + if (st->length == 0) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } - st->state = sw_prefix; - break; + st->state = sw_prefix; + break; - case sw_skip: + case sw_skip: + + rc = ngx_http_v3_parse_skip(b, &st->length); + if (rc != NGX_DONE) { + return rc; + } - if (--st->length == 0) { st->state = sw_type; - } + break; - break; + case sw_prefix: - case sw_prefix: + ngx_http_v3_parse_start_local(b, &loc, st->length); - if (--st->length == 0) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } + rc = ngx_http_v3_parse_field_section_prefix(c, &st->prefix, &loc); - rc = ngx_http_v3_parse_field_section_prefix(c, &st->prefix, ch); - if (rc != NGX_DONE) { - return rc; - } + ngx_http_v3_parse_end_local(b, &loc, &st->length); - st->state = sw_verify; - break; + if (st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } - case sw_verify: + if (rc != NGX_DONE) { + return rc; + } - rc = ngx_http_v3_check_insert_count(c, st->prefix.insert_count); - if (rc != NGX_OK) { - return rc; - } + st->state = sw_verify; + break; - st->state = sw_field_rep; + case sw_verify: - /* fall through */ + rc = ngx_http_v3_check_insert_count(c, st->prefix.insert_count); + if (rc != NGX_OK) { + return rc; + } - case sw_field_rep: + st->state = sw_field_rep; - rc = ngx_http_v3_parse_field_rep(c, &st->field_rep, st->prefix.base, - ch); + /* fall through */ - if (--st->length == 0 && rc == NGX_AGAIN) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } + case sw_field_rep: - if (rc != NGX_DONE) { - return rc; - } + ngx_http_v3_parse_start_local(b, &loc, st->length); - if (st->length == 0) { - goto done; - } + rc = ngx_http_v3_parse_field_rep(c, &st->field_rep, st->prefix.base, + &loc); - return NGX_OK; - } + ngx_http_v3_parse_end_local(b, &loc, &st->length); + + if (st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } + + if (rc != NGX_DONE) { + return rc; + } + + if (st->length == 0) { + goto done; + } - return NGX_AGAIN; + return NGX_OK; + } + } done: @@ -335,8 +404,9 @@ done: static ngx_int_t ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c, - ngx_http_v3_parse_field_section_prefix_t *st, u_char ch) + ngx_http_v3_parse_field_section_prefix_t *st, ngx_buf_t *b) { + u_char ch; ngx_int_t rc; enum { sw_start = 0, @@ -345,47 +415,54 @@ ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c, sw_read_delta_base }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse field section prefix"); + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field section prefix"); - st->state = sw_req_insert_count; + st->state = sw_req_insert_count; - /* fall through */ + /* fall through */ - case sw_req_insert_count: + case sw_req_insert_count: - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 8, ch); - if (rc != NGX_DONE) { - return rc; - } + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 8, b); + if (rc != NGX_DONE) { + return rc; + } - st->insert_count = st->pint.value; - st->state = sw_delta_base; - break; + st->insert_count = st->pint.value; + st->state = sw_delta_base; + break; - case sw_delta_base: + case sw_delta_base: - st->sign = (ch & 0x80) ? 1 : 0; - st->state = sw_read_delta_base; + if (b->pos == b->last) { + return NGX_AGAIN; + } - /* fall through */ + ch = *b->pos; - case sw_read_delta_base: + st->sign = (ch & 0x80) ? 1 : 0; + st->state = sw_read_delta_base; - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); - if (rc != NGX_DONE) { - return rc; - } + /* fall through */ - st->delta_base = st->pint.value; - goto done; - } + case sw_read_delta_base: - return NGX_AGAIN; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->delta_base = st->pint.value; + goto done; + } + } done: @@ -412,8 +489,9 @@ done: static ngx_int_t ngx_http_v3_parse_field_rep(ngx_connection_t *c, - ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, u_char ch) + ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, ngx_buf_t *b) { + u_char ch; ngx_int_t rc; enum { sw_start = 0, @@ -429,6 +507,12 @@ ngx_http_v3_parse_field_rep(ngx_connection_t *c, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field representation"); + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + ngx_memzero(&st->field, sizeof(ngx_http_v3_parse_field_t)); st->field.base = base; @@ -463,23 +547,23 @@ ngx_http_v3_parse_field_rep(ngx_connection_t *c, switch (st->state) { case sw_field_ri: - rc = ngx_http_v3_parse_field_ri(c, &st->field, ch); + rc = ngx_http_v3_parse_field_ri(c, &st->field, b); break; case sw_field_lri: - rc = ngx_http_v3_parse_field_lri(c, &st->field, ch); + rc = ngx_http_v3_parse_field_lri(c, &st->field, b); break; case sw_field_l: - rc = ngx_http_v3_parse_field_l(c, &st->field, ch); + rc = ngx_http_v3_parse_field_l(c, &st->field, b); break; case sw_field_pbi: - rc = ngx_http_v3_parse_field_pbi(c, &st->field, ch); + rc = ngx_http_v3_parse_field_pbi(c, &st->field, b); break; case sw_field_lpbi: - rc = ngx_http_v3_parse_field_lpbi(c, &st->field, ch); + rc = ngx_http_v3_parse_field_lpbi(c, &st->field, b); break; default: @@ -500,8 +584,9 @@ ngx_http_v3_parse_field_rep(ngx_connection_t *c, static ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, - u_char ch) + ngx_buf_t *b) { + u_char ch; ngx_uint_t n; ngx_http_core_srv_conf_t *cscf; enum { @@ -509,63 +594,70 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, sw_value }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse literal huff:%ui, len:%ui", - st->huffman, st->length); + case sw_start: - n = st->length; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse literal huff:%ui, len:%ui", + st->huffman, st->length); - cscf = ngx_http_v3_get_module_srv_conf(c, ngx_http_core_module); + n = st->length; - if (n > cscf->large_client_header_buffers.size) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent too large field line"); - return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; - } + cscf = ngx_http_v3_get_module_srv_conf(c, ngx_http_core_module); - if (st->huffman) { - n = n * 8 / 5; - st->huffstate = 0; - } + if (n > cscf->large_client_header_buffers.size) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent too large field line"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } - st->last = ngx_pnalloc(c->pool, n + 1); - if (st->last == NULL) { - return NGX_ERROR; - } + if (st->huffman) { + n = n * 8 / 5; + st->huffstate = 0; + } + + st->last = ngx_pnalloc(c->pool, n + 1); + if (st->last == NULL) { + return NGX_ERROR; + } - st->value.data = st->last; - st->state = sw_value; + st->value.data = st->last; + st->state = sw_value; - /* fall through */ + /* fall through */ - case sw_value: + case sw_value: - if (st->huffman) { - if (ngx_http_v2_huff_decode(&st->huffstate, &ch, 1, &st->last, - st->length == 1, c->log) - != NGX_OK) - { - return NGX_ERROR; + if (b->pos == b->last) { + return NGX_AGAIN; } - } else { - *st->last++ = ch; - } + ch = *b->pos++; - if (--st->length) { - break; - } + if (st->huffman) { + if (ngx_http_v2_huff_decode(&st->huffstate, &ch, 1, &st->last, + st->length == 1, c->log) + != NGX_OK) + { + return NGX_ERROR; + } - st->value.len = st->last - st->value.data; - *st->last = '\0'; - goto done; - } + } else { + *st->last++ = ch; + } + + if (--st->length) { + break; + } - return NGX_AGAIN; + st->value.len = st->last - st->value.data; + *st->last = '\0'; + goto done; + } + } done: @@ -579,37 +671,46 @@ done: static ngx_int_t ngx_http_v3_parse_field_ri(ngx_connection_t *c, ngx_http_v3_parse_field_t *st, - u_char ch) + ngx_buf_t *b) { + u_char ch; ngx_int_t rc; enum { sw_start = 0, sw_index }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field ri"); + case sw_start: - st->dynamic = (ch & 0x40) ? 0 : 1; - st->state = sw_index; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field ri"); - /* fall through */ + if (b->pos == b->last) { + return NGX_AGAIN; + } - case sw_index: + ch = *b->pos; - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch); - if (rc != NGX_DONE) { - return rc; - } + st->dynamic = (ch & 0x40) ? 0 : 1; + st->state = sw_index; - st->index = st->pint.value; - goto done; - } + /* fall through */ - return NGX_AGAIN; + case sw_index: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b); + if (rc != NGX_DONE) { + return rc; + } + + st->index = st->pint.value; + goto done; + } + } done: @@ -634,8 +735,9 @@ done: static ngx_int_t ngx_http_v3_parse_field_lri(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch) + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) { + u_char ch; ngx_int_t rc; enum { sw_start = 0, @@ -645,62 +747,76 @@ ngx_http_v3_parse_field_lri(ngx_connection_t *c, sw_value }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field lri"); + case sw_start: - st->dynamic = (ch & 0x10) ? 0 : 1; - st->state = sw_index; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field lri"); - /* fall through */ + if (b->pos == b->last) { + return NGX_AGAIN; + } - case sw_index: + ch = *b->pos; - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, ch); - if (rc != NGX_DONE) { - return rc; - } + st->dynamic = (ch & 0x10) ? 0 : 1; + st->state = sw_index; - st->index = st->pint.value; - st->state = sw_value_len; - break; + /* fall through */ - case sw_value_len: + case sw_index: - st->literal.huffman = (ch & 0x80) ? 1 : 0; - st->state = sw_read_value_len; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, b); + if (rc != NGX_DONE) { + return rc; + } - /* fall through */ + st->index = st->pint.value; + st->state = sw_value_len; + break; - case sw_read_value_len: + case sw_value_len: - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); - if (rc != NGX_DONE) { - return rc; - } + if (b->pos == b->last) { + return NGX_AGAIN; + } - st->literal.length = st->pint.value; - if (st->literal.length == 0) { - goto done; - } + ch = *b->pos; - st->state = sw_value; - break; + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; - case sw_value: + /* fall through */ - rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_read_value_len: - st->value = st->literal.value; - goto done; - } + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } - return NGX_AGAIN; + st->value = st->literal.value; + goto done; + } + } done: @@ -725,8 +841,9 @@ done: static ngx_int_t ngx_http_v3_parse_field_l(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch) + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) { + u_char ch; ngx_int_t rc; enum { sw_start = 0, @@ -737,77 +854,90 @@ ngx_http_v3_parse_field_l(ngx_connection_t *c, sw_value }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field l"); + case sw_start: - st->literal.huffman = (ch & 0x08) ? 1 : 0; - st->state = sw_name_len; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field l"); - /* fall through */ + if (b->pos == b->last) { + return NGX_AGAIN; + } - case sw_name_len: + ch = *b->pos; - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, ch); - if (rc != NGX_DONE) { - return rc; - } + st->literal.huffman = (ch & 0x08) ? 1 : 0; + st->state = sw_name_len; - st->literal.length = st->pint.value; - if (st->literal.length == 0) { - return NGX_ERROR; - } + /* fall through */ - st->state = sw_name; - break; + case sw_name_len: - case sw_name: + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, b); + if (rc != NGX_DONE) { + return rc; + } - rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc != NGX_DONE) { - return rc; - } + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + return NGX_ERROR; + } - st->name = st->literal.value; - st->state = sw_value_len; - break; + st->state = sw_name; + break; - case sw_value_len: + case sw_name: - st->literal.huffman = (ch & 0x80) ? 1 : 0; - st->state = sw_read_value_len; + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } - /* fall through */ + st->name = st->literal.value; + st->state = sw_value_len; + break; - case sw_read_value_len: + case sw_value_len: - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); - if (rc != NGX_DONE) { - return rc; - } + if (b->pos == b->last) { + return NGX_AGAIN; + } - st->literal.length = st->pint.value; - if (st->literal.length == 0) { - goto done; - } + ch = *b->pos; - st->state = sw_value; - break; + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; - case sw_value: + /* fall through */ - rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_read_value_len: - st->value = st->literal.value; - goto done; - } + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } - return NGX_AGAIN; + st->value = st->literal.value; + goto done; + } + } done: @@ -822,7 +952,7 @@ done: static ngx_int_t ngx_http_v3_parse_field_pbi(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch) + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) { ngx_int_t rc; enum { @@ -830,28 +960,30 @@ ngx_http_v3_parse_field_pbi(ngx_connection_t *c, sw_index }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field pbi"); + case sw_start: - st->state = sw_index; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field pbi"); - /* fall through */ + st->state = sw_index; - case sw_index: + /* fall through */ - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_index: - st->index = st->pint.value; - goto done; - } + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, b); + if (rc != NGX_DONE) { + return rc; + } - return NGX_AGAIN; + st->index = st->pint.value; + goto done; + } + } done: @@ -871,8 +1003,9 @@ done: static ngx_int_t ngx_http_v3_parse_field_lpbi(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch) + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) { + u_char ch; ngx_int_t rc; enum { sw_start = 0, @@ -882,62 +1015,69 @@ ngx_http_v3_parse_field_lpbi(ngx_connection_t *c, sw_value }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse field lpbi"); + case sw_start: - st->state = sw_index; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field lpbi"); - /* fall through */ + st->state = sw_index; - case sw_index: + /* fall through */ - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_index: - st->index = st->pint.value; - st->state = sw_value_len; - break; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, b); + if (rc != NGX_DONE) { + return rc; + } - case sw_value_len: + st->index = st->pint.value; + st->state = sw_value_len; + break; - st->literal.huffman = (ch & 0x80) ? 1 : 0; - st->state = sw_read_value_len; + case sw_value_len: - /* fall through */ + if (b->pos == b->last) { + return NGX_AGAIN; + } - case sw_read_value_len: + ch = *b->pos; - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); - if (rc != NGX_DONE) { - return rc; - } + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; - st->literal.length = st->pint.value; - if (st->literal.length == 0) { - goto done; - } + /* fall through */ - st->state = sw_value; - break; + case sw_read_value_len: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } - case sw_value: + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + goto done; + } - rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc != NGX_DONE) { - return rc; - } + st->state = sw_value; + break; - st->value = st->literal.value; - goto done; - } + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } - return NGX_AGAIN; + st->value = st->literal.value; + goto done; + } + } done: @@ -1001,8 +1141,9 @@ ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, static ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, - u_char ch) + ngx_buf_t *b) { + ngx_buf_t loc; ngx_int_t rc; enum { sw_start = 0, @@ -1016,188 +1157,208 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, sw_skip }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse control"); + case sw_start: - st->state = sw_first_type; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse control"); - /* fall through */ + st->state = sw_first_type; - case sw_first_type: - case sw_type: + /* fall through */ - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_first_type: + case sw_type: - st->type = st->vlint.value; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse frame type:%ui", st->type); + st->type = st->vlint.value; - if (st->state == sw_first_type - && st->type != NGX_HTTP_V3_FRAME_SETTINGS) - { - return NGX_HTTP_V3_ERR_MISSING_SETTINGS; - } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse frame type:%ui", st->type); - if (st->state != sw_first_type - && st->type == NGX_HTTP_V3_FRAME_SETTINGS) - { - return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; - } + if (st->state == sw_first_type + && st->type != NGX_HTTP_V3_FRAME_SETTINGS) + { + return NGX_HTTP_V3_ERR_MISSING_SETTINGS; + } - if (ngx_http_v3_is_v2_frame(st->type) - || st->type == NGX_HTTP_V3_FRAME_DATA - || st->type == NGX_HTTP_V3_FRAME_HEADERS - || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) - { - return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; - } + if (st->state != sw_first_type + && st->type == NGX_HTTP_V3_FRAME_SETTINGS) + { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } - st->state = sw_length; - break; + if (ngx_http_v3_is_v2_frame(st->type) + || st->type == NGX_HTTP_V3_FRAME_DATA + || st->type == NGX_HTTP_V3_FRAME_HEADERS + || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) + { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } - case sw_length: + st->state = sw_length; + break; - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_length: - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse frame len:%uL", st->vlint.value); + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } - st->length = st->vlint.value; - if (st->length == 0) { - st->state = sw_type; - break; - } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse frame len:%uL", st->vlint.value); - switch (st->type) { + st->length = st->vlint.value; + if (st->length == 0) { + st->state = sw_type; + break; + } - case NGX_HTTP_V3_FRAME_CANCEL_PUSH: - st->state = sw_cancel_push; - break; + switch (st->type) { - case NGX_HTTP_V3_FRAME_SETTINGS: - st->state = sw_settings; - break; + case NGX_HTTP_V3_FRAME_CANCEL_PUSH: + st->state = sw_cancel_push; + break; - case NGX_HTTP_V3_FRAME_MAX_PUSH_ID: - st->state = sw_max_push_id; - break; + case NGX_HTTP_V3_FRAME_SETTINGS: + st->state = sw_settings; + break; + + case NGX_HTTP_V3_FRAME_MAX_PUSH_ID: + st->state = sw_max_push_id; + break; + + case NGX_HTTP_V3_FRAME_GOAWAY: + st->state = sw_goaway; + break; + + default: + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse skip unknown frame"); + st->state = sw_skip; + } - case NGX_HTTP_V3_FRAME_GOAWAY: - st->state = sw_goaway; break; - default: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse skip unknown frame"); - st->state = sw_skip; - } + case sw_cancel_push: - break; + ngx_http_v3_parse_start_local(b, &loc, st->length); - case sw_cancel_push: + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc); - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + ngx_http_v3_parse_end_local(b, &loc, &st->length); - if (--st->length == 0 && rc == NGX_AGAIN) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } + if (st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } - if (rc != NGX_DONE) { - return rc; - } + if (rc != NGX_DONE) { + return rc; + } - rc = ngx_http_v3_cancel_push(c, st->vlint.value); - if (rc != NGX_OK) { - return rc; - } + rc = ngx_http_v3_cancel_push(c, st->vlint.value); + if (rc != NGX_OK) { + return rc; + } - st->state = sw_type; - break; + st->state = sw_type; + break; - case sw_settings: + case sw_settings: - rc = ngx_http_v3_parse_settings(c, &st->settings, ch); + ngx_http_v3_parse_start_local(b, &loc, st->length); - if (--st->length == 0 && rc == NGX_AGAIN) { - return NGX_HTTP_V3_ERR_SETTINGS_ERROR; - } + rc = ngx_http_v3_parse_settings(c, &st->settings, &loc); - if (rc != NGX_DONE) { - return rc; - } + ngx_http_v3_parse_end_local(b, &loc, &st->length); - if (st->length == 0) { - st->state = sw_type; - } + if (st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_SETTINGS_ERROR; + } - break; + if (rc != NGX_DONE) { + return rc; + } - case sw_max_push_id: + if (st->length == 0) { + st->state = sw_type; + } - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + break; - if (--st->length == 0 && rc == NGX_AGAIN) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } + case sw_max_push_id: - if (rc != NGX_DONE) { - return rc; - } + ngx_http_v3_parse_start_local(b, &loc, st->length); - rc = ngx_http_v3_set_max_push_id(c, st->vlint.value); - if (rc != NGX_OK) { - return rc; - } + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc); - st->state = sw_type; - break; + ngx_http_v3_parse_end_local(b, &loc, &st->length); + + if (st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } - case sw_goaway: + if (rc != NGX_DONE) { + return rc; + } - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); + rc = ngx_http_v3_set_max_push_id(c, st->vlint.value); + if (rc != NGX_OK) { + return rc; + } - if (--st->length == 0 && rc == NGX_AGAIN) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } + st->state = sw_type; + break; - if (rc != NGX_DONE) { - return rc; - } + case sw_goaway: - rc = ngx_http_v3_goaway(c, st->vlint.value); - if (rc != NGX_OK) { - return rc; - } + ngx_http_v3_parse_start_local(b, &loc, st->length); - st->state = sw_type; - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc); - case sw_skip: + ngx_http_v3_parse_end_local(b, &loc, &st->length); + + if (st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } + + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_goaway(c, st->vlint.value); + if (rc != NGX_OK) { + return rc; + } - if (--st->length == 0) { st->state = sw_type; - } + break; - break; - } + case sw_skip: - return NGX_AGAIN; + rc = ngx_http_v3_parse_skip(b, &st->length); + if (rc != NGX_DONE) { + return rc; + } + + st->state = sw_type; + break; + } + } } static ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, - ngx_http_v3_parse_settings_t *st, u_char ch) + ngx_http_v3_parse_settings_t *st, ngx_buf_t *b) { ngx_int_t rc; enum { @@ -1206,42 +1367,44 @@ ngx_http_v3_parse_settings(ngx_connection_t *c, sw_value }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse settings"); + case sw_start: - st->state = sw_id; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse settings"); - /* fall through */ + st->state = sw_id; - case sw_id: + /* fall through */ - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_id: - st->id = st->vlint.value; - st->state = sw_value; - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } - case sw_value: + st->id = st->vlint.value; + st->state = sw_value; + break; - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_value: - if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) { - return NGX_HTTP_V3_ERR_SETTINGS_ERROR; - } + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } - goto done; - } + if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) { + return NGX_HTTP_V3_ERR_SETTINGS_ERROR; + } - return NGX_AGAIN; + goto done; + } + } done: @@ -1254,8 +1417,9 @@ done: static ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, - u_char ch) + ngx_buf_t *b) { + u_char ch; ngx_int_t rc; enum { sw_start = 0, @@ -1265,98 +1429,102 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, sw_duplicate }; - if (st->state == sw_start) { + for ( ;; ) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse encoder instruction"); + if (st->state == sw_start) { - if (ch & 0x80) { - /* Insert With Name Reference */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse encoder instruction"); - st->state = sw_inr; + if (b->pos == b->last) { + return NGX_AGAIN; + } - } else if (ch & 0x40) { - /* Insert With Literal Name */ + ch = *b->pos; - st->state = sw_iln; + if (ch & 0x80) { + /* Insert With Name Reference */ - } else if (ch & 0x20) { - /* Set Dynamic Table Capacity */ + st->state = sw_inr; - st->state = sw_capacity; + } else if (ch & 0x40) { + /* Insert With Literal Name */ - } else { - /* Duplicate */ + st->state = sw_iln; - st->state = sw_duplicate; - } - } + } else if (ch & 0x20) { + /* Set Dynamic Table Capacity */ - switch (st->state) { + st->state = sw_capacity; - case sw_inr: + } else { + /* Duplicate */ - rc = ngx_http_v3_parse_field_inr(c, &st->field, ch); - if (rc != NGX_DONE) { - return rc; + st->state = sw_duplicate; + } } - goto done; - - case sw_iln: + switch (st->state) { - rc = ngx_http_v3_parse_field_iln(c, &st->field, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_inr: - goto done; + rc = ngx_http_v3_parse_field_inr(c, &st->field, b); + if (rc != NGX_DONE) { + return rc; + } - case sw_capacity: + st->state = sw_start; + break; - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_iln: - rc = ngx_http_v3_set_capacity(c, st->pint.value); - if (rc != NGX_OK) { - return rc; - } + rc = ngx_http_v3_parse_field_iln(c, &st->field, b); + if (rc != NGX_DONE) { + return rc; + } - goto done; + st->state = sw_start; + break; - case sw_duplicate: + case sw_capacity: - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch); - if (rc != NGX_DONE) { - return rc; - } + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b); + if (rc != NGX_DONE) { + return rc; + } - rc = ngx_http_v3_duplicate(c, st->pint.value); - if (rc != NGX_OK) { - return rc; - } + rc = ngx_http_v3_set_capacity(c, st->pint.value); + if (rc != NGX_OK) { + return rc; + } - goto done; - } + st->state = sw_start; + break; - return NGX_AGAIN; + default: /* sw_duplicate */ -done: + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b); + if (rc != NGX_DONE) { + return rc; + } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse encoder instruction done"); + rc = ngx_http_v3_duplicate(c, st->pint.value); + if (rc != NGX_OK) { + return rc; + } - st->state = sw_start; - return NGX_AGAIN; + st->state = sw_start; + break; + } + } } static ngx_int_t ngx_http_v3_parse_field_inr(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch) + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) { + u_char ch; ngx_int_t rc; enum { sw_start = 0, @@ -1366,63 +1534,77 @@ ngx_http_v3_parse_field_inr(ngx_connection_t *c, sw_value }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field inr"); + case sw_start: - st->dynamic = (ch & 0x40) ? 0 : 1; - st->state = sw_name_index; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field inr"); - /* fall through */ + if (b->pos == b->last) { + return NGX_AGAIN; + } - case sw_name_index: + ch = *b->pos; - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch); - if (rc != NGX_DONE) { - return rc; - } + st->dynamic = (ch & 0x40) ? 0 : 1; + st->state = sw_name_index; - st->index = st->pint.value; - st->state = sw_value_len; - break; + /* fall through */ - case sw_value_len: + case sw_name_index: - st->literal.huffman = (ch & 0x80) ? 1 : 0; - st->state = sw_read_value_len; + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b); + if (rc != NGX_DONE) { + return rc; + } - /* fall through */ + st->index = st->pint.value; + st->state = sw_value_len; + break; - case sw_read_value_len: + case sw_value_len: - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); - if (rc != NGX_DONE) { - return rc; - } + if (b->pos == b->last) { + return NGX_AGAIN; + } - st->literal.length = st->pint.value; - if (st->literal.length == 0) { - st->value.len = 0; - goto done; - } + ch = *b->pos; - st->state = sw_value; - break; + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; - case sw_value: + /* fall through */ - rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_read_value_len: - st->value = st->literal.value; - goto done; - } + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + st->value.len = 0; + goto done; + } - return NGX_AGAIN; + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } + + st->value = st->literal.value; + goto done; + } + } done: @@ -1443,8 +1625,9 @@ done: static ngx_int_t ngx_http_v3_parse_field_iln(ngx_connection_t *c, - ngx_http_v3_parse_field_t *st, u_char ch) + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) { + u_char ch; ngx_int_t rc; enum { sw_start = 0, @@ -1455,79 +1638,92 @@ ngx_http_v3_parse_field_iln(ngx_connection_t *c, sw_value }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse field iln"); + case sw_start: - st->literal.huffman = (ch & 0x20) ? 1 : 0; - st->state = sw_name_len; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field iln"); - /* fall through */ + if (b->pos == b->last) { + return NGX_AGAIN; + } - case sw_name_len: + ch = *b->pos; - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch); - if (rc != NGX_DONE) { - return rc; - } + st->literal.huffman = (ch & 0x20) ? 1 : 0; + st->state = sw_name_len; - st->literal.length = st->pint.value; - if (st->literal.length == 0) { - return NGX_ERROR; - } + /* fall through */ - st->state = sw_name; - break; + case sw_name_len: - case sw_name: + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b); + if (rc != NGX_DONE) { + return rc; + } - rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc != NGX_DONE) { - return rc; - } + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + return NGX_ERROR; + } - st->name = st->literal.value; - st->state = sw_value_len; - break; + st->state = sw_name; + break; - case sw_value_len: + case sw_name: - st->literal.huffman = (ch & 0x80) ? 1 : 0; - st->state = sw_read_value_len; + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } - /* fall through */ + st->name = st->literal.value; + st->state = sw_value_len; + break; - case sw_read_value_len: + case sw_value_len: - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); - if (rc != NGX_DONE) { - return rc; - } + if (b->pos == b->last) { + return NGX_AGAIN; + } - st->literal.length = st->pint.value; - if (st->literal.length == 0) { - st->value.len = 0; - goto done; - } + ch = *b->pos; - st->state = sw_value; - break; + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; - case sw_value: + /* fall through */ - rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_read_value_len: - st->value = st->literal.value; - goto done; - } + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + st->value.len = 0; + goto done; + } + + st->state = sw_value; + break; + + case sw_value: - return NGX_AGAIN; + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } + + st->value = st->literal.value; + goto done; + } + } done: @@ -1547,8 +1743,9 @@ done: static ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, - u_char ch) + ngx_buf_t *b) { + u_char ch; ngx_int_t rc; enum { sw_start = 0, @@ -1557,88 +1754,90 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, sw_inc_insert_count }; - if (st->state == sw_start) { - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse decoder instruction"); + for ( ;; ) { - if (ch & 0x80) { - /* Section Acknowledgment */ + if (st->state == sw_start) { - st->state = sw_ack_section; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse decoder instruction"); - } else if (ch & 0x40) { - /* Stream Cancellation */ + if (b->pos == b->last) { + return NGX_AGAIN; + } - st->state = sw_cancel_stream; + ch = *b->pos; - } else { - /* Insert Count Increment */ + if (ch & 0x80) { + /* Section Acknowledgment */ - st->state = sw_inc_insert_count; - } - } + st->state = sw_ack_section; - switch (st->state) { + } else if (ch & 0x40) { + /* Stream Cancellation */ - case sw_ack_section: + st->state = sw_cancel_stream; - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch); - if (rc != NGX_DONE) { - return rc; - } + } else { + /* Insert Count Increment */ - rc = ngx_http_v3_ack_section(c, st->pint.value); - if (rc != NGX_OK) { - return rc; + st->state = sw_inc_insert_count; + } } - goto done; + switch (st->state) { - case sw_cancel_stream: + case sw_ack_section: - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch); - if (rc != NGX_DONE) { - return rc; - } + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } - rc = ngx_http_v3_cancel_stream(c, st->pint.value); - if (rc != NGX_OK) { - return rc; - } + rc = ngx_http_v3_ack_section(c, st->pint.value); + if (rc != NGX_OK) { + return rc; + } - goto done; + st->state = sw_start; + break; - case sw_inc_insert_count: + case sw_cancel_stream: - rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch); - if (rc != NGX_DONE) { - return rc; - } + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b); + if (rc != NGX_DONE) { + return rc; + } - rc = ngx_http_v3_inc_insert_count(c, st->pint.value); - if (rc != NGX_OK) { - return rc; - } + rc = ngx_http_v3_cancel_stream(c, st->pint.value); + if (rc != NGX_OK) { + return rc; + } - goto done; - } + st->state = sw_start; + break; - return NGX_AGAIN; + case sw_inc_insert_count: -done: + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b); + if (rc != NGX_DONE) { + return rc; + } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse decoder instruction done"); + rc = ngx_http_v3_inc_insert_count(c, st->pint.value); + if (rc != NGX_OK) { + return rc; + } - st->state = sw_start; - return NGX_AGAIN; + st->state = sw_start; + break; + } + } } ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, - u_char ch) + ngx_buf_t *b) { ngx_int_t rc; enum { @@ -1648,75 +1847,78 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, sw_skip }; - switch (st->state) { + for ( ;; ) { - case sw_start: + switch (st->state) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data"); + case sw_start: - st->state = sw_type; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data"); - /* fall through */ + st->state = sw_type; - case sw_type: + /* fall through */ - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_type: - st->type = st->vlint.value; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } - if (st->type == NGX_HTTP_V3_FRAME_HEADERS) { - /* trailers */ - goto done; - } + st->type = st->vlint.value; - if (ngx_http_v3_is_v2_frame(st->type) - || st->type == NGX_HTTP_V3_FRAME_GOAWAY - || st->type == NGX_HTTP_V3_FRAME_SETTINGS - || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID - || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH - || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) - { - return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; - } + if (st->type == NGX_HTTP_V3_FRAME_HEADERS) { + /* trailers */ + goto done; + } - st->state = sw_length; - break; + if (ngx_http_v3_is_v2_frame(st->type) + || st->type == NGX_HTTP_V3_FRAME_GOAWAY + || st->type == NGX_HTTP_V3_FRAME_SETTINGS + || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID + || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH + || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) + { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } - case sw_length: + st->state = sw_length; + break; - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); - if (rc != NGX_DONE) { - return rc; - } + case sw_length: - st->length = st->vlint.value; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse data type:%ui, len:%ui", - st->type, st->length); + st->length = st->vlint.value; - if (st->type != NGX_HTTP_V3_FRAME_DATA && st->length > 0) { - st->state = sw_skip; - break; - } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse data type:%ui, len:%ui", + st->type, st->length); - st->state = sw_type; - return NGX_OK; + if (st->type != NGX_HTTP_V3_FRAME_DATA && st->length > 0) { + st->state = sw_skip; + break; + } + + st->state = sw_type; + return NGX_OK; + + case sw_skip: - case sw_skip: + rc = ngx_http_v3_parse_skip(b, &st->length); + if (rc != NGX_DONE) { + return rc; + } - if (--st->length == 0) { st->state = sw_type; + break; } - - break; } - return NGX_AGAIN; - done: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data done"); @@ -1728,7 +1930,7 @@ done: ngx_int_t ngx_http_v3_parse_uni(ngx_connection_t *c, ngx_http_v3_parse_uni_t *st, - u_char ch) + ngx_buf_t *b) { ngx_int_t rc; enum { @@ -1740,76 +1942,64 @@ ngx_http_v3_parse_uni(ngx_connection_t *c, ngx_http_v3_parse_uni_t *st, sw_unknown }; - switch (st->state) { - case sw_start: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse uni"); + for ( ;; ) { - st->state = sw_type; + switch (st->state) { + case sw_start: - /* fall through */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse uni"); - case sw_type: + st->state = sw_type; - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch); - if (rc != NGX_DONE) { - return rc; - } + /* fall through */ - rc = ngx_http_v3_register_uni_stream(c, st->vlint.value); - if (rc != NGX_OK) { - return rc; - } + case sw_type: - switch (st->vlint.value) { - case NGX_HTTP_V3_STREAM_CONTROL: - st->state = sw_control; - break; + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } - case NGX_HTTP_V3_STREAM_ENCODER: - st->state = sw_encoder; - break; + rc = ngx_http_v3_register_uni_stream(c, st->vlint.value); + if (rc != NGX_OK) { + return rc; + } - case NGX_HTTP_V3_STREAM_DECODER: - st->state = sw_decoder; - break; + switch (st->vlint.value) { + case NGX_HTTP_V3_STREAM_CONTROL: + st->state = sw_control; + break; - default: - st->state = sw_unknown; - } + case NGX_HTTP_V3_STREAM_ENCODER: + st->state = sw_encoder; + break; - break; + case NGX_HTTP_V3_STREAM_DECODER: + st->state = sw_decoder; + break; - case sw_control: + default: + st->state = sw_unknown; + } - rc = ngx_http_v3_parse_control(c, &st->u.control, ch); - if (rc != NGX_OK) { - return rc; - } + break; - break; + case sw_control: - case sw_encoder: + return ngx_http_v3_parse_control(c, &st->u.control, b); - rc = ngx_http_v3_parse_encoder(c, &st->u.encoder, ch); - if (rc != NGX_OK) { - return rc; - } + case sw_encoder: - break; + return ngx_http_v3_parse_encoder(c, &st->u.encoder, b); - case sw_decoder: + case sw_decoder: - rc = ngx_http_v3_parse_decoder(c, &st->u.decoder, ch); - if (rc != NGX_OK) { - return rc; - } + return ngx_http_v3_parse_decoder(c, &st->u.decoder, b); - break; + case sw_unknown: - case sw_unknown: - break; + b->pos = b->last; + return NGX_AGAIN; + } } - - return NGX_AGAIN; } diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h index 5ca3a410f..ba004db5d 100644 --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -136,11 +136,11 @@ typedef struct { */ ngx_int_t ngx_http_v3_parse_headers(ngx_connection_t *c, - ngx_http_v3_parse_headers_t *st, u_char ch); + ngx_http_v3_parse_headers_t *st, ngx_buf_t *b); ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c, - ngx_http_v3_parse_data_t *st, u_char ch); + ngx_http_v3_parse_data_t *st, ngx_buf_t *b); ngx_int_t ngx_http_v3_parse_uni(ngx_connection_t *c, - ngx_http_v3_parse_uni_t *st, u_char ch); + ngx_http_v3_parse_uni_t *st, ngx_buf_t *b); #endif /* _NGX_HTTP_V3_PARSE_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 0bd585317..fb9ea8ff1 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -207,6 +207,7 @@ ngx_http_v3_cleanup_request(void *data) static void ngx_http_v3_process_request(ngx_event_t *rev) { + u_char *p; ssize_t n; ngx_buf_t *b; ngx_int_t rc; @@ -273,7 +274,9 @@ ngx_http_v3_process_request(ngx_event_t *rev) b->last = b->start + n; } - rc = ngx_http_v3_parse_headers(c, st, *b->pos); + p = b->pos; + + rc = ngx_http_v3_parse_headers(c, st, b); if (rc > 0) { ngx_http_v3_finalize_connection(c, rc, @@ -302,8 +305,7 @@ ngx_http_v3_process_request(ngx_event_t *rev) break; } - b->pos++; - r->request_length++; + r->request_length += b->pos - p; if (rc == NGX_AGAIN) { continue; @@ -1024,6 +1026,7 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { off_t max; size_t size; + u_char *p; ngx_int_t rc; ngx_buf_t *b; ngx_uint_t last; @@ -1078,9 +1081,11 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) while (cl->buf->pos < cl->buf->last) { if (st->length == 0) { - r->request_length++; + p = cl->buf->pos; + + rc = ngx_http_v3_parse_data(r->connection, st, cl->buf); - rc = ngx_http_v3_parse_data(r->connection, st, *cl->buf->pos++); + r->request_length += cl->buf->pos - p; if (rc == NGX_AGAIN) { continue; diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 1b0f91454..1cff29319 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -168,7 +168,8 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) { u_char buf[128]; ssize_t n; - ngx_int_t rc, i; + ngx_buf_t b; + ngx_int_t rc; ngx_connection_t *c; ngx_http_v3_uni_stream_t *us; @@ -177,6 +178,8 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler"); + ngx_memzero(&b, sizeof(ngx_buf_t)); + while (rev->ready) { n = c->recv(c, buf, sizeof(buf)); @@ -201,25 +204,25 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) break; } - for (i = 0; i < n; i++) { + b.pos = buf; + b.last = buf + n; - rc = ngx_http_v3_parse_uni(c, &us->parse, buf[i]); + rc = ngx_http_v3_parse_uni(c, &us->parse, &b); - if (rc == NGX_DONE) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 read done"); - ngx_http_v3_close_uni_stream(c); - return; - } + if (rc == NGX_DONE) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 read done"); + ngx_http_v3_close_uni_stream(c); + return; + } - if (rc > 0) { - goto failed; - } + if (rc > 0) { + goto failed; + } - if (rc != NGX_AGAIN) { - rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; - goto failed; - } + if (rc != NGX_AGAIN) { + rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; + goto failed; } } -- cgit From 590996466ca568dbc7de5d40b7062dc254d211c2 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 9 Sep 2021 15:47:29 +0300 Subject: HTTP/3: reading body buffering in filters. This change follows similar changes in HTTP/1 and HTTP/2 in 9cf043a5d9ca. --- src/http/v3/ngx_http_v3_request.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index fb9ea8ff1..f11c32da9 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -822,7 +822,7 @@ ngx_http_v3_read_request_body(ngx_http_request_t *r) return rc; } - if (rb->rest == 0) { + if (rb->rest == 0 && rb->last_saved) { /* the whole request body was pre-read */ r->request_body_no_buffering = 0; rb->post_handler(r); @@ -895,6 +895,7 @@ ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r) size_t size; ssize_t n; ngx_int_t rc; + ngx_uint_t flush; ngx_chain_t out; ngx_connection_t *c; ngx_http_request_body_t *rb; @@ -902,12 +903,17 @@ ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r) c = r->connection; rb = r->request_body; + flush = 1; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read client request body"); for ( ;; ) { for ( ;; ) { + if (rb->rest == 0) { + break; + } + if (rb->buf->last == rb->buf->end) { /* update chains */ @@ -931,12 +937,25 @@ ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r) return NGX_AGAIN; } + if (rb->filter_need_buffering) { + clcf = ngx_http_get_module_loc_conf(r, + ngx_http_core_module); + ngx_add_timer(c->read, clcf->client_body_timeout); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_AGAIN; + } + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "busy buffers after request body flush"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } + flush = 0; rb->buf->pos = rb->buf->start; rb->buf->last = rb->buf->start; } @@ -948,6 +967,10 @@ ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r) size = (size_t) rest; } + if (size == 0) { + break; + } + n = c->recv(c, rb->buf->last, size); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -970,6 +993,7 @@ ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r) /* pass buffer to request body filter chain */ + flush = 0; out.buf = rb->buf; out.next = NULL; @@ -991,11 +1015,19 @@ ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client request body rest %O", rb->rest); - if (rb->rest == 0) { + if (flush) { + rc = ngx_http_v3_request_body_filter(r, NULL); + + if (rc != NGX_OK) { + return rc; + } + } + + if (rb->rest == 0 && rb->last_saved) { break; } - if (!c->read->ready) { + if (!c->read->ready || rb->rest == 0) { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_add_timer(c->read, clcf->client_body_timeout); -- cgit From 0ac1f6fd47b59bd59abb4c8465e622e60f666ee0 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 13 Sep 2021 16:25:08 +0300 Subject: HTTP/3: implemented QPACK Huffman encoding for response fields. --- src/http/v3/ngx_http_v3_encode.c | 87 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_encode.c b/src/http/v3/ngx_http_v3_encode.c index 2740ccffa..a62f5d30f 100644 --- a/src/http/v3/ngx_http_v3_encode.c +++ b/src/http/v3/ngx_http_v3_encode.c @@ -137,6 +137,9 @@ uintptr_t ngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, u_char *data, size_t len) { + size_t hlen; + u_char *p1, *p2; + /* Literal Field Line With Name Reference */ if (p == NULL) { @@ -148,11 +151,28 @@ ngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, *p = dynamic ? 0x40 : 0x50; p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 4); + p1 = p; *p = 0; p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7); if (data) { - p = ngx_cpymem(p, data, len); + p2 = p; + hlen = ngx_http_v2_huff_encode(data, len, p, 0); + + if (hlen) { + p = p1; + *p = 0x80; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 7); + + if (p != p2) { + ngx_memmove(p, p2, hlen); + } + + p += hlen; + + } else { + p = ngx_cpymem(p, data, len); + } } return (uintptr_t) p; @@ -162,6 +182,9 @@ ngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, uintptr_t ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, ngx_str_t *value) { + size_t hlen; + u_char *p1, *p2; + /* Literal Field Line With Literal Name */ if (p == NULL) { @@ -171,16 +194,50 @@ ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, ngx_str_t *value) + value->len; } + p1 = p; *p = 0x20; p = (u_char *) ngx_http_v3_encode_prefix_int(p, name->len, 3); - ngx_strlow(p, name->data, name->len); - p += name->len; + p2 = p; + hlen = ngx_http_v2_huff_encode(name->data, name->len, p, 1); + + if (hlen) { + p = p1; + *p = 0x28; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 3); + + if (p != p2) { + ngx_memmove(p, p2, hlen); + } + + p += hlen; + + } else { + ngx_strlow(p, name->data, name->len); + p += name->len; + } + p1 = p; *p = 0; p = (u_char *) ngx_http_v3_encode_prefix_int(p, value->len, 7); - p = ngx_cpymem(p, value->data, value->len); + p2 = p; + hlen = ngx_http_v2_huff_encode(value->data, value->len, p, 0); + + if (hlen) { + p = p1; + *p = 0x80; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 7); + + if (p != p2) { + ngx_memmove(p, p2, hlen); + } + + p += hlen; + + } else { + p = ngx_cpymem(p, value->data, value->len); + } return (uintptr_t) p; } @@ -205,6 +262,9 @@ uintptr_t ngx_http_v3_encode_field_lpbi(u_char *p, ngx_uint_t index, u_char *data, size_t len) { + size_t hlen; + u_char *p1, *p2; + /* Literal Field Line With Post-Base Name Reference */ if (p == NULL) { @@ -216,11 +276,28 @@ ngx_http_v3_encode_field_lpbi(u_char *p, ngx_uint_t index, u_char *data, *p = 0; p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 3); + p1 = p; *p = 0; p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7); if (data) { - p = ngx_cpymem(p, data, len); + p2 = p; + hlen = ngx_http_v2_huff_encode(data, len, p, 0); + + if (hlen) { + p = p1; + *p = 0x80; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 7); + + if (p != p2) { + ngx_memmove(p, p2, hlen); + } + + p += hlen; + + } else { + p = ngx_cpymem(p, data, len); + } } return (uintptr_t) p; -- cgit From ee5d9279288beec74e563c5fd3e1d8dd0171fb70 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 13 Sep 2021 16:25:23 +0300 Subject: HTTP/3: Huffman encoding for the Content-Type response field. --- src/http/v3/ngx_http_v3_filter_module.c | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 71aa839e1..45063524b 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -359,35 +359,35 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) } if (r->headers_out.content_type.len) { - n = r->headers_out.content_type.len; - if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { - n += sizeof("; charset=") - 1 + r->headers_out.charset.len; - } + n = r->headers_out.content_type.len + sizeof("; charset=") - 1 + + r->headers_out.charset.len; - b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, - NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, - NULL, n); + p = ngx_pnalloc(r->pool, n); + if (p == NULL) { + return NGX_ERROR; + } - p = b->last; - b->last = ngx_cpymem(b->last, r->headers_out.content_type.data, - r->headers_out.content_type.len); + p = ngx_cpymem(p, r->headers_out.content_type.data, + r->headers_out.content_type.len); - if (r->headers_out.content_type_len == r->headers_out.content_type.len - && r->headers_out.charset.len) - { - b->last = ngx_cpymem(b->last, "; charset=", - sizeof("; charset=") - 1); - b->last = ngx_cpymem(b->last, r->headers_out.charset.data, - r->headers_out.charset.len); + p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1); - /* update r->headers_out.content_type for possible logging */ + p = ngx_cpymem(p, r->headers_out.charset.data, + r->headers_out.charset.len); - r->headers_out.content_type.len = b->last - p; - r->headers_out.content_type.data = p; + /* updated r->headers_out.content_type is also needed for logging */ + + r->headers_out.content_type.len = n; + r->headers_out.content_type.data = p - n; } + + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, + r->headers_out.content_type.data, + r->headers_out.content_type.len); } if (r->headers_out.content_length == NULL) { -- cgit From 12cf623bc2f77ffd0a0425c8f1d07cf912160ef6 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 13 Sep 2021 16:25:31 +0300 Subject: HTTP/3: Huffman encoding for the Last-Modified response field. --- src/http/v3/ngx_http_v3_filter_module.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 45063524b..0fcbed01f 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -411,11 +411,18 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { - b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, - NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, - sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); + n = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1; - b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); + p = ngx_pnalloc(r->pool, n); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_http_time(p, r->headers_out.last_modified_time); + + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_LAST_MODIFIED, + p, n); } if (host.data) { -- cgit From bacd7ef0bed22b1b447a0de765fbd13721e80afb Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 13 Sep 2021 16:25:32 +0300 Subject: HTTP/3: Huffman encoding for the Location response field. --- src/http/v3/ngx_http_v3_filter_module.c | 114 +++++++++++++++----------------- 1 file changed, 55 insertions(+), 59 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 0fcbed01f..0ab31b5d6 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -95,7 +95,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) u_char *p; size_t len, n; ngx_buf_t *b; - ngx_str_t host; + ngx_str_t host, location; ngx_uint_t i, port; ngx_chain_t *out, *hl, *cl, **ll; ngx_list_part_t *part; @@ -229,51 +229,70 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); } - if (r->headers_out.location - && r->headers_out.location->value.len - && r->headers_out.location->value.data[0] == '/' - && clcf->absolute_redirect) - { - r->headers_out.location->hash = 0; + if (r->headers_out.location && r->headers_out.location->value.len) { - if (clcf->server_name_in_redirect) { - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - host = cscf->server_name; + if (r->headers_out.location->value.data[0] == '/' + && clcf->absolute_redirect) + { + if (clcf->server_name_in_redirect) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + host = cscf->server_name; - } else if (r->headers_in.server.len) { - host = r->headers_in.server; + } else if (r->headers_in.server.len) { + host = r->headers_in.server; - } else { - host.len = NGX_SOCKADDR_STRLEN; - host.data = addr; + } else { + host.len = NGX_SOCKADDR_STRLEN; + host.data = addr; + + if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + port = ngx_inet_get_port(c->local_sockaddr); - if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { + location.len = sizeof("https://") - 1 + host.len + + r->headers_out.location->value.len; + + if (clcf->port_in_redirect) { + port = (port == 443) ? 0 : port; + + } else { + port = 0; + } + + if (port) { + location.len += sizeof(":65535") - 1; + } + + location.data = ngx_pnalloc(r->pool, location.len); + if (location.data == NULL) { return NGX_ERROR; } - } - port = ngx_inet_get_port(c->local_sockaddr); + p = ngx_cpymem(location.data, "https://", sizeof("https://") - 1); + p = ngx_cpymem(p, host.data, host.len); - n = sizeof("https://") - 1 + host.len - + r->headers_out.location->value.len; + if (port) { + p = ngx_sprintf(p, ":%ui", port); + } - if (clcf->port_in_redirect) { - port = (port == 443) ? 0 : port; + p = ngx_cpymem(p, r->headers_out.location->value.data, + r->headers_out.location->value.len); - } else { - port = 0; - } + /* update r->headers_out.location->value for possible logging */ - if (port) { - n += sizeof(":65535") - 1; + r->headers_out.location->value.len = p - location.data; + r->headers_out.location->value.data = location.data; + ngx_str_set(&r->headers_out.location->key, "Location"); } - len += ngx_http_v3_encode_field_lri(NULL, 0, - NGX_HTTP_V3_HEADER_LOCATION, NULL, n); + r->headers_out.location->hash = 0; - } else { - ngx_str_null(&host); - port = 0; + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_LOCATION, NULL, + r->headers_out.location->value.len); } #if (NGX_HTTP_GZIP) @@ -425,34 +444,11 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) p, n); } - if (host.data) { - n = sizeof("https://") - 1 + host.len - + r->headers_out.location->value.len; - - if (port) { - n += ngx_sprintf(b->last, ":%ui", port) - b->last; - } - + if (r->headers_out.location && r->headers_out.location->value.len) { b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, - NGX_HTTP_V3_HEADER_LOCATION, - NULL, n); - - p = b->last; - b->last = ngx_cpymem(b->last, "https://", sizeof("https://") - 1); - b->last = ngx_cpymem(b->last, host.data, host.len); - - if (port) { - b->last = ngx_sprintf(b->last, ":%ui", port); - } - - b->last = ngx_cpymem(b->last, r->headers_out.location->value.data, - r->headers_out.location->value.len); - - /* update r->headers_out.location->value for possible logging */ - - r->headers_out.location->value.len = b->last - p; - r->headers_out.location->value.data = p; - ngx_str_set(&r->headers_out.location->key, "Location"); + NGX_HTTP_V3_HEADER_LOCATION, + r->headers_out.location->value.data, + r->headers_out.location->value.len); } #if (NGX_HTTP_GZIP) -- cgit From 10cafa75a4c3ccc1df52e55225c28c6b4110b229 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 13 Sep 2021 16:25:37 +0300 Subject: HTTP/3: added debug logging of response fields. Because of QPACK compression it's hard to see what fields are actually sent by the server. --- src/http/v3/ngx_http_v3_filter_module.c | 41 ++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 0ab31b5d6..764d97957 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -340,6 +340,10 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last, 0, 0, 0); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \":status: %03ui\"", + r->headers_out.status); + if (r->headers_out.status == NGX_HTTP_OK) { b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, NGX_HTTP_V3_HEADER_STATUS_200); @@ -365,12 +369,19 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) n = sizeof("nginx") - 1; } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"server: %*s\"", n, p); + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_SERVER, p, n); } if (r->headers_out.date == NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"date: %V\"", + &ngx_cached_http_time); + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_DATE, ngx_cached_http_time.data, @@ -403,13 +414,23 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) r->headers_out.content_type.data = p - n; } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"content-type: %V\"", + &r->headers_out.content_type); + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, r->headers_out.content_type.data, r->headers_out.content_type.len); } - if (r->headers_out.content_length == NULL) { + if (r->headers_out.content_length == NULL + && r->headers_out.content_length_n >= 0) + { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"content-length: %O\"", + r->headers_out.content_length_n); + if (r->headers_out.content_length_n > 0) { p = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); n = p - b->last; @@ -421,7 +442,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) b->last = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); - } else if (r->headers_out.content_length_n == 0) { + } else { b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); } @@ -439,12 +460,19 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) ngx_http_time(p, r->headers_out.last_modified_time); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"last-modified: %*s\"", n, p); + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_LAST_MODIFIED, p, n); } if (r->headers_out.location && r->headers_out.location->value.len) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"location: %V\"", + &r->headers_out.location->value); + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, NGX_HTTP_V3_HEADER_LOCATION, r->headers_out.location->value.data, @@ -453,6 +481,9 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) #if (NGX_HTTP_GZIP) if (r->gzip_vary) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"vary: Accept-Encoding\""); + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); } @@ -477,6 +508,10 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) continue; } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"%V: %V\"", + &header[i].key, &header[i].value); + b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, &header[i].key, &header[i].value); @@ -1417,7 +1452,7 @@ ngx_http_v3_create_trailers(ngx_http_request_t *r, } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 trailer: \"%V: %V\"", + "http3 output trailer: \"%V: %V\"", &header[i].key, &header[i].value); b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, -- cgit From bd89c448b7e7beb15409e2abe2f174a36a7a0823 Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Tue, 14 Sep 2021 12:09:13 +0300 Subject: Removed NGX_OPENSSL_QUIC macro, NGX_QUIC is enough. --- src/http/ngx_http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index e1604af60..c9aa3761b 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1372,7 +1372,7 @@ ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #endif -#if (NGX_HTTP_QUIC && !defined NGX_OPENSSL_QUIC) +#if (NGX_HTTP_QUIC && !defined NGX_QUIC) if (lsopt->quic) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, -- cgit From 9d7f2e79176b3fc73c06e8ba1594f287b4536bbe Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 16 Sep 2021 13:13:22 +0300 Subject: HTTP/3: added CONNECT and TRACE methods rejection. It has got lost in e1eb7f4ca9f1, let alone a subsequent update in 63c66b7cc07c. --- src/http/v3/ngx_http_v3_request.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index f11c32da9..793a34816 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -45,7 +45,8 @@ static const struct { { ngx_string("LOCK"), NGX_HTTP_LOCK }, { ngx_string("UNLOCK"), NGX_HTTP_UNLOCK }, { ngx_string("PATCH"), NGX_HTTP_PATCH }, - { ngx_string("TRACE"), NGX_HTTP_TRACE } + { ngx_string("TRACE"), NGX_HTTP_TRACE }, + { ngx_string("CONNECT"), NGX_HTTP_CONNECT } }; @@ -780,6 +781,18 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r) } } + if (r->method == NGX_HTTP_CONNECT) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent CONNECT method"); + ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); + return NGX_ERROR; + } + + if (r->method == NGX_HTTP_TRACE) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent TRACE method"); + ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); + return NGX_ERROR; + } + return NGX_OK; failed: -- cgit From 0f3eb180d2af781c98b84c1e5e2b4fe4c0c3be54 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 17 Sep 2021 16:32:23 +0300 Subject: HTTP/3: make ngx_http_log_error() static again. This function was only referenced from ngx_http_v3_create_push_request() to initialize push connection log. Now the log handler is copied from the parent request connection. The change reduces diff to the default branch. --- src/http/ngx_http.h | 1 - src/http/ngx_http_request.c | 3 ++- src/http/v3/ngx_http_v3_filter_module.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index fb4157715..708defebc 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -92,7 +92,6 @@ ngx_int_t ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, void ngx_http_init_connection(ngx_connection_t *c); void ngx_http_close_connection(ngx_connection_t *c); -u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len); #if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME) int ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 30200075e..2b838cfc3 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -50,6 +50,7 @@ static void ngx_http_lingering_close_handler(ngx_event_t *ev); static ngx_int_t ngx_http_post_action(ngx_http_request_t *r); static void ngx_http_log_request(ngx_http_request_t *r); +static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len); static u_char *ngx_http_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr, u_char *buf, size_t len); @@ -3829,7 +3830,7 @@ ngx_http_close_connection(ngx_connection_t *c) } -u_char * +static u_char * ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len) { u_char *p; diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 764d97957..5af17e40a 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -917,7 +917,7 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, ctx->request = NULL; ctx->current_request = NULL; - c->log->handler = ngx_http_log_error; + c->log->handler = pc->log->handler; c->log->data = ctx; c->log->action = "processing pushed request headers"; -- cgit From 3ae914c837b2ad4217165007c980d31161d6db9f Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 17 Sep 2021 15:28:31 +0300 Subject: HTTP/3: fixed pushed request finalization in case of error. Previously request could be finalized twice. For example, this could happen if "Host" header was invalid. --- src/http/v3/ngx_http_v3_filter_module.c | 55 ++++++++++++++------------------- 1 file changed, 23 insertions(+), 32 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 5af17e40a..34b2991ad 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -880,7 +880,6 @@ static ngx_int_t ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, uint64_t push_id) { - ngx_pool_t *pool; ngx_connection_t *c, *pc; ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; @@ -889,8 +888,6 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, pc = pr->connection; - r = NULL; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "http3 create push request id:%uL", push_id); @@ -901,7 +898,8 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, hc = ngx_palloc(c->pool, sizeof(ngx_http_connection_t)); if (hc == NULL) { - goto failed; + ngx_http_close_connection(c); + return NGX_ERROR; } phc = ngx_http_quic_get_connection(pc); @@ -910,7 +908,8 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); if (ctx == NULL) { - goto failed; + ngx_http_close_connection(c); + return NGX_ERROR; } ctx->connection = c; @@ -925,7 +924,8 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, r = ngx_http_create_request(c); if (r == NULL) { - goto failed; + ngx_http_close_connection(c); + return NGX_ERROR; } c->data = r; @@ -941,44 +941,49 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, r->header_in = ngx_create_temp_buf(r->pool, cscf->client_header_buffer_size); if (r->header_in == NULL) { - goto failed; + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; } if (ngx_list_init(&r->headers_in.headers, r->pool, 4, sizeof(ngx_table_elt_t)) != NGX_OK) { - goto failed; + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; } r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; r->schema.data = ngx_pstrdup(r->pool, &pr->schema); if (r->schema.data == NULL) { - goto failed; + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; } r->schema.len = pr->schema.len; r->uri_start = ngx_pstrdup(r->pool, path); if (r->uri_start == NULL) { - goto failed; + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; } r->uri_end = r->uri_start + path->len; if (ngx_http_parse_uri(r) != NGX_OK) { - goto failed; + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; } if (ngx_http_process_request_uri(r) != NGX_OK) { - goto failed; + return NGX_ERROR; } if (ngx_http_v3_set_push_header(r, "host", &pr->headers_in.server) != NGX_OK) { - goto failed; + return NGX_ERROR; } if (pr->headers_in.accept_encoding) { @@ -986,7 +991,7 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, &pr->headers_in.accept_encoding->value) != NGX_OK) { - goto failed; + return NGX_ERROR; } } @@ -995,7 +1000,7 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, &pr->headers_in.accept_language->value) != NGX_OK) { - goto failed; + return NGX_ERROR; } } @@ -1004,7 +1009,7 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, &pr->headers_in.user_agent->value) != NGX_OK) { - goto failed; + return NGX_ERROR; } } @@ -1014,22 +1019,6 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, ngx_post_event(c->read, &ngx_posted_events); return NGX_OK; - -failed: - - if (r) { - ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - c->destroyed = 1; - - pool = c->pool; - - ngx_close_connection(c); - - ngx_destroy_pool(pool); - - return NGX_ERROR; } @@ -1049,6 +1038,7 @@ ngx_http_v3_set_push_header(ngx_http_request_t *r, const char *name, p = ngx_pnalloc(r->pool, value->len + 1); if (p == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -1057,6 +1047,7 @@ ngx_http_v3_set_push_header(ngx_http_request_t *r, const char *name, h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } -- cgit From 08dcf62f5b8ee49927dc38bae705b8fa777799e4 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 22 Sep 2021 14:08:21 +0300 Subject: HTTP/3: fixed ngx_stat_active counter. Previously the counter was not incremented for HTTP/3 streams, but still decremented in ngx_http_close_connection(). There are two solutions here, one is to increment the counter for HTTP/3 streams, and the other one is not to decrement the counter for HTTP/3 streams. The latter solution looks inconsistent with ngx_stat_reading/ngx_stat_writing, which are incremented on a per-request basis. The change adds ngx_stat_active increment for HTTP/3 request and push streams. --- src/http/v3/ngx_http_v3_filter_module.c | 4 ++++ src/http/v3/ngx_http_v3_request.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 34b2991ad..43d2cd1bd 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -896,6 +896,10 @@ ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, return NGX_ABORT; } +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_active, 1); +#endif + hc = ngx_palloc(c->pool, sizeof(ngx_http_connection_t)); if (hc == NULL) { ngx_http_close_connection(c); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 793a34816..533a50fb8 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -78,6 +78,10 @@ ngx_http_v3_init(ngx_connection_t *c) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init request stream"); +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_active, 1); +#endif + hc = c->data; clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); -- cgit From 2cd173d450cbb61291c772556609bcfbc9da1170 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 22 Sep 2021 14:10:43 +0300 Subject: HTTP/3: fixed null pointer dereference with server push. See details for HTTP/2 fix in 8b0553239592 for a complete description. --- src/http/v3/ngx_http_v3_filter_module.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 43d2cd1bd..a88b32f07 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -858,6 +858,10 @@ ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, return NGX_ABORT; } + if (r->headers_in.host == NULL) { + return NGX_ABORT; + } + push_id = h3c->next_push_id++; rc = ngx_http_v3_create_push_request(r, path, push_id); -- cgit From 4d92aa79571d095e088c22513262a68aa347950d Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 27 Sep 2021 17:42:53 +0300 Subject: HTTP/3: fixed server push after ea9b645472b5. Unlike in HTTP/2, both "host" and ":authority" reside in r->headers_in.server. --- src/http/v3/ngx_http_v3_filter_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index a88b32f07..ee3c99dc1 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -858,7 +858,7 @@ ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, return NGX_ABORT; } - if (r->headers_in.host == NULL) { + if (r->headers_in.server.len == 0) { return NGX_ABORT; } -- cgit From 2765b63216fab23040aa83731ffd7d767cf0fa31 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 29 Sep 2021 15:01:53 +0300 Subject: Fixed mismerge of ssl_reject_handshake in 71b7453fb11f. In particular, this fixes rejecting "listen .. quic|http3" configurations without TLSv1.3 configured. --- src/http/modules/ngx_http_ssl_module.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index efbc4594c..dbb5905df 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -1385,14 +1385,23 @@ ngx_http_ssl_init(ngx_conf_t *cf) sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; if (sscf->certificates) { + + if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "\"ssl_protocols\" must enable TLSv1.3 for " + "the \"listen ... %s\" directive in %s:%ui", + name, cscf->file_name, cscf->line); + return NGX_ERROR; + } + continue; } if (!sscf->reject_handshake) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "no \"ssl_certificate\" is defined for " - "the \"listen ... ssl\" directive in %s:%ui", - cscf->file_name, cscf->line); + "the \"listen ... %s\" directive in %s:%ui", + name, cscf->file_name, cscf->line); return NGX_ERROR; } @@ -1417,14 +1426,6 @@ ngx_http_ssl_init(ngx_conf_t *cf) name, cscf->file_name, cscf->line); return NGX_ERROR; } - - if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "\"ssl_protocols\" did not enable TLSv1.3 for " - "the \"listen ... %s\" directives in %s:%ui", - name, cscf->file_name, cscf->line); - return NGX_ERROR; - } } } -- cgit From dab6035d68f3dd3f212393635c193c7aefea0d65 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 29 Sep 2021 15:01:59 +0300 Subject: HTTP/3: fixed segfault when using SSL certificates with variables. A QUIC connection doesn't have c->log->data and friends initialized to sensible values. Yet, a request can be created in the certificate callback with such an assumption, which leads to a segmentation fault due to null pointer dereference in ngx_http_free_request(). The fix is to adjust initializing the QUIC part of a connection such that it has all of that in place. Further, this appends logging error context for unsuccessful QUIC handshakes: - cannot load certificate .. while handling frames - SSL_do_handshake() failed .. while sending frames --- src/http/modules/ngx_http_quic_module.c | 2 -- src/http/ngx_http_request.c | 16 ++++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index b41c069b6..ce13a223f 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -198,8 +198,6 @@ ngx_http_quic_init(ngx_connection_t *c) hc->ssl = 1; if (c->quic == NULL) { - c->log->connection = c->number; - qcf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_quic_module); ngx_quic_run(c, qcf); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 2b838cfc3..6496a5400 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -299,14 +299,6 @@ ngx_http_init_connection(ngx_connection_t *c) /* the default server configuration for the address:port */ hc->conf_ctx = hc->addr_conf->default_server->ctx; -#if (NGX_HTTP_QUIC) - if (hc->addr_conf->quic) { - if (ngx_http_quic_init(c) == NGX_DONE) { - return; - } - } -#endif - ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); if (ctx == NULL) { ngx_http_close_connection(c); @@ -324,6 +316,14 @@ ngx_http_init_connection(ngx_connection_t *c) c->log_error = NGX_ERROR_INFO; +#if (NGX_HTTP_QUIC) + if (hc->addr_conf->quic) { + if (ngx_http_quic_init(c) == NGX_DONE) { + return; + } + } +#endif + rev = c->read; rev->handler = ngx_http_wait_request_handler; c->write->handler = ngx_http_empty_handler; -- cgit From b6b2a45fb6824185889373cc0af070c4a90c1b4a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 30 Sep 2021 17:14:42 +0300 Subject: Added r->response_sent flag. The flag indicates that the entire response was sent to the socket up to the last_buf flag. The flag is only usable for protocol implementations that call ngx_http_write_filter() from header filter, such as HTTP/1.x and HTTP/3. --- src/http/ngx_http_request.h | 1 + src/http/ngx_http_write_filter_module.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index aee9109c3..a19e975b2 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -547,6 +547,7 @@ struct ngx_http_request_s { unsigned request_complete:1; unsigned request_output:1; unsigned header_sent:1; + unsigned response_sent:1; unsigned expect_tested:1; unsigned root_tested:1; unsigned done:1; diff --git a/src/http/ngx_http_write_filter_module.c b/src/http/ngx_http_write_filter_module.c index 6a5d957b1..2a4996251 100644 --- a/src/http/ngx_http_write_filter_module.c +++ b/src/http/ngx_http_write_filter_module.c @@ -239,6 +239,10 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) r->out = NULL; c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; + if (last) { + r->response_sent = 1; + } + return NGX_OK; } @@ -350,6 +354,10 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; + if (last) { + r->response_sent = 1; + } + if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) { return NGX_AGAIN; } -- cgit From 38d56f4ccd94ffba49e84ace82125249af7ef20a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 27 Sep 2021 17:08:48 +0300 Subject: HTTP/3: reset streams with incomplete responses or timeouts. This prevents client from closing the QUIC connection due to response parse error. --- src/http/ngx_http_request.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 6496a5400..e37ef8024 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -3746,6 +3746,12 @@ ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc) if (r->connection->timedout) { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); +#if (NGX_HTTP_V3) + if (r->connection->quic) { + (void) ngx_quic_reset_stream(r->connection, + NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR); + } else +#endif if (clcf->reset_timedout_connection) { linger.l_onoff = 1; linger.l_linger = 0; @@ -3757,6 +3763,14 @@ ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc) "setsockopt(SO_LINGER) failed"); } } + + } else if (!r->response_sent) { +#if (NGX_HTTP_V3) + if (r->connection->quic) { + (void) ngx_quic_reset_stream(r->connection, + NGX_HTTP_V3_ERR_INTERNAL_ERROR); + } +#endif } /* the various request strings were allocated from r->pool */ -- cgit From ec86cf18fa5d64aba662d99ab7d195e2e6009545 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 6 Oct 2021 14:48:59 +0300 Subject: HTTP/3: removed client-side encoder support. Dynamic tables are not used when generating responses anyway. --- src/http/v3/ngx_http_v3_streams.c | 149 -------------------------------------- src/http/v3/ngx_http_v3_streams.h | 7 -- 2 files changed, 156 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 1cff29319..303d1d301 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -479,155 +479,6 @@ failed: } -ngx_int_t -ngx_http_v3_send_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, - ngx_uint_t index, ngx_str_t *value) -{ - u_char *p, buf[NGX_HTTP_V3_PREFIX_INT_LEN * 2]; - size_t n; - ngx_connection_t *ec; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 client ref insert, %s[%ui] \"%V\"", - dynamic ? "dynamic" : "static", index, value); - - ec = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); - if (ec == NULL) { - return NGX_ERROR; - } - - p = buf; - - *p = (dynamic ? 0x80 : 0xc0); - p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 6); - - /* XXX option for huffman? */ - *p = 0; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, value->len, 7); - - n = p - buf; - - if (ec->send(ec, buf, n) != (ssize_t) n) { - goto failed; - } - - if (ec->send(ec, value->data, value->len) != (ssize_t) value->len) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_http_v3_close_uni_stream(ec); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_send_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) -{ - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *ec; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 client insert \"%V\":\"%V\"", name, value); - - ec = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); - if (ec == NULL) { - return NGX_ERROR; - } - - /* XXX option for huffman? */ - buf[0] = 0x40; - n = (u_char *) ngx_http_v3_encode_prefix_int(buf, name->len, 5) - buf; - - if (ec->send(ec, buf, n) != (ssize_t) n) { - goto failed; - } - - if (ec->send(ec, name->data, name->len) != (ssize_t) name->len) { - goto failed; - } - - /* XXX option for huffman? */ - buf[0] = 0; - n = (u_char *) ngx_http_v3_encode_prefix_int(buf, value->len, 7) - buf; - - if (ec->send(ec, buf, n) != (ssize_t) n) { - goto failed; - } - - if (ec->send(ec, value->data, value->len) != (ssize_t) value->len) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_http_v3_close_uni_stream(ec); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_send_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) -{ - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *ec; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 client set capacity %ui", capacity); - - ec = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); - if (ec == NULL) { - return NGX_ERROR; - } - - buf[0] = 0x20; - n = (u_char *) ngx_http_v3_encode_prefix_int(buf, capacity, 5) - buf; - - if (ec->send(ec, buf, n) != (ssize_t) n) { - ngx_http_v3_close_uni_stream(ec); - return NGX_ERROR; - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_send_duplicate(ngx_connection_t *c, ngx_uint_t index) -{ - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *ec; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 client duplicate %ui", index); - - ec = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_ENCODER); - if (ec == NULL) { - return NGX_ERROR; - } - - buf[0] = 0; - n = (u_char *) ngx_http_v3_encode_prefix_int(buf, index, 5) - buf; - - if (ec->send(ec, buf, n) != (ssize_t) n) { - ngx_http_v3_close_uni_stream(ec); - return NGX_ERROR; - } - - return NGX_OK; -} - - ngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) { diff --git a/src/http/v3/ngx_http_v3_streams.h b/src/http/v3/ngx_http_v3_streams.h index 4f7290e4c..42ff849be 100644 --- a/src/http/v3/ngx_http_v3_streams.h +++ b/src/http/v3/ngx_http_v3_streams.h @@ -27,13 +27,6 @@ ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); -ngx_int_t ngx_http_v3_send_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, - ngx_uint_t index, ngx_str_t *value); -ngx_int_t ngx_http_v3_send_insert(ngx_connection_t *c, ngx_str_t *name, - ngx_str_t *value); -ngx_int_t ngx_http_v3_send_set_capacity(ngx_connection_t *c, - ngx_uint_t capacity); -ngx_int_t ngx_http_v3_send_duplicate(ngx_connection_t *c, ngx_uint_t index); ngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id); ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, -- cgit From 0c33e484a4333fe2a343baf3aeefae3212534db3 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 6 Oct 2021 14:51:16 +0300 Subject: HTTP/3: fixed request length calculation. Previously, when request was blocked, r->request_length was not updated. --- src/http/v3/ngx_http_v3_request.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 533a50fb8..44aef49e4 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -297,6 +297,8 @@ ngx_http_v3_process_request(ngx_event_t *rev) break; } + r->request_length += b->pos - p; + if (rc == NGX_BUSY) { if (rev->error) { ngx_http_close_request(r, NGX_HTTP_CLOSE); @@ -310,8 +312,6 @@ ngx_http_v3_process_request(ngx_event_t *rev) break; } - r->request_length += b->pos - p; - if (rc == NGX_AGAIN) { continue; } -- cgit From 434f11bf3f4c9c8466a946c775441ecd6f768c13 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 7 Oct 2021 13:22:42 +0300 Subject: HTTP/3: traffic-based flood detection. With this patch, all traffic over HTTP/3 bidi and uni streams is counted in the h3c->total_bytes field, and payload traffic is counted in the h3c->payload_bytes field. As long as total traffic is many times larger than payload traffic, we consider this to be a flood. Request header traffic is counted as if all fields are literal. Response header traffic is counted as is. --- src/http/v3/ngx_http_v3.c | 19 +++++++++++ src/http/v3/ngx_http_v3.h | 4 +++ src/http/v3/ngx_http_v3_filter_module.c | 51 ++++++++++++++++++++++------ src/http/v3/ngx_http_v3_request.c | 27 +++++++++++++++ src/http/v3/ngx_http_v3_streams.c | 60 ++++++++++++++++++++++++++------- 5 files changed, 138 insertions(+), 23 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index 2c838f4b5..500113509 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -86,3 +86,22 @@ ngx_http_v3_cleanup_session(void *data) ngx_del_timer(&h3c->keepalive); } } + + +ngx_int_t +ngx_http_v3_check_flood(ngx_connection_t *c) +{ + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + if (h3c->total_bytes / 8 > h3c->payload_bytes + 1048576) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "http3 flood detected"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "HTTP/3 flood detected"); + return NGX_ERROR; + } + + return NGX_OK; +} diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 9076b6ff5..53f38a7f2 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -128,6 +128,9 @@ struct ngx_http_v3_session_s { uint64_t max_push_id; uint64_t goaway_push_id; + off_t total_bytes; + off_t payload_bytes; + ngx_uint_t goaway; /* unsigned goaway:1; */ ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; @@ -136,6 +139,7 @@ struct ngx_http_v3_session_s { void ngx_http_v3_init(ngx_connection_t *c); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); +ngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c); ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r); ngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r); diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index ee3c99dc1..f835f8e5c 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -101,6 +101,7 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) ngx_list_part_t *part; ngx_table_elt_t *header; ngx_connection_t *c; + ngx_http_v3_session_t *h3c; ngx_http_v3_filter_ctx_t *ctx; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; @@ -120,6 +121,8 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) return NGX_OK; } + h3c = ngx_http_v3_get_session(r->connection); + if (r->method == NGX_HTTP_HEAD) { r->header_only = 1; } @@ -531,6 +534,8 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) n = b->last - b->pos; + h3c->payload_bytes += n; + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) + ngx_http_v3_encode_varlen_int(NULL, n); @@ -571,6 +576,9 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, r->headers_out.content_length_n); + h3c->payload_bytes += r->headers_out.content_length_n; + h3c->total_bytes += r->headers_out.content_length_n; + cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; @@ -590,6 +598,10 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) ngx_http_set_ctx(r, ctx, ngx_http_v3_filter_module); } + for (cl = out; cl; cl = cl->next) { + h3c->total_bytes += cl->buf->last - cl->buf->pos; + } + return ngx_http_write_filter(r, out); } @@ -1096,9 +1108,12 @@ static ngx_chain_t * ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, uint64_t push_id) { - size_t n, len; - ngx_buf_t *b; - ngx_chain_t *hl, *cl; + size_t n, len; + ngx_buf_t *b; + ngx_chain_t *hl, *cl; + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(r->connection); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http3 create push promise id:%uL", push_id); @@ -1233,6 +1248,8 @@ ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, n = b->last - b->pos; + h3c->payload_bytes += n; + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_PUSH_PROMISE) + ngx_http_v3_encode_varlen_int(NULL, n); @@ -1265,6 +1282,7 @@ ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *out, *cl, *tl, **ll; + ngx_http_v3_session_t *h3c; ngx_http_v3_filter_ctx_t *ctx; if (in == NULL) { @@ -1276,6 +1294,8 @@ ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) return ngx_http_next_body_filter(r, in); } + h3c = ngx_http_v3_get_session(r->connection); + out = NULL; ll = &out; @@ -1340,6 +1360,8 @@ ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) tl->next = out; out = tl; + + h3c->payload_bytes += size; } if (cl->buf->last_buf) { @@ -1356,6 +1378,10 @@ ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) *ll = NULL; } + for (cl = out; cl; cl = cl->next) { + h3c->total_bytes += cl->buf->last - cl->buf->pos; + } + rc = ngx_http_next_body_filter(r, out); ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, @@ -1369,13 +1395,16 @@ static ngx_chain_t * ngx_http_v3_create_trailers(ngx_http_request_t *r, ngx_http_v3_filter_ctx_t *ctx) { - size_t len, n; - u_char *p; - ngx_buf_t *b; - ngx_uint_t i; - ngx_chain_t *cl, *hl; - ngx_list_part_t *part; - ngx_table_elt_t *header; + size_t len, n; + u_char *p; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *cl, *hl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(r->connection); len = 0; @@ -1461,6 +1490,8 @@ ngx_http_v3_create_trailers(ngx_http_request_t *r, n = b->last - b->pos; + h3c->payload_bytes += n; + hl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (hl == NULL) { return NULL; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 44aef49e4..bb9a72248 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -218,6 +218,7 @@ ngx_http_v3_process_request(ngx_event_t *rev) ngx_int_t rc; ngx_connection_t *c; ngx_http_request_t *r; + ngx_http_v3_session_t *h3c; ngx_http_core_srv_conf_t *cscf; ngx_http_v3_parse_headers_t *st; @@ -233,6 +234,8 @@ ngx_http_v3_process_request(ngx_event_t *rev) return; } + h3c = ngx_http_v3_get_session(c); + st = &r->v3_parse->headers; b = r->header_in; @@ -298,6 +301,12 @@ ngx_http_v3_process_request(ngx_event_t *rev) } r->request_length += b->pos - p; + h3c->total_bytes += b->pos - p; + + if (ngx_http_v3_check_flood(c) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_CLOSE); + break; + } if (rc == NGX_BUSY) { if (rev->error) { @@ -318,6 +327,10 @@ ngx_http_v3_process_request(ngx_event_t *rev) /* rc == NGX_OK || rc == NGX_DONE */ + h3c->payload_bytes += ngx_http_v3_encode_field_l(NULL, + &st->field_rep.field.name, + &st->field_rep.field.value); + if (ngx_http_v3_process_header(r, &st->field_rep.field.name, &st->field_rep.field.value) != NGX_OK) @@ -1080,6 +1093,7 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_buf_t *b; ngx_uint_t last; ngx_chain_t *cl, *out, *tl, **ll; + ngx_http_v3_session_t *h3c; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; @@ -1088,6 +1102,8 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) rb = r->request_body; st = &r->v3_parse->body; + h3c = ngx_http_v3_get_session(r->connection); + if (rb->rest == -1) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -1135,6 +1151,11 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) rc = ngx_http_v3_parse_data(r->connection, st, cl->buf); r->request_length += cl->buf->pos - p; + h3c->total_bytes += cl->buf->pos - p; + + if (ngx_http_v3_check_flood(r->connection) != NGX_OK) { + return NGX_HTTP_CLOSE; + } if (rc == NGX_AGAIN) { continue; @@ -1178,6 +1199,8 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { rb->received += st->length; r->request_length += st->length; + h3c->total_bytes += st->length; + h3c->payload_bytes += st->length; if (st->length < 8) { @@ -1222,12 +1245,16 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) cl->buf->pos += (size_t) st->length; rb->received += st->length; r->request_length += st->length; + h3c->total_bytes += st->length; + h3c->payload_bytes += st->length; st->length = 0; } else { st->length -= size; rb->received += size; r->request_length += size; + h3c->total_bytes += size; + h3c->payload_bytes += size; cl->buf->pos = cl->buf->last; } diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 303d1d301..2ff1320ae 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -171,6 +171,7 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) ngx_buf_t b; ngx_int_t rc; ngx_connection_t *c; + ngx_http_v3_session_t *h3c; ngx_http_v3_uni_stream_t *us; c = rev->data; @@ -207,6 +208,14 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) b.pos = buf; b.last = buf + n; + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (ngx_http_v3_check_flood(c) != NGX_OK) { + ngx_http_v3_close_uni_stream(c); + return; + } + rc = ngx_http_v3_parse_uni(c, &us->parse, &b); if (rc == NGX_DONE) { @@ -282,6 +291,9 @@ ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) p = (u_char *) ngx_http_v3_encode_varlen_int(p, push_id); n = p - buf; + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + if (sc->send(sc, buf, n) != (ssize_t) n) { goto failed; } @@ -291,7 +303,6 @@ ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) goto failed; } - h3c = ngx_http_v3_get_session(c); h3c->npushing++; cln->handler = ngx_http_v3_push_cleanup; @@ -383,6 +394,9 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf; + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + if (sc->send(sc, buf, n) != (ssize_t) n) { goto failed; } @@ -403,6 +417,7 @@ ngx_http_v3_send_settings(ngx_connection_t *c) u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; size_t n; ngx_connection_t *cc; + ngx_http_v3_session_t *h3c; ngx_http_v3_srv_conf_t *h3scf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); @@ -431,6 +446,9 @@ ngx_http_v3_send_settings(ngx_connection_t *c) p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_blocked_streams); n = p - buf; + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + if (cc->send(cc, buf, n) != (ssize_t) n) { goto failed; } @@ -448,9 +466,10 @@ failed: ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id) { - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3]; - size_t n; - ngx_connection_t *cc; + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3]; + size_t n; + ngx_connection_t *cc; + ngx_http_v3_session_t *h3c; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send goaway %uL", id); @@ -465,6 +484,9 @@ ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id) p = (u_char *) ngx_http_v3_encode_varlen_int(p, id); n = p - buf; + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + if (cc->send(cc, buf, n) != (ssize_t) n) { goto failed; } @@ -482,9 +504,10 @@ failed: ngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) { - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *dc; + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client ack section %ui", stream_id); @@ -497,6 +520,9 @@ ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) buf[0] = 0x80; n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 7) - buf; + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + if (dc->send(dc, buf, n) != (ssize_t) n) { ngx_http_v3_close_uni_stream(dc); return NGX_ERROR; @@ -509,9 +535,10 @@ ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) { - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *dc; + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client cancel stream %ui", stream_id); @@ -524,6 +551,9 @@ ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) buf[0] = 0x40; n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 6) - buf; + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + if (dc->send(dc, buf, n) != (ssize_t) n) { ngx_http_v3_close_uni_stream(dc); return NGX_ERROR; @@ -536,9 +566,10 @@ ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) ngx_int_t ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) { - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *dc; + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 client increment insert count %ui", inc); @@ -551,6 +582,9 @@ ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) buf[0] = 0; n = (u_char *) ngx_http_v3_encode_prefix_int(buf, inc, 6) - buf; + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + if (dc->send(dc, buf, n) != (ssize_t) n) { ngx_http_v3_close_uni_stream(dc); return NGX_ERROR; -- cgit From 0572c2a69f4edef04e3babdb6f9ef18ff52a9619 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Wed, 3 Nov 2021 13:36:21 +0300 Subject: QUIC: connections with wrong ALPN protocols are now rejected. Previously, it was not enforced in the stream module. Now, since b9e02e9b2f1d it is possible to specify protocols. Since ALPN is always required, the 'require_alpn' setting is now obsolete. --- src/http/modules/ngx_http_quic_module.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index ce13a223f..9e6d17ead 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -331,7 +331,6 @@ ngx_http_quic_create_srv_conf(ngx_conf_t *cf) conf->retry = NGX_CONF_UNSET; conf->gso_enabled = NGX_CONF_UNSET; - conf->require_alpn = 1; return conf; } -- cgit From 6118ec73cfef53897d00cc81810ee661321f0057 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 18 Oct 2021 15:22:33 +0300 Subject: HTTP/3: adjusted QUIC connection finalization. When an HTTP/3 function returns an error in context of a QUIC stream, it's this function's responsibility now to finalize the entire QUIC connection with the right code, if required. Previously, QUIC connection finalization could be done both outside and inside such functions. The new rule follows a similar rule for logging, leads to cleaner code, and allows to provide more details about the error. While here, a few error cases are no longer treated as fatal and QUIC connection is no longer finalized in these cases. A few other cases now lead to stream reset instead of connection finalization. --- src/http/v3/ngx_http_v3.c | 12 +++++- src/http/v3/ngx_http_v3_request.c | 17 ++------- src/http/v3/ngx_http_v3_streams.c | 79 +++++++++++++++++++++++++++++++-------- src/http/v3/ngx_http_v3_tables.c | 4 ++ 4 files changed, 82 insertions(+), 30 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index 500113509..97d8a5e34 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -33,7 +33,7 @@ ngx_http_v3_init_session(ngx_connection_t *c) h3c = ngx_pcalloc(pc->pool, sizeof(ngx_http_v3_session_t)); if (h3c == NULL) { - return NGX_ERROR; + goto failed; } h3c->max_push_id = (uint64_t) -1; @@ -49,7 +49,7 @@ ngx_http_v3_init_session(ngx_connection_t *c) cln = ngx_pool_cleanup_add(pc->pool, 0); if (cln == NULL) { - return NGX_ERROR; + goto failed; } cln->handler = ngx_http_v3_cleanup_session; @@ -58,6 +58,14 @@ ngx_http_v3_init_session(ngx_connection_t *c) hc->v3_session = h3c; return ngx_http_v3_send_settings(c); + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create http3 session"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "failed to create http3 session"); + return NGX_ERROR; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index bb9a72248..5c905bc3a 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -65,8 +65,6 @@ ngx_http_v3_init(ngx_connection_t *c) ngx_http_core_srv_conf_t *cscf; if (ngx_http_v3_init_session(c) != NGX_OK) { - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - "internal error"); ngx_http_close_connection(c); return; } @@ -110,8 +108,6 @@ ngx_http_v3_init(ngx_connection_t *c) h3c->goaway = 1; if (ngx_http_v3_send_goaway(c, (n + 1) << 2) != NGX_OK) { - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - "goaway error"); ngx_http_close_connection(c); return; } @@ -287,15 +283,14 @@ ngx_http_v3_process_request(ngx_event_t *rev) rc = ngx_http_v3_parse_headers(c, st, b); if (rc > 0) { - ngx_http_v3_finalize_connection(c, rc, - "could not parse request headers"); + ngx_quic_reset_stream(c, rc); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client sent invalid header"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); break; } if (rc == NGX_ERROR) { - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - "internal error"); ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); break; } @@ -1167,17 +1162,13 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) } if (rc > 0) { - ngx_http_v3_finalize_connection(r->connection, rc, - "client sent invalid body"); + ngx_quic_reset_stream(r->connection, rc); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client sent invalid body"); return NGX_HTTP_BAD_REQUEST; } if (rc == NGX_ERROR) { - ngx_http_v3_finalize_connection(r->connection, - NGX_HTTP_V3_ERR_INTERNAL_ERROR, - "internal error"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 2ff1320ae..257ec317a 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -283,7 +283,7 @@ ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) sc = ngx_quic_open_stream(c, 0); if (sc == NULL) { - return NULL; + goto failed; } p = buf; @@ -318,7 +318,13 @@ ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) failed: - ngx_http_v3_close_uni_stream(sc); + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create push stream"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "failed to create push stream"); + if (sc) { + ngx_http_v3_close_uni_stream(sc); + } return NULL; } @@ -368,7 +374,7 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) sc = ngx_quic_open_stream(c, 0); if (sc == NULL) { - return NULL; + goto failed; } sc->quic->cancelable = 1; @@ -405,7 +411,13 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) failed: - ngx_http_v3_close_uni_stream(sc); + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create server stream"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "failed to create server stream"); + if (sc) { + ngx_http_v3_close_uni_stream(sc); + } return NULL; } @@ -424,7 +436,7 @@ ngx_http_v3_send_settings(ngx_connection_t *c) cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); if (cc == NULL) { - return NGX_DECLINED; + return NGX_ERROR; } h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); @@ -457,6 +469,10 @@ ngx_http_v3_send_settings(ngx_connection_t *c) failed: + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send settings"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send settings"); ngx_http_v3_close_uni_stream(cc); return NGX_ERROR; @@ -475,7 +491,7 @@ ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id) cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); if (cc == NULL) { - return NGX_DECLINED; + return NGX_ERROR; } n = ngx_http_v3_encode_varlen_int(NULL, id); @@ -495,6 +511,10 @@ ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id) failed: + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send goaway"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send goaway"); ngx_http_v3_close_uni_stream(cc); return NGX_ERROR; @@ -510,7 +530,7 @@ ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) ngx_http_v3_session_t *h3c; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 client ack section %ui", stream_id); + "http3 send section acknowledgement %ui", stream_id); dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); if (dc == NULL) { @@ -524,11 +544,21 @@ ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) h3c->total_bytes += n; if (dc->send(dc, buf, n) != (ssize_t) n) { - ngx_http_v3_close_uni_stream(dc); - return NGX_ERROR; + goto failed; } return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "failed to send section acknowledgement"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send section acknowledgement"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; } @@ -541,7 +571,7 @@ ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) ngx_http_v3_session_t *h3c; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 client cancel stream %ui", stream_id); + "http3 send stream cancellation %ui", stream_id); dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); if (dc == NULL) { @@ -555,11 +585,20 @@ ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) h3c->total_bytes += n; if (dc->send(dc, buf, n) != (ssize_t) n) { - ngx_http_v3_close_uni_stream(dc); - return NGX_ERROR; + goto failed; } return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send stream cancellation"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send stream cancellation"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; } @@ -572,7 +611,7 @@ ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) ngx_http_v3_session_t *h3c; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 client increment insert count %ui", inc); + "http3 send insert count increment %ui", inc); dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); if (dc == NULL) { @@ -586,11 +625,21 @@ ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) h3c->total_bytes += n; if (dc->send(dc, buf, n) != (ssize_t) n) { - ngx_http_v3_close_uni_stream(dc); - return NGX_ERROR; + goto failed; } return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "failed to send insert count increment"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send insert count increment"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; } diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index 348088774..c6d543ac4 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -589,6 +589,10 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) if (h3c->nblocked == h3scf->max_blocked_streams) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_blocked_streams limit"); + + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED, + "too many blocked streams"); return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } -- cgit From a6fb8fe85077bd10e11231c70ece803284890520 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 18 Oct 2021 15:47:06 +0300 Subject: HTTP/3: allowed QUIC stream connection reuse. A QUIC stream connection is treated as reusable until first bytes of request arrive, which is also when the request object is now allocated. A connection closed as a result of draining, is reset with the error code H3_REQUEST_REJECTED. Such behavior is allowed by quic-http-34: Once a request stream has been opened, the request MAY be cancelled by either endpoint. Clients cancel requests if the response is no longer of interest; servers cancel requests if they are unable to or choose not to respond. When the server cancels a request without performing any application processing, the request is considered "rejected." The server SHOULD abort its response stream with the error code H3_REQUEST_REJECTED. The client can treat requests rejected by the server as though they had never been sent at all, thereby allowing them to be retried later. --- src/http/ngx_http_request.c | 27 ++++--- src/http/v3/ngx_http_v3.h | 4 ++ src/http/v3/ngx_http_v3_request.c | 144 +++++++++++++++++++++++++++++++++----- src/http/v3/ngx_http_v3_streams.c | 17 +++-- 4 files changed, 154 insertions(+), 38 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 88516cb4d..7125e7dd1 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -3731,15 +3731,14 @@ ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc) log->action = "closing request"; - if (r->connection->timedout) { + if (r->connection->timedout +#if (NGX_HTTP_QUIC) + && r->connection->quic == NULL +#endif + ) + { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); -#if (NGX_HTTP_V3) - if (r->connection->quic) { - (void) ngx_quic_reset_stream(r->connection, - NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR); - } else -#endif if (clcf->reset_timedout_connection) { linger.l_onoff = 1; linger.l_linger = 0; @@ -3751,14 +3750,6 @@ ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc) "setsockopt(SO_LINGER) failed"); } } - - } else if (!r->response_sent) { -#if (NGX_HTTP_V3) - if (r->connection->quic) { - (void) ngx_quic_reset_stream(r->connection, - NGX_HTTP_V3_ERR_INTERNAL_ERROR); - } -#endif } /* the various request strings were allocated from r->pool */ @@ -3818,6 +3809,12 @@ ngx_http_close_connection(ngx_connection_t *c) #endif +#if (NGX_HTTP_V3) + if (ngx_http_v3_connection(c)) { + ngx_http_v3_reset_connection(c); + } +#endif + #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_active, -1); #endif diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 53f38a7f2..97e8a1c29 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -90,6 +90,9 @@ #define ngx_http_v3_shutdown_connection(c, code, reason) \ ngx_quic_shutdown_connection(c->quic->parent, code, reason) +#define ngx_http_v3_connection(c) \ + ((c)->quic ? ngx_http_quic_get_connection(c)->addr_conf->http3 : 0) + typedef struct { size_t max_table_capacity; @@ -138,6 +141,7 @@ struct ngx_http_v3_session_s { void ngx_http_v3_init(ngx_connection_t *c); +void ngx_http_v3_reset_connection(ngx_connection_t *c); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); ngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 5c905bc3a..6f980ed0b 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,6 +10,7 @@ #include +static void ngx_http_v3_wait_request_handler(ngx_event_t *rev); static void ngx_http_v3_cleanup_request(void *data); static void ngx_http_v3_process_request(ngx_event_t *rev); static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, @@ -53,12 +54,8 @@ static const struct { void ngx_http_v3_init(ngx_connection_t *c) { - size_t size; uint64_t n; - ngx_buf_t *b; ngx_event_t *rev; - ngx_pool_cleanup_t *cln; - ngx_http_request_t *r; ngx_http_connection_t *hc; ngx_http_v3_session_t *h3c; ngx_http_core_loc_conf_t *clcf; @@ -96,7 +93,7 @@ ngx_http_v3_init(ngx_connection_t *c) h3c = ngx_http_v3_get_session(c); if (h3c->goaway) { - ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_REQUEST_REJECTED); + c->close = 1; ngx_http_close_connection(c); return; } @@ -116,21 +113,57 @@ ngx_http_v3_init(ngx_connection_t *c) "reached maximum number of requests"); } - cln = ngx_pool_cleanup_add(c->pool, 0); - if (cln == NULL) { + rev = c->read; + rev->handler = ngx_http_v3_wait_request_handler; + c->write->handler = ngx_http_empty_handler; + + if (rev->ready) { + rev->handler(rev); + return; + } + + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); + + ngx_add_timer(rev, cscf->client_header_timeout); + ngx_reusable_connection(c, 1); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; } +} - cln->handler = ngx_http_v3_cleanup_request; - cln->data = c; - h3c->nrequests++; +static void +ngx_http_v3_wait_request_handler(ngx_event_t *rev) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_pool_cleanup_t *cln; + ngx_http_request_t *r; + ngx_http_connection_t *hc; + ngx_http_v3_session_t *h3c; + ngx_http_core_srv_conf_t *cscf; - if (h3c->keepalive.timer_set) { - ngx_del_timer(&h3c->keepalive); + c = rev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 wait request handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_http_close_connection(c); + return; } + if (c->close) { + ngx_http_close_connection(c); + return; + } + + hc = c->data; cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); size = cscf->client_header_buffer_size; @@ -159,8 +192,49 @@ ngx_http_v3_init(ngx_connection_t *c) b->end = b->last + size; } + n = c->recv(c, b->last, size); + + if (n == NGX_AGAIN) { + + if (!rev->timer_set) { + ngx_add_timer(rev, cscf->client_header_timeout); + ngx_reusable_connection(c, 1); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + /* + * We are trying to not hold c->buffer's memory for an idle connection. + */ + + if (ngx_pfree(c->pool, b->start) == NGX_OK) { + b->start = NULL; + } + + return; + } + + if (n == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client closed connection"); + ngx_http_close_connection(c); + return; + } + + b->last += n; + c->log->action = "reading client request"; + ngx_reusable_connection(c, 0); + r = ngx_http_create_request(c); if (r == NULL) { ngx_http_close_connection(c); @@ -171,7 +245,7 @@ ngx_http_v3_init(ngx_connection_t *c) r->v3_parse = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_parse_t)); if (r->v3_parse == NULL) { - ngx_http_close_connection(c); + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } @@ -179,23 +253,59 @@ ngx_http_v3_init(ngx_connection_t *c) * cscf->large_client_header_buffers.num; c->data = r; - c->requests = n + 1; + c->requests = (c->quic->id >> 2) + 1; - rev = c->read; - rev->handler = ngx_http_v3_process_request; + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + cln->handler = ngx_http_v3_cleanup_request; + cln->data = r; + h3c = ngx_http_v3_get_session(c); + h3c->nrequests++; + + if (h3c->keepalive.timer_set) { + ngx_del_timer(&h3c->keepalive); + } + + rev->handler = ngx_http_v3_process_request; ngx_http_v3_process_request(rev); } +void +ngx_http_v3_reset_connection(ngx_connection_t *c) +{ + if (c->timedout) { + ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR); + + } else if (c->close) { + ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_REQUEST_REJECTED); + + } else if (c->requests == 0 || c->error) { + ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR); + } +} + + static void ngx_http_v3_cleanup_request(void *data) { - ngx_connection_t *c = data; + ngx_http_request_t *r = data; + ngx_connection_t *c; ngx_http_v3_session_t *h3c; ngx_http_core_loc_conf_t *clcf; + c = r->connection; + + if (!r->response_sent) { + c->error = 1; + } + h3c = ngx_http_v3_get_session(c); if (--h3c->nrequests == 0) { diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 257ec317a..23b16cbc2 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -49,7 +49,8 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, "reached maximum number of uni streams"); - ngx_http_close_connection(c); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); return; } @@ -57,7 +58,11 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { - ngx_http_close_connection(c); + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "memory allocation error"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); return; } @@ -79,12 +84,12 @@ ngx_http_v3_close_uni_stream(ngx_connection_t *c) ngx_http_v3_session_t *h3c; ngx_http_v3_uni_stream_t *us; - us = c->data; - h3c = ngx_http_v3_get_session(c); - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 close stream"); - if (us->index >= 0) { + us = c->data; + + if (us && us->index >= 0) { + h3c = ngx_http_v3_get_session(c); h3c->known_streams[us->index] = NULL; } -- cgit From 5c99f43e6f6f2e97a625bf00330f2f8d5af29815 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 18 Oct 2021 14:48:11 +0300 Subject: HTTP/3: send Stream Cancellation instruction. As per quic-qpack-21: When a stream is reset or reading is abandoned, the decoder emits a Stream Cancellation instruction. Previously the instruction was not sent. Now it's sent when closing QUIC stream connection if dynamic table capacity is non-zero and eof was not received from client. The latter condition means that a trailers section may still be on its way from client and the stream needs to be cancelled. --- src/http/v3/ngx_http_v3_request.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 6f980ed0b..e0c3a71ba 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -279,6 +279,14 @@ ngx_http_v3_wait_request_handler(ngx_event_t *rev) void ngx_http_v3_reset_connection(ngx_connection_t *c) { + ngx_http_v3_srv_conf_t *h3scf; + + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + if (h3scf->max_table_capacity > 0 && !c->read->eof) { + (void) ngx_http_v3_send_cancel_stream(c, c->quic->id); + } + if (c->timedout) { ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR); -- cgit From 50dd9ba7e85a8d4eeecaad8776f83adc607fd132 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 11 Nov 2021 19:07:00 +0300 Subject: QUIC: reject streams which we could not create. The reasons why a stream may not be created by server currently include hitting worker_connections limit and memory allocation error. Previously in these cases the entire QUIC connection was closed and all its streams were shut down. Now the new stream is rejected and existing streams continue working. To reject an HTTP/3 request stream, RESET_STREAM and STOP_SENDING with H3_REQUEST_REJECTED error code are sent to client. HTTP/3 uni streams and Stream streams are not rejected. --- src/http/modules/ngx_http_quic_module.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 9e6d17ead..323ee2ead 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -314,6 +314,7 @@ ngx_http_quic_create_srv_conf(ngx_conf_t *cf) * conf->tp.sr_enabled = 0 * conf->tp.preferred_address = NULL * conf->host_key = { 0, NULL } + * cong->stream_reject_code_uni = 0; */ conf->tp.max_idle_timeout = NGX_CONF_UNSET_MSEC; @@ -331,6 +332,8 @@ ngx_http_quic_create_srv_conf(ngx_conf_t *cf) conf->retry = NGX_CONF_UNSET; conf->gso_enabled = NGX_CONF_UNSET; + conf->stream_close_code = NGX_HTTP_V3_ERR_NO_ERROR; + conf->stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; return conf; } -- cgit From bfa2d1d59932b9983e3a694ed651bc4fe349edc5 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 17 Nov 2021 18:49:48 +0300 Subject: HTTP/3: fixed compilation with QUIC, but without HTTP/3. --- src/http/modules/ngx_http_quic_module.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c index 323ee2ead..9470df60e 100644 --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -332,8 +332,10 @@ ngx_http_quic_create_srv_conf(ngx_conf_t *cf) conf->retry = NGX_CONF_UNSET; conf->gso_enabled = NGX_CONF_UNSET; +#if (NGX_HTTP_V3) conf->stream_close_code = NGX_HTTP_V3_ERR_NO_ERROR; conf->stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; +#endif return conf; } -- cgit From 7e7e552a10b9e5d0fd94b1a657061253ccac709e Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 2 Dec 2021 13:59:09 +0300 Subject: HTTP/3: adjusted ALPN macro names to align with 61abb35bb8cf. --- src/http/modules/ngx_http_quic_module.h | 2 +- src/http/modules/ngx_http_ssl_module.c | 8 ++++---- src/http/v3/ngx_http_v3.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.h b/src/http/modules/ngx_http_quic_module.h index 8cadfbb87..3b96fcc47 100644 --- a/src/http/modules/ngx_http_quic_module.h +++ b/src/http/modules/ngx_http_quic_module.h @@ -14,7 +14,7 @@ #include -#define NGX_HTTP_QUIC_ALPN_ADVERTISE "\x0Ahq-interop" +#define NGX_HTTP_QUIC_ALPN_PROTO "\x0Ahq-interop" #define NGX_HTTP_QUIC_ALPN_DRAFT_FMT "\x05hq-%02uD" diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index f2a85f12e..afeb68462 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -455,15 +455,15 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, if (hc->addr_conf->quic) { #if (NGX_HTTP_V3) if (hc->addr_conf->http3) { - srv = (unsigned char *) NGX_HTTP_V3_ALPN_ADVERTISE; - srvlen = sizeof(NGX_HTTP_V3_ALPN_ADVERTISE) - 1; + srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO; + srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1; fmt = NGX_HTTP_V3_ALPN_DRAFT_FMT; } else #endif { - srv = (unsigned char *) NGX_HTTP_QUIC_ALPN_ADVERTISE; - srvlen = sizeof(NGX_HTTP_QUIC_ALPN_ADVERTISE) - 1; + srv = (unsigned char *) NGX_HTTP_QUIC_ALPN_PROTO; + srvlen = sizeof(NGX_HTTP_QUIC_ALPN_PROTO) - 1; fmt = NGX_HTTP_QUIC_ALPN_DRAFT_FMT; } diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 97e8a1c29..758cbd1af 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -19,7 +19,7 @@ #include -#define NGX_HTTP_V3_ALPN_ADVERTISE "\x02h3" +#define NGX_HTTP_V3_ALPN_PROTO "\x02h3" #define NGX_HTTP_V3_ALPN_DRAFT_FMT "\x05h3-%02uD" #define NGX_HTTP_V3_VARLEN_INT_LEN 4 -- cgit From 731915a0c5e90b79d3cca1a4b0a3c33e1f77631c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 6 Dec 2021 13:02:36 +0300 Subject: HTTP/3: merged ngx_http_quic_module into ngx_http_v3_module. --- src/http/modules/ngx_http_quic_module.c | 570 -------------------------------- src/http/modules/ngx_http_quic_module.h | 28 -- src/http/modules/ngx_http_ssl_module.c | 15 +- src/http/ngx_http.c | 24 +- src/http/ngx_http.h | 3 - src/http/ngx_http_core_module.c | 6 +- src/http/ngx_http_request.c | 18 +- src/http/ngx_http_upstream.c | 4 +- src/http/v3/ngx_http_v3.h | 10 +- src/http/v3/ngx_http_v3_module.c | 431 +++++++++++++++++++++++- src/http/v3/ngx_http_v3_request.c | 123 ++++++- 11 files changed, 579 insertions(+), 653 deletions(-) delete mode 100644 src/http/modules/ngx_http_quic_module.c delete mode 100644 src/http/modules/ngx_http_quic_module.h (limited to 'src/http') diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c deleted file mode 100644 index 9470df60e..000000000 --- a/src/http/modules/ngx_http_quic_module.c +++ /dev/null @@ -1,570 +0,0 @@ - -/* - * Copyright (C) Nginx, Inc. - * Copyright (C) Roman Arutyunyan - */ - - -#include -#include -#include - - -static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data); -static ngx_int_t ngx_http_quic_add_variables(ngx_conf_t *cf); -static void *ngx_http_quic_create_srv_conf(ngx_conf_t *cf); -static char *ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, - void *child); -static char *ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, - void *data); -static char *ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, - void *data); -static char *ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); - -static ngx_conf_post_t ngx_http_quic_max_ack_delay_post = - { ngx_http_quic_max_ack_delay }; -static ngx_conf_post_t ngx_http_quic_max_udp_payload_size_post = - { ngx_http_quic_max_udp_payload_size }; -static ngx_conf_num_bounds_t ngx_http_quic_ack_delay_exponent_bounds = - { ngx_conf_check_num_bounds, 0, 20 }; -static ngx_conf_num_bounds_t ngx_http_quic_active_connection_id_limit_bounds = - { ngx_conf_check_num_bounds, 2, -1 }; - - -static ngx_command_t ngx_http_quic_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_quic_conf_t, tp.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_quic_conf_t, tp.max_ack_delay), - &ngx_http_quic_max_ack_delay_post }, - - { ngx_string("quic_max_udp_payload_size"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_quic_conf_t, tp.max_udp_payload_size), - &ngx_http_quic_max_udp_payload_size_post }, - - { ngx_string("quic_initial_max_data"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_quic_conf_t, tp.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_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_quic_conf_t, tp.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_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_quic_conf_t, tp.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_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_quic_conf_t, tp.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_quic_conf_t, tp.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_quic_conf_t, tp.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_quic_conf_t, tp.ack_delay_exponent), - &ngx_http_quic_ack_delay_exponent_bounds }, - - { ngx_string("quic_disable_active_migration"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_flag_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_quic_conf_t, tp.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_quic_conf_t, tp.active_connection_id_limit), - &ngx_http_quic_active_connection_id_limit_bounds }, - - { ngx_string("quic_retry"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_quic_conf_t, retry), - NULL }, - - { ngx_string("quic_gso"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_quic_conf_t, gso_enabled), - NULL }, - - { ngx_string("quic_host_key"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_http_quic_host_key, - NGX_HTTP_SRV_CONF_OFFSET, - 0, - NULL }, - - ngx_null_command -}; - - -static ngx_http_module_t ngx_http_quic_module_ctx = { - ngx_http_quic_add_variables, /* preconfiguration */ - NULL, /* postconfiguration */ - - NULL, /* create main configuration */ - NULL, /* init main configuration */ - - ngx_http_quic_create_srv_conf, /* create server configuration */ - ngx_http_quic_merge_srv_conf, /* merge server configuration */ - - NULL, /* create location configuration */ - NULL /* merge location configuration */ -}; - - -ngx_module_t ngx_http_quic_module = { - NGX_MODULE_V1, - &ngx_http_quic_module_ctx, /* module context */ - ngx_http_quic_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - NULL, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ - NGX_MODULE_V1_PADDING -}; - - -static ngx_http_variable_t ngx_http_quic_vars[] = { - - { ngx_string("quic"), NULL, ngx_http_variable_quic, 0, 0, 0 }, - - ngx_http_null_variable -}; - -static ngx_str_t ngx_http_quic_salt = ngx_string("ngx_quic"); - - -ngx_int_t -ngx_http_quic_init(ngx_connection_t *c) -{ - uint64_t n; - ngx_quic_conf_t *qcf; - ngx_http_connection_t *hc, *phc; - ngx_http_core_loc_conf_t *clcf; - - hc = c->data; - - hc->ssl = 1; - - if (c->quic == NULL) { - qcf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_quic_module); - - ngx_quic_run(c, qcf); - - return NGX_DONE; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http init quic stream"); - -#if (NGX_HTTP_V3) - if (!hc->addr_conf->http3) -#endif - { - /* Use HTTP/3 General Protocol Error Code 0x101 for finalization */ - - if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { - ngx_quic_finalize_connection(c->quic->parent, 0x101, - "unexpected uni stream"); - ngx_http_close_connection(c); - return NGX_DONE; - } - - clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); - - n = c->quic->id >> 2; - - if (n >= clcf->keepalive_requests) { - ngx_quic_finalize_connection(c->quic->parent, 0x101, - "reached maximum number of requests"); - ngx_http_close_connection(c); - return NGX_DONE; - } - - if (ngx_current_msec - c->quic->parent->start_time - > clcf->keepalive_time) - { - ngx_quic_finalize_connection(c->quic->parent, 0x101, - "reached maximum time for requests"); - ngx_http_close_connection(c); - return NGX_DONE; - } - } - - phc = ngx_http_quic_get_connection(c); - - if (phc->ssl_servername) { - hc->ssl_servername = phc->ssl_servername; - hc->conf_ctx = phc->conf_ctx; - - clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); - ngx_set_connection_log(c, clcf->error_log); - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_variable_quic(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data) -{ - if (r->connection->quic) { - - v->len = 4; - v->valid = 1; - v->no_cacheable = 1; - v->not_found = 0; - v->data = (u_char *) "quic"; - return NGX_OK; - } - - v->not_found = 1; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_quic_add_variables(ngx_conf_t *cf) -{ - ngx_http_variable_t *var, *v; - - for (v = ngx_http_quic_vars; v->name.len; v++) { - var = ngx_http_add_variable(cf, &v->name, v->flags); - if (var == NULL) { - return NGX_ERROR; - } - - var->get_handler = v->get_handler; - var->data = v->data; - } - - return NGX_OK; -} - - -static void * -ngx_http_quic_create_srv_conf(ngx_conf_t *cf) -{ - ngx_quic_conf_t *conf; - - conf = ngx_pcalloc(cf->pool, sizeof(ngx_quic_conf_t)); - if (conf == NULL) { - return NULL; - } - - /* - * set by ngx_pcalloc(): - * - * conf->tp.original_dcid = { 0, NULL }; - * conf->tp.initial_scid = { 0, NULL }; - * conf->tp.retry_scid = { 0, NULL }; - * conf->tp.sr_token = { 0 } - * conf->tp.sr_enabled = 0 - * conf->tp.preferred_address = NULL - * conf->host_key = { 0, NULL } - * cong->stream_reject_code_uni = 0; - */ - - conf->tp.max_idle_timeout = NGX_CONF_UNSET_MSEC; - conf->tp.max_ack_delay = NGX_CONF_UNSET_MSEC; - conf->tp.max_udp_payload_size = NGX_CONF_UNSET_SIZE; - conf->tp.initial_max_data = NGX_CONF_UNSET_SIZE; - conf->tp.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_SIZE; - conf->tp.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_SIZE; - conf->tp.initial_max_stream_data_uni = NGX_CONF_UNSET_SIZE; - conf->tp.initial_max_streams_bidi = NGX_CONF_UNSET_UINT; - conf->tp.initial_max_streams_uni = NGX_CONF_UNSET_UINT; - conf->tp.ack_delay_exponent = NGX_CONF_UNSET_UINT; - conf->tp.disable_active_migration = NGX_CONF_UNSET; - conf->tp.active_connection_id_limit = NGX_CONF_UNSET_UINT; - - conf->retry = NGX_CONF_UNSET; - conf->gso_enabled = NGX_CONF_UNSET; -#if (NGX_HTTP_V3) - conf->stream_close_code = NGX_HTTP_V3_ERR_NO_ERROR; - conf->stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; -#endif - - return conf; -} - - -static char * -ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) -{ - ngx_quic_conf_t *prev = parent; - ngx_quic_conf_t *conf = child; - - ngx_http_ssl_srv_conf_t *sscf; - - ngx_conf_merge_msec_value(conf->tp.max_idle_timeout, - prev->tp.max_idle_timeout, 60000); - - ngx_conf_merge_msec_value(conf->tp.max_ack_delay, - prev->tp.max_ack_delay, - NGX_QUIC_DEFAULT_MAX_ACK_DELAY); - - ngx_conf_merge_size_value(conf->tp.max_udp_payload_size, - prev->tp.max_udp_payload_size, - NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); - - ngx_conf_merge_size_value(conf->tp.initial_max_data, - prev->tp.initial_max_data, - 16 * NGX_QUIC_STREAM_BUFSIZE); - - ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_bidi_local, - prev->tp.initial_max_stream_data_bidi_local, - NGX_QUIC_STREAM_BUFSIZE); - - ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_bidi_remote, - prev->tp.initial_max_stream_data_bidi_remote, - NGX_QUIC_STREAM_BUFSIZE); - - ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_uni, - prev->tp.initial_max_stream_data_uni, - NGX_QUIC_STREAM_BUFSIZE); - - ngx_conf_merge_uint_value(conf->tp.initial_max_streams_bidi, - prev->tp.initial_max_streams_bidi, 16); - - ngx_conf_merge_uint_value(conf->tp.initial_max_streams_uni, - prev->tp.initial_max_streams_uni, 3); - - ngx_conf_merge_uint_value(conf->tp.ack_delay_exponent, - prev->tp.ack_delay_exponent, - NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); - - ngx_conf_merge_value(conf->tp.disable_active_migration, - prev->tp.disable_active_migration, 0); - - ngx_conf_merge_uint_value(conf->tp.active_connection_id_limit, - prev->tp.active_connection_id_limit, 2); - - ngx_conf_merge_value(conf->retry, prev->retry, 0); - ngx_conf_merge_value(conf->gso_enabled, prev->gso_enabled, 0); - - ngx_conf_merge_str_value(conf->host_key, prev->host_key, ""); - - if (conf->host_key.len == 0) { - - conf->host_key.len = NGX_QUIC_DEFAULT_HOST_KEY_LEN; - conf->host_key.data = ngx_palloc(cf->pool, conf->host_key.len); - if (conf->host_key.data == NULL) { - return NGX_CONF_ERROR; - } - - if (RAND_bytes(conf->host_key.data, NGX_QUIC_DEFAULT_HOST_KEY_LEN) - <= 0) - { - return NGX_CONF_ERROR; - } - } - - if (ngx_quic_derive_key(cf->log, "av_token_key", - &conf->host_key, &ngx_http_quic_salt, - conf->av_token_key, NGX_QUIC_AV_KEY_LEN) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - - if (ngx_quic_derive_key(cf->log, "sr_token_key", - &conf->host_key, &ngx_http_quic_salt, - conf->sr_token_key, NGX_QUIC_SR_KEY_LEN) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - - sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); - conf->ssl = &sscf->ssl; - - return NGX_CONF_OK; -} - - -static char * -ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, void *data) -{ - ngx_msec_t *sp = data; - - if (*sp >= 16384) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_max_ack_delay\" must be less than 16384"); - - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} - - -static char * -ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data) -{ - size_t *sp = data; - - if (*sp < NGX_QUIC_MIN_INITIAL_SIZE - || *sp > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_max_udp_payload_size\" must be between " - "%d and %d", - NGX_QUIC_MIN_INITIAL_SIZE, - NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); - - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} - - -static char * -ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_quic_conf_t *qcf = conf; - - u_char *buf; - size_t size; - ssize_t n; - ngx_str_t *value; - ngx_file_t file; - ngx_file_info_t fi; - - if (qcf->host_key.len) { - return "is duplicate"; - } - - buf = NULL; -#if (NGX_SUPPRESS_WARN) - size = 0; -#endif - - value = cf->args->elts; - - if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) { - return NGX_CONF_ERROR; - } - - ngx_memzero(&file, sizeof(ngx_file_t)); - file.name = value[1]; - file.log = cf->log; - - file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); - - if (file.fd == NGX_INVALID_FILE) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, - ngx_open_file_n " \"%V\" failed", &file.name); - return NGX_CONF_ERROR; - } - - if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { - ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, - ngx_fd_info_n " \"%V\" failed", &file.name); - goto failed; - } - - size = ngx_file_size(&fi); - - if (size == 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"%V\" zero key size", &file.name); - goto failed; - } - - buf = ngx_pnalloc(cf->pool, size); - if (buf == NULL) { - goto failed; - } - - n = ngx_read_file(&file, buf, size, 0); - - if (n == NGX_ERROR) { - ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, - ngx_read_file_n " \"%V\" failed", &file.name); - goto failed; - } - - if ((size_t) n != size) { - ngx_conf_log_error(NGX_LOG_CRIT, cf, 0, - ngx_read_file_n " \"%V\" returned only " - "%z bytes instead of %uz", &file.name, n, size); - goto failed; - } - - qcf->host_key.data = buf; - qcf->host_key.len = n; - - if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, - ngx_close_file_n " \"%V\" failed", &file.name); - } - - return NGX_CONF_OK; - -failed: - - if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, - ngx_close_file_n " \"%V\" failed", &file.name); - } - - if (buf) { - ngx_explicit_memzero(buf, size); - } - - return NGX_CONF_ERROR; -} diff --git a/src/http/modules/ngx_http_quic_module.h b/src/http/modules/ngx_http_quic_module.h deleted file mode 100644 index 3b96fcc47..000000000 --- a/src/http/modules/ngx_http_quic_module.h +++ /dev/null @@ -1,28 +0,0 @@ - -/* - * Copyright (C) Nginx, Inc. - * Copyright (C) Roman Arutyunyan - */ - - -#ifndef _NGX_HTTP_QUIC_H_INCLUDED_ -#define _NGX_HTTP_QUIC_H_INCLUDED_ - - -#include -#include -#include - - -#define NGX_HTTP_QUIC_ALPN_PROTO "\x0Ahq-interop" -#define NGX_HTTP_QUIC_ALPN_DRAFT_FMT "\x05hq-%02uD" - - -#define ngx_http_quic_get_connection(c) \ - ((ngx_http_connection_t *) (c)->quic->parent->data) - - -ngx_int_t ngx_http_quic_init(ngx_connection_t *c); - - -#endif /* _NGX_HTTP_QUIC_H_INCLUDED_ */ diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index afeb68462..3af21178b 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -416,7 +416,7 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { -#if (NGX_HTTP_QUIC) +#if (NGX_HTTP_V3) const char *fmt; #endif unsigned int srvlen; @@ -424,10 +424,10 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, #if (NGX_DEBUG) unsigned int i; #endif -#if (NGX_HTTP_V2 || NGX_HTTP_QUIC) +#if (NGX_HTTP_V2 || NGX_HTTP_V3) ngx_http_connection_t *hc; #endif -#if (NGX_HTTP_V2 || NGX_HTTP_QUIC || NGX_DEBUG) +#if (NGX_HTTP_V2 || NGX_HTTP_V3 || NGX_DEBUG) ngx_connection_t *c; c = ngx_ssl_get_connection(ssl_conn); @@ -441,7 +441,7 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, } #endif -#if (NGX_HTTP_V2 || NGX_HTTP_QUIC) +#if (NGX_HTTP_V2 || NGX_HTTP_V3) hc = c->data; #endif @@ -451,17 +451,14 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, srvlen = sizeof(NGX_HTTP_V2_ALPN_PROTO NGX_HTTP_ALPN_PROTOS) - 1; } else #endif -#if (NGX_HTTP_QUIC) - if (hc->addr_conf->quic) { #if (NGX_HTTP_V3) + if (hc->addr_conf->quic) { if (hc->addr_conf->http3) { srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO; srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1; fmt = NGX_HTTP_V3_ALPN_DRAFT_FMT; - } else -#endif - { + } else { srv = (unsigned char *) NGX_HTTP_QUIC_ALPN_PROTO; srvlen = sizeof(NGX_HTTP_QUIC_ALPN_PROTO) - 1; fmt = NGX_HTTP_QUIC_ALPN_DRAFT_FMT; diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 908e88103..187d867bb 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1237,13 +1237,11 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #if (NGX_HTTP_SSL) ngx_uint_t ssl; #endif -#if (NGX_HTTP_QUIC) - ngx_uint_t quic; -#endif #if (NGX_HTTP_V2) ngx_uint_t http2; #endif #if (NGX_HTTP_V3) + ngx_uint_t quic; ngx_uint_t http3; #endif @@ -1278,13 +1276,11 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #if (NGX_HTTP_SSL) ssl = lsopt->ssl || addr[i].opt.ssl; #endif -#if (NGX_HTTP_QUIC) - quic = lsopt->quic || addr[i].opt.quic; -#endif #if (NGX_HTTP_V2) http2 = lsopt->http2 || addr[i].opt.http2; #endif #if (NGX_HTTP_V3) + quic = lsopt->quic || addr[i].opt.quic; http3 = lsopt->http3 || addr[i].opt.http3; #endif @@ -1320,13 +1316,11 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #if (NGX_HTTP_SSL) addr[i].opt.ssl = ssl; #endif -#if (NGX_HTTP_QUIC) - addr[i].opt.quic = quic; -#endif #if (NGX_HTTP_V2) addr[i].opt.http2 = http2; #endif #if (NGX_HTTP_V3) + addr[i].opt.quic = quic; addr[i].opt.http3 = http3; #endif @@ -1371,7 +1365,7 @@ ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #endif -#if (NGX_HTTP_QUIC && !defined NGX_QUIC) +#if (NGX_HTTP_V3 && !defined NGX_QUIC) if (lsopt->quic) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, @@ -1841,7 +1835,7 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) ls->wildcard = addr->opt.wildcard; -#if (NGX_HTTP_QUIC) +#if (NGX_HTTP_V3) ls->quic = addr->opt.quic; #endif @@ -1874,13 +1868,11 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, #if (NGX_HTTP_SSL) addrs[i].conf.ssl = addr[i].opt.ssl; #endif -#if (NGX_HTTP_QUIC) - addrs[i].conf.quic = addr[i].opt.quic; -#endif #if (NGX_HTTP_V2) addrs[i].conf.http2 = addr[i].opt.http2; #endif #if (NGX_HTTP_V3) + addrs[i].conf.quic = addr[i].opt.quic; addrs[i].conf.http3 = addr[i].opt.http3; #endif addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; @@ -1945,13 +1937,11 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, #if (NGX_HTTP_SSL) addrs6[i].conf.ssl = addr[i].opt.ssl; #endif -#if (NGX_HTTP_QUIC) - addrs6[i].conf.quic = addr[i].opt.quic; -#endif #if (NGX_HTTP_V2) addrs6[i].conf.http2 = addr[i].opt.http2; #endif #if (NGX_HTTP_V3) + addrs6[i].conf.quic = addr[i].opt.quic; addrs6[i].conf.http3 = addr[i].opt.http3; #endif addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 708defebc..5ac65c859 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -52,9 +52,6 @@ typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r, #if (NGX_HTTP_SSL) #include #endif -#if (NGX_HTTP_QUIC) -#include -#endif struct ngx_http_log_ctx_s { diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 19bc75136..1e8e27802 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -4097,14 +4097,14 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } if (ngx_strcmp(value[n].data, "quic") == 0) { -#if (NGX_HTTP_QUIC) +#if (NGX_HTTP_V3) lsopt.quic = 1; lsopt.type = SOCK_DGRAM; continue; #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the \"quic\" parameter requires " - "ngx_http_quic_module"); + "ngx_http_v3_module"); return NGX_CONF_ERROR; #endif } @@ -4232,7 +4232,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } #endif -#if (NGX_HTTP_QUIC) +#if (NGX_HTTP_V3) if (lsopt.ssl && lsopt.quic) { return "\"ssl\" parameter is incompatible with \"quic\""; } diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 7125e7dd1..0acedb6e9 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -316,14 +316,6 @@ ngx_http_init_connection(ngx_connection_t *c) c->log_error = NGX_ERROR_INFO; -#if (NGX_HTTP_QUIC) - if (hc->addr_conf->quic) { - if (ngx_http_quic_init(c) == NGX_DONE) { - return; - } - } -#endif - rev = c->read; rev->handler = ngx_http_wait_request_handler; c->write->handler = ngx_http_empty_handler; @@ -335,7 +327,7 @@ ngx_http_init_connection(ngx_connection_t *c) #endif #if (NGX_HTTP_V3) - if (hc->addr_conf->http3) { + if (hc->addr_conf->quic) { ngx_http_v3_init(c); return; } @@ -2746,7 +2738,7 @@ ngx_http_finalize_connection(ngx_http_request_t *r) } #endif -#if (NGX_HTTP_QUIC) +#if (NGX_HTTP_V3) if (r->connection->quic) { ngx_http_close_request(r, 0); return; @@ -2967,7 +2959,7 @@ ngx_http_test_reading(ngx_http_request_t *r) #endif -#if (NGX_HTTP_QUIC) +#if (NGX_HTTP_V3) if (c->quic) { if (c->read->error) { @@ -3732,7 +3724,7 @@ ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc) log->action = "closing request"; if (r->connection->timedout -#if (NGX_HTTP_QUIC) +#if (NGX_HTTP_V3) && r->connection->quic == NULL #endif ) @@ -3810,7 +3802,7 @@ ngx_http_close_connection(ngx_connection_t *c) #endif #if (NGX_HTTP_V3) - if (ngx_http_v3_connection(c)) { + if (c->quic) { ngx_http_v3_reset_connection(c); } #endif diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index d27a53ea4..670fc022f 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -525,7 +525,7 @@ ngx_http_upstream_init(ngx_http_request_t *r) } #endif -#if (NGX_HTTP_QUIC) +#if (NGX_HTTP_V3) if (c->quic) { ngx_http_upstream_init_request(r); return; @@ -1365,7 +1365,7 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, } #endif -#if (NGX_HTTP_QUIC) +#if (NGX_HTTP_V3) if (c->quic) { if (c->write->error) { diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 758cbd1af..bee073e1d 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -22,6 +22,9 @@ #define NGX_HTTP_V3_ALPN_PROTO "\x02h3" #define NGX_HTTP_V3_ALPN_DRAFT_FMT "\x05h3-%02uD" +#define NGX_HTTP_QUIC_ALPN_PROTO "\x0Ahq-interop" +#define NGX_HTTP_QUIC_ALPN_DRAFT_FMT "\x05hq-%02uD" + #define NGX_HTTP_V3_VARLEN_INT_LEN 4 #define NGX_HTTP_V3_PREFIX_INT_LEN 11 @@ -74,6 +77,9 @@ #define NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR 0x202 +#define ngx_http_quic_get_connection(c) \ + ((ngx_http_connection_t *) (c)->quic->parent->data) + #define ngx_http_v3_get_session(c) ngx_http_quic_get_connection(c)->v3_session #define ngx_http_v3_get_module_loc_conf(c, module) \ @@ -90,15 +96,13 @@ #define ngx_http_v3_shutdown_connection(c, code, reason) \ ngx_quic_shutdown_connection(c->quic->parent, code, reason) -#define ngx_http_v3_connection(c) \ - ((c)->quic ? ngx_http_quic_get_connection(c)->addr_conf->http3 : 0) - typedef struct { size_t max_table_capacity; ngx_uint_t max_blocked_streams; ngx_uint_t max_concurrent_pushes; ngx_uint_t max_uni_streams; + ngx_quic_conf_t quic; } ngx_http_v3_srv_conf_t; diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 4b59b097c..14e24d29a 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -10,15 +10,34 @@ #include +static ngx_int_t ngx_http_v3_variable_quic(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); 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 char *ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, + void *data); +static char *ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, + void *data); +static char *ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static void *ngx_http_v3_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_v3_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static char *ngx_http_v3_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_conf_post_t ngx_http_quic_max_ack_delay_post = + { ngx_http_quic_max_ack_delay }; +static ngx_conf_post_t ngx_http_quic_max_udp_payload_size_post = + { ngx_http_quic_max_udp_payload_size }; +static ngx_conf_num_bounds_t ngx_http_quic_ack_delay_exponent_bounds = + { ngx_conf_check_num_bounds, 0, 20 }; +static ngx_conf_num_bounds_t ngx_http_quic_active_connection_id_limit_bounds = + { ngx_conf_check_num_bounds, 2, -1 }; + + static ngx_command_t ngx_http_v3_commands[] = { { ngx_string("http3_max_table_capacity"), @@ -63,12 +82,119 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_loc_conf_t, push_preload), NULL }, + { 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.tp.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.tp.max_ack_delay), + &ngx_http_quic_max_ack_delay_post }, + + { ngx_string("quic_max_udp_payload_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.tp.max_udp_payload_size), + &ngx_http_quic_max_udp_payload_size_post }, + + { ngx_string("quic_initial_max_data"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.tp.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_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, + quic.tp.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_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, + quic.tp.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_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.tp.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.tp.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.tp.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.tp.ack_delay_exponent), + &ngx_http_quic_ack_delay_exponent_bounds }, + + { ngx_string("quic_disable_active_migration"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.tp.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.tp.active_connection_id_limit), + &ngx_http_quic_active_connection_id_limit_bounds }, + + { ngx_string("quic_retry"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.retry), + NULL }, + + { ngx_string("quic_gso"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.gso_enabled), + NULL }, + + { ngx_string("quic_host_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_quic_host_key, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + ngx_null_command }; static ngx_http_module_t ngx_http_v3_module_ctx = { - NULL, /* preconfiguration */ + ngx_http_v3_add_variables, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ @@ -98,6 +224,55 @@ ngx_module_t ngx_http_v3_module = { }; +static ngx_http_variable_t ngx_http_v3_vars[] = { + + { ngx_string("quic"), NULL, ngx_http_v3_variable_quic, 0, 0, 0 }, + + ngx_http_null_variable +}; + +static ngx_str_t ngx_http_quic_salt = ngx_string("ngx_quic"); + + +static ngx_int_t +ngx_http_v3_variable_quic(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->connection->quic) { + + v->len = 4; + v->valid = 1; + v->no_cacheable = 1; + v->not_found = 0; + v->data = (u_char *) "quic"; + return NGX_OK; + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_v3_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + static void * ngx_http_v3_create_srv_conf(ngx_conf_t *cf) { @@ -108,11 +283,42 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) return NULL; } + /* + * set by ngx_pcalloc(): + * + * h3scf->quic.tp.original_dcid = { 0, NULL }; + * h3scf->quic.tp.initial_scid = { 0, NULL }; + * h3scf->quic.tp.retry_scid = { 0, NULL }; + * h3scf->quic.tp.sr_token = { 0 } + * h3scf->quic.tp.sr_enabled = 0 + * h3scf->quic.tp.preferred_address = NULL + * h3scf->quic.host_key = { 0, NULL } + * h3scf->quic.stream_reject_code_uni = 0; + */ + h3scf->max_table_capacity = NGX_CONF_UNSET_SIZE; h3scf->max_blocked_streams = NGX_CONF_UNSET_UINT; h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT; h3scf->max_uni_streams = NGX_CONF_UNSET_UINT; + h3scf->quic.tp.max_idle_timeout = NGX_CONF_UNSET_MSEC; + h3scf->quic.tp.max_ack_delay = NGX_CONF_UNSET_MSEC; + h3scf->quic.tp.max_udp_payload_size = NGX_CONF_UNSET_SIZE; + h3scf->quic.tp.initial_max_data = NGX_CONF_UNSET_SIZE; + h3scf->quic.tp.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_SIZE; + h3scf->quic.tp.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_SIZE; + h3scf->quic.tp.initial_max_stream_data_uni = NGX_CONF_UNSET_SIZE; + h3scf->quic.tp.initial_max_streams_bidi = NGX_CONF_UNSET_UINT; + h3scf->quic.tp.initial_max_streams_uni = NGX_CONF_UNSET_UINT; + h3scf->quic.tp.ack_delay_exponent = NGX_CONF_UNSET_UINT; + h3scf->quic.tp.disable_active_migration = NGX_CONF_UNSET; + h3scf->quic.tp.active_connection_id_limit = NGX_CONF_UNSET_UINT; + + h3scf->quic.retry = NGX_CONF_UNSET; + h3scf->quic.gso_enabled = NGX_CONF_UNSET; + h3scf->quic.stream_close_code = NGX_HTTP_V3_ERR_NO_ERROR; + h3scf->quic.stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; + return h3scf; } @@ -123,6 +329,8 @@ 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_http_ssl_srv_conf_t *sscf; + ngx_conf_merge_size_value(conf->max_table_capacity, prev->max_table_capacity, 16384); @@ -135,7 +343,228 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->max_uni_streams, prev->max_uni_streams, 3); + ngx_conf_merge_msec_value(conf->quic.tp.max_idle_timeout, + prev->quic.tp.max_idle_timeout, 60000); + + ngx_conf_merge_msec_value(conf->quic.tp.max_ack_delay, + prev->quic.tp.max_ack_delay, + NGX_QUIC_DEFAULT_MAX_ACK_DELAY); + + ngx_conf_merge_size_value(conf->quic.tp.max_udp_payload_size, + prev->quic.tp.max_udp_payload_size, + NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); + + ngx_conf_merge_size_value(conf->quic.tp.initial_max_data, + prev->quic.tp.initial_max_data, + 16 * NGX_QUIC_STREAM_BUFSIZE); + + ngx_conf_merge_size_value(conf->quic.tp.initial_max_stream_data_bidi_local, + prev->quic.tp.initial_max_stream_data_bidi_local, + NGX_QUIC_STREAM_BUFSIZE); + + ngx_conf_merge_size_value(conf->quic.tp.initial_max_stream_data_bidi_remote, + prev->quic.tp.initial_max_stream_data_bidi_remote, + NGX_QUIC_STREAM_BUFSIZE); + + ngx_conf_merge_size_value(conf->quic.tp.initial_max_stream_data_uni, + prev->quic.tp.initial_max_stream_data_uni, + NGX_QUIC_STREAM_BUFSIZE); + + ngx_conf_merge_uint_value(conf->quic.tp.initial_max_streams_bidi, + prev->quic.tp.initial_max_streams_bidi, 16); + + ngx_conf_merge_uint_value(conf->quic.tp.initial_max_streams_uni, + prev->quic.tp.initial_max_streams_uni, 3); + + ngx_conf_merge_uint_value(conf->quic.tp.ack_delay_exponent, + prev->quic.tp.ack_delay_exponent, + NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); + + ngx_conf_merge_value(conf->quic.tp.disable_active_migration, + prev->quic.tp.disable_active_migration, 0); + + ngx_conf_merge_uint_value(conf->quic.tp.active_connection_id_limit, + prev->quic.tp.active_connection_id_limit, 2); + + ngx_conf_merge_value(conf->quic.retry, prev->quic.retry, 0); + ngx_conf_merge_value(conf->quic.gso_enabled, prev->quic.gso_enabled, 0); + + ngx_conf_merge_str_value(conf->quic.host_key, prev->quic.host_key, ""); + + if (conf->quic.host_key.len == 0) { + + conf->quic.host_key.len = NGX_QUIC_DEFAULT_HOST_KEY_LEN; + conf->quic.host_key.data = ngx_palloc(cf->pool, + conf->quic.host_key.len); + if (conf->quic.host_key.data == NULL) { + return NGX_CONF_ERROR; + } + + if (RAND_bytes(conf->quic.host_key.data, NGX_QUIC_DEFAULT_HOST_KEY_LEN) + <= 0) + { + return NGX_CONF_ERROR; + } + } + + if (ngx_quic_derive_key(cf->log, "av_token_key", + &conf->quic.host_key, &ngx_http_quic_salt, + conf->quic.av_token_key, NGX_QUIC_AV_KEY_LEN) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_quic_derive_key(cf->log, "sr_token_key", + &conf->quic.host_key, &ngx_http_quic_salt, + conf->quic.sr_token_key, NGX_QUIC_SR_KEY_LEN) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); + conf->quic.ssl = &sscf->ssl; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, void *data) +{ + ngx_msec_t *sp = data; + + if (*sp >= 16384) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"quic_max_ack_delay\" must be less than 16384"); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp < NGX_QUIC_MIN_INITIAL_SIZE + || *sp > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"quic_max_udp_payload_size\" must be between " + "%d and %d", + NGX_QUIC_MIN_INITIAL_SIZE, + NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_v3_srv_conf_t *h3scf = conf; + + u_char *buf; + size_t size; + ssize_t n; + ngx_str_t *value; + ngx_file_t file; + ngx_file_info_t fi; + ngx_quic_conf_t *qcf; + + qcf = &h3scf->quic; + + if (qcf->host_key.len) { + return "is duplicate"; + } + + buf = NULL; +#if (NGX_SUPPRESS_WARN) + size = 0; +#endif + + value = cf->args->elts; + + if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&file, sizeof(ngx_file_t)); + file.name = value[1]; + file.log = cf->log; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + + if (file.fd == NGX_INVALID_FILE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + ngx_open_file_n " \"%V\" failed", &file.name); + return NGX_CONF_ERROR; + } + + if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_fd_info_n " \"%V\" failed", &file.name); + goto failed; + } + + size = ngx_file_size(&fi); + + if (size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" zero key size", &file.name); + goto failed; + } + + buf = ngx_pnalloc(cf->pool, size); + if (buf == NULL) { + goto failed; + } + + n = ngx_read_file(&file, buf, size, 0); + + if (n == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_read_file_n " \"%V\" failed", &file.name); + goto failed; + } + + if ((size_t) n != size) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, 0, + ngx_read_file_n " \"%V\" returned only " + "%z bytes instead of %uz", &file.name, n, size); + goto failed; + } + + qcf->host_key.data = buf; + qcf->host_key.len = n; + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%V\" failed", &file.name); + } + return NGX_CONF_OK; + +failed: + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%V\" failed", &file.name); + } + + if (buf) { + ngx_explicit_memzero(buf, size); + } + + return NGX_CONF_ERROR; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index e0c3a71ba..a4b570370 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,6 +10,8 @@ #include +static void ngx_http_v3_init_hq_stream(ngx_connection_t *c); +static void ngx_http_v3_init_request_stream(ngx_connection_t *c); static void ngx_http_v3_wait_request_handler(ngx_event_t *rev); static void ngx_http_v3_cleanup_request(void *data); static void ngx_http_v3_process_request(ngx_event_t *rev); @@ -53,24 +55,130 @@ static const struct { void ngx_http_v3_init(ngx_connection_t *c) +{ + ngx_http_connection_t *hc, *phc; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_core_loc_conf_t *clcf; + + hc = c->data; + + hc->ssl = 1; + + if (c->quic == NULL) { + h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); + + ngx_quic_run(c, &h3scf->quic); + + return; + } + + phc = ngx_http_quic_get_connection(c); + + if (phc->ssl_servername) { + hc->ssl_servername = phc->ssl_servername; + hc->conf_ctx = phc->conf_ctx; + + clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + ngx_set_connection_log(c, clcf->error_log); + } + + if (!hc->addr_conf->http3) { + ngx_http_v3_init_hq_stream(c); + return; + } + + if (ngx_http_v3_init_session(c) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { + ngx_http_v3_init_uni_stream(c); + + } else { + ngx_http_v3_init_request_stream(c); + } +} + + +static void +ngx_http_v3_init_hq_stream(ngx_connection_t *c) { uint64_t n; ngx_event_t *rev; ngx_http_connection_t *hc; - ngx_http_v3_session_t *h3c; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; - if (ngx_http_v3_init_session(c) != NGX_OK) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init hq stream"); + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_active, 1); +#endif + + hc = c->data; + + /* Use HTTP/3 General Protocol Error Code 0x101 for finalization */ + + if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { + ngx_quic_finalize_connection(c->quic->parent, + NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR, + "unexpected uni stream"); ngx_http_close_connection(c); return; } - if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { - ngx_http_v3_init_uni_stream(c); + clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + + n = c->quic->id >> 2; + + if (n >= clcf->keepalive_requests) { + ngx_quic_finalize_connection(c->quic->parent, + NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR, + "reached maximum number of requests"); + ngx_http_close_connection(c); + return; + } + + if (ngx_current_msec - c->quic->parent->start_time + > clcf->keepalive_time) + { + ngx_quic_finalize_connection(c->quic->parent, + NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR, + "reached maximum time for requests"); + ngx_http_close_connection(c); return; } + rev = c->read; + + if (rev->ready) { + rev->handler(rev); + return; + } + + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); + + ngx_add_timer(rev, cscf->client_header_timeout); + ngx_reusable_connection(c, 1); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } +} + + +static void +ngx_http_v3_init_request_stream(ngx_connection_t *c) +{ + uint64_t n; + ngx_event_t *rev; + ngx_http_connection_t *hc; + ngx_http_v3_session_t *h3c; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init request stream"); #if (NGX_STAT_STUB) @@ -279,8 +387,15 @@ ngx_http_v3_wait_request_handler(ngx_event_t *rev) void ngx_http_v3_reset_connection(ngx_connection_t *c) { + ngx_http_connection_t *hc; ngx_http_v3_srv_conf_t *h3scf; + hc = ngx_http_quic_get_connection(c); + + if (!hc->addr_conf->http3) { + return; + } + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); if (h3scf->max_table_capacity > 0 && !c->read->eof) { -- cgit From d84c1f7885cc898f626057c314cdae4047c5d513 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Sat, 4 Dec 2021 10:52:55 +0300 Subject: HTTP/3: http3_hq directive and NGX_HTTP_V3_HQ macro. Listen quic parameter is no longer supported. --- src/http/modules/ngx_http_ssl_module.c | 41 +++++++++++++++++++--------------- src/http/ngx_http.c | 11 +++------ src/http/ngx_http_core_module.c | 26 +-------------------- src/http/ngx_http_core_module.h | 1 - src/http/ngx_http_request.c | 2 +- src/http/v3/ngx_http_v3.h | 7 ++++-- src/http/v3/ngx_http_v3_module.c | 16 +++++++++++++ src/http/v3/ngx_http_v3_request.c | 24 ++++++++++++-------- 8 files changed, 64 insertions(+), 64 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 3af21178b..f43e51eac 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -417,18 +417,21 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, void *arg) { #if (NGX_HTTP_V3) - const char *fmt; + const char *fmt; #endif - unsigned int srvlen; - unsigned char *srv; + unsigned int srvlen; + unsigned char *srv; #if (NGX_DEBUG) - unsigned int i; + unsigned int i; #endif #if (NGX_HTTP_V2 || NGX_HTTP_V3) - ngx_http_connection_t *hc; + ngx_http_connection_t *hc; +#endif +#if (NGX_HTTP_V3 && NGX_HTTP_V3_HQ) + ngx_http_v3_srv_conf_t *h3scf; #endif #if (NGX_HTTP_V2 || NGX_HTTP_V3 || NGX_DEBUG) - ngx_connection_t *c; + ngx_connection_t *c; c = ngx_ssl_get_connection(ssl_conn); #endif @@ -452,16 +455,21 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, } else #endif #if (NGX_HTTP_V3) - if (hc->addr_conf->quic) { - if (hc->addr_conf->http3) { + if (hc->addr_conf->http3) { + +#if (NGX_HTTP_V3_HQ) + h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); + + if (h3scf->hq) { + srv = (unsigned char *) NGX_HTTP_V3_HQ_ALPN_PROTO; + srvlen = sizeof(NGX_HTTP_V3_HQ_ALPN_PROTO) - 1; + fmt = NGX_HTTP_V3_HQ_ALPN_DRAFT_FMT; + } else +#endif + { srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO; srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1; fmt = NGX_HTTP_V3_ALPN_DRAFT_FMT; - - } else { - srv = (unsigned char *) NGX_HTTP_QUIC_ALPN_PROTO; - srvlen = sizeof(NGX_HTTP_QUIC_ALPN_PROTO) - 1; - fmt = NGX_HTTP_QUIC_ALPN_DRAFT_FMT; } /* QUIC draft */ @@ -1317,16 +1325,13 @@ ngx_http_ssl_init(ngx_conf_t *cf) addr = port[p].addrs.elts; for (a = 0; a < port[p].addrs.nelts; a++) { - if (!addr[a].opt.ssl && !addr[a].opt.quic) { + if (!addr[a].opt.ssl && !addr[a].opt.http3) { continue; } if (addr[a].opt.http3) { name = "http3"; - } else if (addr[a].opt.quic) { - name = "quic"; - } else { name = "ssl"; } @@ -1336,7 +1341,7 @@ ngx_http_ssl_init(ngx_conf_t *cf) if (sscf->certificates) { - if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) { + if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "\"ssl_protocols\" must enable TLSv1.3 for " "the \"listen ... %s\" directive in %s:%ui", diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 187d867bb..4886a88bf 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1241,7 +1241,6 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, ngx_uint_t http2; #endif #if (NGX_HTTP_V3) - ngx_uint_t quic; ngx_uint_t http3; #endif @@ -1280,7 +1279,6 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, http2 = lsopt->http2 || addr[i].opt.http2; #endif #if (NGX_HTTP_V3) - quic = lsopt->quic || addr[i].opt.quic; http3 = lsopt->http3 || addr[i].opt.http3; #endif @@ -1320,7 +1318,6 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, addr[i].opt.http2 = http2; #endif #if (NGX_HTTP_V3) - addr[i].opt.quic = quic; addr[i].opt.http3 = http3; #endif @@ -1367,10 +1364,10 @@ ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #if (NGX_HTTP_V3 && !defined NGX_QUIC) - if (lsopt->quic) { + if (lsopt->http3) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "nginx was built with OpenSSL that lacks QUIC " - "support, QUIC is not enabled for %V", + "support, HTTP/3 is not enabled for %V", &lsopt->addr_text); } @@ -1836,7 +1833,7 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) ls->wildcard = addr->opt.wildcard; #if (NGX_HTTP_V3) - ls->quic = addr->opt.quic; + ls->quic = addr->opt.http3; #endif return ls; @@ -1872,7 +1869,6 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, addrs[i].conf.http2 = addr[i].opt.http2; #endif #if (NGX_HTTP_V3) - addrs[i].conf.quic = addr[i].opt.quic; addrs[i].conf.http3 = addr[i].opt.http3; #endif addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; @@ -1941,7 +1937,6 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, addrs6[i].conf.http2 = addr[i].opt.http2; #endif #if (NGX_HTTP_V3) - addrs6[i].conf.quic = addr[i].opt.quic; addrs6[i].conf.http3 = addr[i].opt.http3; #endif addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 1e8e27802..12f7bfb27 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -4096,22 +4096,8 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #endif } - if (ngx_strcmp(value[n].data, "quic") == 0) { -#if (NGX_HTTP_V3) - lsopt.quic = 1; - lsopt.type = SOCK_DGRAM; - continue; -#else - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the \"quic\" parameter requires " - "ngx_http_v3_module"); - return NGX_CONF_ERROR; -#endif - } - if (ngx_strcmp(value[n].data, "http3") == 0) { #if (NGX_HTTP_V3) - lsopt.quic = 1; lsopt.http3 = 1; lsopt.type = SOCK_DGRAM; continue; @@ -4224,20 +4210,10 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } -#if (NGX_HTTP_SSL) - -#if (NGX_HTTP_V3) +#if (NGX_HTTP_SSL && NGX_HTTP_V3) if (lsopt.ssl && lsopt.http3) { return "\"ssl\" parameter is incompatible with \"http3\""; } -#endif - -#if (NGX_HTTP_V3) - if (lsopt.ssl && lsopt.quic) { - return "\"ssl\" parameter is incompatible with \"quic\""; - } -#endif - #endif for (n = 0; n < u.naddrs; n++) { diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index 148696641..822c7d264 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -74,7 +74,6 @@ typedef struct { unsigned bind:1; unsigned wildcard:1; unsigned ssl:1; - unsigned quic:1; unsigned http2:1; unsigned http3:1; #if (NGX_HAVE_INET6) diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 0acedb6e9..e8907c100 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -327,7 +327,7 @@ ngx_http_init_connection(ngx_connection_t *c) #endif #if (NGX_HTTP_V3) - if (hc->addr_conf->quic) { + if (hc->addr_conf->http3) { ngx_http_v3_init(c); return; } diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index bee073e1d..f470b327d 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -22,8 +22,8 @@ #define NGX_HTTP_V3_ALPN_PROTO "\x02h3" #define NGX_HTTP_V3_ALPN_DRAFT_FMT "\x05h3-%02uD" -#define NGX_HTTP_QUIC_ALPN_PROTO "\x0Ahq-interop" -#define NGX_HTTP_QUIC_ALPN_DRAFT_FMT "\x05hq-%02uD" +#define NGX_HTTP_V3_HQ_ALPN_PROTO "\x0Ahq-interop" +#define NGX_HTTP_V3_HQ_ALPN_DRAFT_FMT "\x05hq-%02uD" #define NGX_HTTP_V3_VARLEN_INT_LEN 4 #define NGX_HTTP_V3_PREFIX_INT_LEN 11 @@ -102,6 +102,9 @@ typedef struct { ngx_uint_t max_blocked_streams; ngx_uint_t max_concurrent_pushes; ngx_uint_t max_uni_streams; +#if (NGX_HTTP_V3_HQ) + ngx_flag_t hq; +#endif ngx_quic_conf_t quic; } ngx_http_v3_srv_conf_t; diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 14e24d29a..0b2e59b9a 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -68,6 +68,15 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, max_uni_streams), NULL }, +#if (NGX_HTTP_V3_HQ) + { ngx_string("http3_hq"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, hq), + NULL }, +#endif + { ngx_string("http3_push"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_v3_push, @@ -300,6 +309,9 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) h3scf->max_blocked_streams = NGX_CONF_UNSET_UINT; h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT; h3scf->max_uni_streams = NGX_CONF_UNSET_UINT; +#if (NGX_HTTP_V3_HQ) + h3scf->hq = NGX_CONF_UNSET; +#endif h3scf->quic.tp.max_idle_timeout = NGX_CONF_UNSET_MSEC; h3scf->quic.tp.max_ack_delay = NGX_CONF_UNSET_MSEC; @@ -343,6 +355,10 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->max_uni_streams, prev->max_uni_streams, 3); +#if (NGX_HTTP_V3_HQ) + ngx_conf_merge_value(conf->hq, prev->hq, 0); +#endif + ngx_conf_merge_msec_value(conf->quic.tp.max_idle_timeout, prev->quic.tp.max_idle_timeout, 60000); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index a4b570370..7fce688aa 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,7 +10,9 @@ #include +#if (NGX_HTTP_V3_HQ) static void ngx_http_v3_init_hq_stream(ngx_connection_t *c); +#endif static void ngx_http_v3_init_request_stream(ngx_connection_t *c); static void ngx_http_v3_wait_request_handler(ngx_event_t *rev); static void ngx_http_v3_cleanup_request(void *data); @@ -64,11 +66,10 @@ ngx_http_v3_init(ngx_connection_t *c) hc->ssl = 1; - if (c->quic == NULL) { - h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); + h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); + if (c->quic == NULL) { ngx_quic_run(c, &h3scf->quic); - return; } @@ -82,10 +83,12 @@ ngx_http_v3_init(ngx_connection_t *c) ngx_set_connection_log(c, clcf->error_log); } - if (!hc->addr_conf->http3) { +#if (NGX_HTTP_V3_HQ) + if (h3scf->hq) { ngx_http_v3_init_hq_stream(c); return; } +#endif if (ngx_http_v3_init_session(c) != NGX_OK) { ngx_http_close_connection(c); @@ -101,6 +104,8 @@ ngx_http_v3_init(ngx_connection_t *c) } +#if (NGX_HTTP_V3_HQ) + static void ngx_http_v3_init_hq_stream(ngx_connection_t *c) { @@ -168,6 +173,8 @@ ngx_http_v3_init_hq_stream(ngx_connection_t *c) } } +#endif + static void ngx_http_v3_init_request_stream(ngx_connection_t *c) @@ -387,16 +394,15 @@ ngx_http_v3_wait_request_handler(ngx_event_t *rev) void ngx_http_v3_reset_connection(ngx_connection_t *c) { - ngx_http_connection_t *hc; ngx_http_v3_srv_conf_t *h3scf; - hc = ngx_http_quic_get_connection(c); + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - if (!hc->addr_conf->http3) { +#if (NGX_HTTP_V3_HQ) + if (h3scf->hq) { return; } - - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); +#endif if (h3scf->max_table_capacity > 0 && !c->read->eof) { (void) ngx_http_v3_send_cancel_stream(c, c->quic->id); -- cgit From 835854520a07adf6e3bedfad486a92cecdcd33ac Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 1 Dec 2021 11:02:17 +0300 Subject: HTTP/3: $http3 variable. A new variable $http3 is added. The variable equals to "h3" for HTTP/3 connections, "hq" for hq connections and is an empty string otherwise. The variable $quic is eliminated. The new variable is similar to $http2 variable. --- src/http/v3/ngx_http_v3_module.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 0b2e59b9a..697eaa4e9 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -10,7 +10,7 @@ #include -static ngx_int_t ngx_http_v3_variable_quic(ngx_http_request_t *r, +static ngx_int_t ngx_http_v3_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); static void *ngx_http_v3_create_srv_conf(ngx_conf_t *cf); @@ -235,7 +235,7 @@ ngx_module_t ngx_http_v3_module = { static ngx_http_variable_t ngx_http_v3_vars[] = { - { ngx_string("quic"), NULL, ngx_http_v3_variable_quic, 0, 0, 0 }, + { ngx_string("http3"), NULL, ngx_http_v3_variable, 0, 0, 0 }, ngx_http_null_variable }; @@ -244,20 +244,38 @@ static ngx_str_t ngx_http_quic_salt = ngx_string("ngx_quic"); static ngx_int_t -ngx_http_v3_variable_quic(ngx_http_request_t *r, +ngx_http_v3_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { if (r->connection->quic) { +#if (NGX_HTTP_V3_HQ) + + ngx_http_v3_srv_conf_t *h3scf; + + h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); - v->len = 4; + if (h3scf->hq) { + v->len = sizeof("hq") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "hq"; + + return NGX_OK; + } + +#endif + + v->len = sizeof("h3") - 1; v->valid = 1; - v->no_cacheable = 1; + v->no_cacheable = 0; v->not_found = 0; - v->data = (u_char *) "quic"; + v->data = (u_char *) "h3"; + return NGX_OK; } - v->not_found = 1; + *v = ngx_http_variable_null_value; return NGX_OK; } -- cgit From 0791b508807eac65681c9c33d27acece67a9a421 Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Mon, 6 Dec 2021 15:19:54 +0300 Subject: QUIC: simplified configuration. Directives that set transport parameters are removed from the configuration. Corresponding values are derived from the quic configuration or initialized to default. Whenever possible, quic configuration parameters are taken from higher-level protocol settings, i.e. HTTP/3. --- src/http/v3/ngx_http_v3.h | 5 +- src/http/v3/ngx_http_v3_module.c | 230 ++++++-------------------------------- src/http/v3/ngx_http_v3_request.c | 3 +- src/http/v3/ngx_http_v3_streams.c | 5 +- 4 files changed, 41 insertions(+), 202 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index f470b327d..91fa19f10 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -45,6 +45,8 @@ #define NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE 0x06 #define NGX_HTTP_V3_PARAM_BLOCKED_STREAMS 0x07 +#define NGX_HTTP_V3_MAX_TABLE_CAPACITY 4096 + #define NGX_HTTP_V3_STREAM_CLIENT_CONTROL 0 #define NGX_HTTP_V3_STREAM_SERVER_CONTROL 1 #define NGX_HTTP_V3_STREAM_CLIENT_ENCODER 2 @@ -52,6 +54,7 @@ #define NGX_HTTP_V3_STREAM_CLIENT_DECODER 4 #define NGX_HTTP_V3_STREAM_SERVER_DECODER 5 #define NGX_HTTP_V3_MAX_KNOWN_STREAM 6 +#define NGX_HTTP_V3_MAX_UNI_STREAMS 3 /* HTTP/3 errors */ #define NGX_HTTP_V3_ERR_NO_ERROR 0x100 @@ -101,7 +104,7 @@ typedef struct { size_t max_table_capacity; ngx_uint_t max_blocked_streams; ngx_uint_t max_concurrent_pushes; - ngx_uint_t max_uni_streams; + ngx_uint_t max_concurrent_streams; #if (NGX_HTTP_V3_HQ) ngx_flag_t hq; #endif diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 697eaa4e9..455b613e1 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -16,9 +16,7 @@ static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); 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 char *ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, - void *data); -static char *ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, +static char *ngx_http_quic_mtu(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -28,32 +26,12 @@ static char *ngx_http_v3_merge_loc_conf(ngx_conf_t *cf, void *parent, static char *ngx_http_v3_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static ngx_conf_post_t ngx_http_quic_max_ack_delay_post = - { ngx_http_quic_max_ack_delay }; -static ngx_conf_post_t ngx_http_quic_max_udp_payload_size_post = - { ngx_http_quic_max_udp_payload_size }; -static ngx_conf_num_bounds_t ngx_http_quic_ack_delay_exponent_bounds = - { ngx_conf_check_num_bounds, 0, 20 }; -static ngx_conf_num_bounds_t ngx_http_quic_active_connection_id_limit_bounds = - { ngx_conf_check_num_bounds, 2, -1 }; +static ngx_conf_post_t ngx_http_quic_mtu_post = + { ngx_http_quic_mtu }; static ngx_command_t ngx_http_v3_commands[] = { - { ngx_string("http3_max_table_capacity"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, max_table_capacity), - NULL }, - - { ngx_string("http3_max_blocked_streams"), - 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, max_blocked_streams), - NULL }, - { ngx_string("http3_max_concurrent_pushes"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, @@ -61,11 +39,11 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, max_concurrent_pushes), NULL }, - { ngx_string("http3_max_uni_streams"), + { ngx_string("http3_max_concurrent_streams"), 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, max_uni_streams), + offsetof(ngx_http_v3_srv_conf_t, max_concurrent_streams), NULL }, #if (NGX_HTTP_V3_HQ) @@ -91,92 +69,13 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_loc_conf_t, push_preload), NULL }, - { 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.tp.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.tp.max_ack_delay), - &ngx_http_quic_max_ack_delay_post }, - - { ngx_string("quic_max_udp_payload_size"), + { ngx_string("http3_stream_buffer_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, quic.tp.max_udp_payload_size), - &ngx_http_quic_max_udp_payload_size_post }, - - { ngx_string("quic_initial_max_data"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, quic.tp.initial_max_data), + offsetof(ngx_http_v3_srv_conf_t, quic.stream_buffer_size), NULL }, - { ngx_string("quic_initial_max_stream_data_bidi_local"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, - quic.tp.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_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, - quic.tp.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_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, quic.tp.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.tp.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.tp.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.tp.ack_delay_exponent), - &ngx_http_quic_ack_delay_exponent_bounds }, - - { ngx_string("quic_disable_active_migration"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_flag_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, quic.tp.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.tp.active_connection_id_limit), - &ngx_http_quic_active_connection_id_limit_bounds }, - { ngx_string("quic_retry"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -191,6 +90,13 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, quic.gso_enabled), NULL }, + { ngx_string("quic_mtu"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.mtu), + &ngx_http_quic_mtu_post }, + { ngx_string("quic_host_key"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_http_quic_host_key, @@ -313,37 +219,23 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) /* * set by ngx_pcalloc(): * - * h3scf->quic.tp.original_dcid = { 0, NULL }; - * h3scf->quic.tp.initial_scid = { 0, NULL }; - * h3scf->quic.tp.retry_scid = { 0, NULL }; - * h3scf->quic.tp.sr_token = { 0 } - * h3scf->quic.tp.sr_enabled = 0 - * h3scf->quic.tp.preferred_address = NULL * h3scf->quic.host_key = { 0, NULL } * h3scf->quic.stream_reject_code_uni = 0; + * h3scf->quic.disable_active_migration = 0; + * h3scf->quic.timeout = 0; + * h3scf->max_blocked_streams = 0; */ - - h3scf->max_table_capacity = NGX_CONF_UNSET_SIZE; - h3scf->max_blocked_streams = NGX_CONF_UNSET_UINT; + h3scf->max_table_capacity = NGX_HTTP_V3_MAX_TABLE_CAPACITY; h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT; - h3scf->max_uni_streams = NGX_CONF_UNSET_UINT; + h3scf->max_concurrent_streams = NGX_CONF_UNSET_UINT; #if (NGX_HTTP_V3_HQ) h3scf->hq = NGX_CONF_UNSET; #endif - h3scf->quic.tp.max_idle_timeout = NGX_CONF_UNSET_MSEC; - h3scf->quic.tp.max_ack_delay = NGX_CONF_UNSET_MSEC; - h3scf->quic.tp.max_udp_payload_size = NGX_CONF_UNSET_SIZE; - h3scf->quic.tp.initial_max_data = NGX_CONF_UNSET_SIZE; - h3scf->quic.tp.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_SIZE; - h3scf->quic.tp.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_SIZE; - h3scf->quic.tp.initial_max_stream_data_uni = NGX_CONF_UNSET_SIZE; - h3scf->quic.tp.initial_max_streams_bidi = NGX_CONF_UNSET_UINT; - h3scf->quic.tp.initial_max_streams_uni = NGX_CONF_UNSET_UINT; - h3scf->quic.tp.ack_delay_exponent = NGX_CONF_UNSET_UINT; - h3scf->quic.tp.disable_active_migration = NGX_CONF_UNSET; - h3scf->quic.tp.active_connection_id_limit = NGX_CONF_UNSET_UINT; - + h3scf->quic.mtu = NGX_CONF_UNSET_SIZE; + h3scf->quic.stream_buffer_size = NGX_CONF_UNSET_SIZE; + h3scf->quic.max_concurrent_streams_bidi = NGX_CONF_UNSET_UINT; + h3scf->quic.max_concurrent_streams_uni = NGX_HTTP_V3_MAX_UNI_STREAMS; h3scf->quic.retry = NGX_CONF_UNSET; h3scf->quic.gso_enabled = NGX_CONF_UNSET; h3scf->quic.stream_close_code = NGX_HTTP_V3_ERR_NO_ERROR; @@ -361,64 +253,27 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_ssl_srv_conf_t *sscf; - ngx_conf_merge_size_value(conf->max_table_capacity, - prev->max_table_capacity, 16384); - - ngx_conf_merge_uint_value(conf->max_blocked_streams, - prev->max_blocked_streams, 16); - ngx_conf_merge_uint_value(conf->max_concurrent_pushes, prev->max_concurrent_pushes, 10); - ngx_conf_merge_uint_value(conf->max_uni_streams, - prev->max_uni_streams, 3); + ngx_conf_merge_uint_value(conf->max_concurrent_streams, + prev->max_concurrent_streams, 128); + + conf->max_blocked_streams = conf->max_concurrent_streams; #if (NGX_HTTP_V3_HQ) ngx_conf_merge_value(conf->hq, prev->hq, 0); #endif - ngx_conf_merge_msec_value(conf->quic.tp.max_idle_timeout, - prev->quic.tp.max_idle_timeout, 60000); - - ngx_conf_merge_msec_value(conf->quic.tp.max_ack_delay, - prev->quic.tp.max_ack_delay, - NGX_QUIC_DEFAULT_MAX_ACK_DELAY); - ngx_conf_merge_size_value(conf->quic.tp.max_udp_payload_size, - prev->quic.tp.max_udp_payload_size, + ngx_conf_merge_size_value(conf->quic.mtu, prev->quic.mtu, NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); - ngx_conf_merge_size_value(conf->quic.tp.initial_max_data, - prev->quic.tp.initial_max_data, - 16 * NGX_QUIC_STREAM_BUFSIZE); - - ngx_conf_merge_size_value(conf->quic.tp.initial_max_stream_data_bidi_local, - prev->quic.tp.initial_max_stream_data_bidi_local, - NGX_QUIC_STREAM_BUFSIZE); - - ngx_conf_merge_size_value(conf->quic.tp.initial_max_stream_data_bidi_remote, - prev->quic.tp.initial_max_stream_data_bidi_remote, - NGX_QUIC_STREAM_BUFSIZE); - - ngx_conf_merge_size_value(conf->quic.tp.initial_max_stream_data_uni, - prev->quic.tp.initial_max_stream_data_uni, - NGX_QUIC_STREAM_BUFSIZE); + ngx_conf_merge_size_value(conf->quic.stream_buffer_size, + prev->quic.stream_buffer_size, + 65536); - ngx_conf_merge_uint_value(conf->quic.tp.initial_max_streams_bidi, - prev->quic.tp.initial_max_streams_bidi, 16); - - ngx_conf_merge_uint_value(conf->quic.tp.initial_max_streams_uni, - prev->quic.tp.initial_max_streams_uni, 3); - - ngx_conf_merge_uint_value(conf->quic.tp.ack_delay_exponent, - prev->quic.tp.ack_delay_exponent, - NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); - - ngx_conf_merge_value(conf->quic.tp.disable_active_migration, - prev->quic.tp.disable_active_migration, 0); - - ngx_conf_merge_uint_value(conf->quic.tp.active_connection_id_limit, - prev->quic.tp.active_connection_id_limit, 2); + conf->quic.max_concurrent_streams_bidi = conf->max_concurrent_streams; ngx_conf_merge_value(conf->quic.retry, prev->quic.retry, 0); ngx_conf_merge_value(conf->quic.gso_enabled, prev->quic.gso_enabled, 0); @@ -465,23 +320,7 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) static char * -ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, void *data) -{ - ngx_msec_t *sp = data; - - if (*sp >= 16384) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_max_ack_delay\" must be less than 16384"); - - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} - - -static char * -ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data) +ngx_http_quic_mtu(ngx_conf_t *cf, void *post, void *data) { size_t *sp = data; @@ -489,8 +328,7 @@ ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data) || *sp > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_max_udp_payload_size\" must be between " - "%d and %d", + "\"quic_mtu\" must be between %d and %d", NGX_QUIC_MIN_INITIAL_SIZE, NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 7fce688aa..e103a7eca 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -66,9 +66,11 @@ ngx_http_v3_init(ngx_connection_t *c) hc->ssl = 1; + clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); if (c->quic == NULL) { + h3scf->quic.timeout = clcf->keepalive_timeout; ngx_quic_run(c, &h3scf->quic); return; } @@ -79,7 +81,6 @@ ngx_http_v3_init(ngx_connection_t *c) hc->ssl_servername = phc->ssl_servername; hc->conf_ctx = phc->conf_ctx; - clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); ngx_set_connection_log(c, clcf->error_log); } diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 23b16cbc2..f0a599655 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -36,16 +36,13 @@ void ngx_http_v3_init_uni_stream(ngx_connection_t *c) { uint64_t n; - ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_uni_stream_t *us; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - n = c->quic->id >> 2; - if (n >= h3scf->max_uni_streams) { + if (n >= NGX_HTTP_V3_MAX_UNI_STREAMS) { ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, "reached maximum number of uni streams"); -- cgit From d1bc1da950488d8d3114a06ca00346fa58848a2d Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 7 Dec 2021 13:01:28 +0300 Subject: HTTP/3: renamed files. ngx_http_v3_tables.h and ngx_http_v3_tables.c are renamed to ngx_http_v3_table.h and ngx_http_v3_table.c to better match HTTP/2 code. ngx_http_v3_streams.h and ngx_http_v3_streams.c are renamed to ngx_http_v3_uni.h and ngx_http_v3_uni.c to better match their content. --- src/http/v3/ngx_http_v3.h | 4 +- src/http/v3/ngx_http_v3_streams.c | 733 -------------------------------------- src/http/v3/ngx_http_v3_streams.h | 38 -- src/http/v3/ngx_http_v3_table.c | 678 +++++++++++++++++++++++++++++++++++ src/http/v3/ngx_http_v3_table.h | 53 +++ src/http/v3/ngx_http_v3_tables.c | 678 ----------------------------------- src/http/v3/ngx_http_v3_tables.h | 53 --- src/http/v3/ngx_http_v3_uni.c | 733 ++++++++++++++++++++++++++++++++++++++ src/http/v3/ngx_http_v3_uni.h | 38 ++ 9 files changed, 1504 insertions(+), 1504 deletions(-) delete mode 100644 src/http/v3/ngx_http_v3_streams.c delete mode 100644 src/http/v3/ngx_http_v3_streams.h create mode 100644 src/http/v3/ngx_http_v3_table.c create mode 100644 src/http/v3/ngx_http_v3_table.h delete mode 100644 src/http/v3/ngx_http_v3_tables.c delete mode 100644 src/http/v3/ngx_http_v3_tables.h create mode 100644 src/http/v3/ngx_http_v3_uni.c create mode 100644 src/http/v3/ngx_http_v3_uni.h (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 91fa19f10..b7951e9bb 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -15,8 +15,8 @@ #include #include -#include -#include +#include +#include #define NGX_HTTP_V3_ALPN_PROTO "\x02h3" diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c deleted file mode 100644 index f0a599655..000000000 --- a/src/http/v3/ngx_http_v3_streams.c +++ /dev/null @@ -1,733 +0,0 @@ - -/* - * Copyright (C) Roman Arutyunyan - * Copyright (C) Nginx, Inc. - */ - - -#include -#include -#include - - -typedef struct { - ngx_http_v3_parse_uni_t parse; - ngx_int_t index; -} ngx_http_v3_uni_stream_t; - - -typedef struct { - ngx_queue_t queue; - uint64_t id; - ngx_connection_t *connection; - ngx_uint_t *npushing; -} ngx_http_v3_push_t; - - -static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); -static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); -static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); -static void ngx_http_v3_push_cleanup(void *data); -static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, - ngx_uint_t type); - - -void -ngx_http_v3_init_uni_stream(ngx_connection_t *c) -{ - uint64_t n; - ngx_http_v3_uni_stream_t *us; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); - - n = c->quic->id >> 2; - - if (n >= NGX_HTTP_V3_MAX_UNI_STREAMS) { - ngx_http_v3_finalize_connection(c, - NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, - "reached maximum number of uni streams"); - c->data = NULL; - ngx_http_v3_close_uni_stream(c); - return; - } - - c->quic->cancelable = 1; - - us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); - if (us == NULL) { - ngx_http_v3_finalize_connection(c, - NGX_HTTP_V3_ERR_INTERNAL_ERROR, - "memory allocation error"); - c->data = NULL; - ngx_http_v3_close_uni_stream(c); - return; - } - - us->index = -1; - - c->data = us; - - c->read->handler = ngx_http_v3_uni_read_handler; - c->write->handler = ngx_http_v3_dummy_write_handler; - - ngx_http_v3_uni_read_handler(c->read); -} - - -static void -ngx_http_v3_close_uni_stream(ngx_connection_t *c) -{ - ngx_pool_t *pool; - ngx_http_v3_session_t *h3c; - ngx_http_v3_uni_stream_t *us; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 close stream"); - - us = c->data; - - if (us && us->index >= 0) { - h3c = ngx_http_v3_get_session(c); - h3c->known_streams[us->index] = NULL; - } - - c->destroyed = 1; - - pool = c->pool; - - ngx_close_connection(c); - - ngx_destroy_pool(pool); -} - - -ngx_int_t -ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type) -{ - ngx_int_t index; - ngx_http_v3_session_t *h3c; - ngx_http_v3_uni_stream_t *us; - - h3c = ngx_http_v3_get_session(c); - - switch (type) { - - case NGX_HTTP_V3_STREAM_ENCODER: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 encoder stream"); - index = NGX_HTTP_V3_STREAM_CLIENT_ENCODER; - break; - - case NGX_HTTP_V3_STREAM_DECODER: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 decoder stream"); - index = NGX_HTTP_V3_STREAM_CLIENT_DECODER; - break; - - case NGX_HTTP_V3_STREAM_CONTROL: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 control stream"); - index = NGX_HTTP_V3_STREAM_CLIENT_CONTROL; - - break; - - default: - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 stream 0x%02xL", type); - - if (h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_ENCODER] == NULL - || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_DECODER] == NULL - || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_CONTROL] == NULL) - { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "missing mandatory stream"); - return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; - } - - index = -1; - } - - if (index >= 0) { - if (h3c->known_streams[index]) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); - return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; - } - - h3c->known_streams[index] = c; - - us = c->data; - us->index = index; - } - - return NGX_OK; -} - - -static void -ngx_http_v3_uni_read_handler(ngx_event_t *rev) -{ - u_char buf[128]; - ssize_t n; - ngx_buf_t b; - ngx_int_t rc; - ngx_connection_t *c; - ngx_http_v3_session_t *h3c; - ngx_http_v3_uni_stream_t *us; - - c = rev->data; - us = c->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler"); - - ngx_memzero(&b, sizeof(ngx_buf_t)); - - while (rev->ready) { - - n = c->recv(c, buf, sizeof(buf)); - - if (n == NGX_ERROR) { - rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; - goto failed; - } - - if (n == 0) { - if (us->index >= 0) { - rc = NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM; - goto failed; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read eof"); - ngx_http_v3_close_uni_stream(c); - return; - } - - if (n == NGX_AGAIN) { - break; - } - - b.pos = buf; - b.last = buf + n; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (ngx_http_v3_check_flood(c) != NGX_OK) { - ngx_http_v3_close_uni_stream(c); - return; - } - - rc = ngx_http_v3_parse_uni(c, &us->parse, &b); - - if (rc == NGX_DONE) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 read done"); - ngx_http_v3_close_uni_stream(c); - return; - } - - if (rc > 0) { - goto failed; - } - - if (rc != NGX_AGAIN) { - rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; - goto failed; - } - } - - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; - goto failed; - } - - return; - -failed: - - ngx_http_v3_finalize_connection(c, rc, "stream error"); - ngx_http_v3_close_uni_stream(c); -} - - -static void -ngx_http_v3_dummy_write_handler(ngx_event_t *wev) -{ - ngx_connection_t *c; - - c = wev->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy write handler"); - - if (ngx_handle_write_event(wev, 0) != NGX_OK) { - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - NULL); - ngx_http_v3_close_uni_stream(c); - } -} - - -/* XXX async & buffered stream writes */ - -ngx_connection_t * -ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) -{ - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 2]; - size_t n; - ngx_connection_t *sc; - ngx_pool_cleanup_t *cln; - ngx_http_v3_push_t *push; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 create push stream id:%uL", push_id); - - sc = ngx_quic_open_stream(c, 0); - if (sc == NULL) { - goto failed; - } - - p = buf; - p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_STREAM_PUSH); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, push_id); - n = p - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (sc->send(sc, buf, n) != (ssize_t) n) { - goto failed; - } - - cln = ngx_pool_cleanup_add(sc->pool, sizeof(ngx_http_v3_push_t)); - if (cln == NULL) { - goto failed; - } - - h3c->npushing++; - - cln->handler = ngx_http_v3_push_cleanup; - - push = cln->data; - push->id = push_id; - push->connection = sc; - push->npushing = &h3c->npushing; - - ngx_queue_insert_tail(&h3c->pushing, &push->queue); - - return sc; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create push stream"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, - "failed to create push stream"); - if (sc) { - ngx_http_v3_close_uni_stream(sc); - } - - return NULL; -} - - -static void -ngx_http_v3_push_cleanup(void *data) -{ - ngx_http_v3_push_t *push = data; - - ngx_queue_remove(&push->queue); - (*push->npushing)--; -} - - -static ngx_connection_t * -ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) -{ - u_char buf[NGX_HTTP_V3_VARLEN_INT_LEN]; - size_t n; - ngx_int_t index; - ngx_connection_t *sc; - ngx_http_v3_session_t *h3c; - ngx_http_v3_uni_stream_t *us; - - switch (type) { - case NGX_HTTP_V3_STREAM_ENCODER: - index = NGX_HTTP_V3_STREAM_SERVER_ENCODER; - break; - case NGX_HTTP_V3_STREAM_DECODER: - index = NGX_HTTP_V3_STREAM_SERVER_DECODER; - break; - case NGX_HTTP_V3_STREAM_CONTROL: - index = NGX_HTTP_V3_STREAM_SERVER_CONTROL; - break; - default: - index = -1; - } - - h3c = ngx_http_v3_get_session(c); - - if (index >= 0) { - if (h3c->known_streams[index]) { - return h3c->known_streams[index]; - } - } - - sc = ngx_quic_open_stream(c, 0); - if (sc == NULL) { - goto failed; - } - - sc->quic->cancelable = 1; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 create uni stream, type:%ui", type); - - us = ngx_pcalloc(sc->pool, sizeof(ngx_http_v3_uni_stream_t)); - if (us == NULL) { - goto failed; - } - - us->index = index; - - sc->data = us; - - sc->read->handler = ngx_http_v3_uni_read_handler; - sc->write->handler = ngx_http_v3_dummy_write_handler; - - if (index >= 0) { - h3c->known_streams[index] = sc; - } - - n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (sc->send(sc, buf, n) != (ssize_t) n) { - goto failed; - } - - return sc; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create server stream"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, - "failed to create server stream"); - if (sc) { - ngx_http_v3_close_uni_stream(sc); - } - - return NULL; -} - - -ngx_int_t -ngx_http_v3_send_settings(ngx_connection_t *c) -{ - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; - size_t n; - ngx_connection_t *cc; - ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); - - cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); - if (cc == NULL) { - return NGX_ERROR; - } - - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - - n = ngx_http_v3_encode_varlen_int(NULL, - NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); - n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_table_capacity); - n += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); - n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_blocked_streams); - - p = (u_char *) ngx_http_v3_encode_varlen_int(buf, - NGX_HTTP_V3_FRAME_SETTINGS); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, - NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_table_capacity); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, - NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_blocked_streams); - n = p - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (cc->send(cc, buf, n) != (ssize_t) n) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send settings"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, - "failed to send settings"); - ngx_http_v3_close_uni_stream(cc); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id) -{ - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3]; - size_t n; - ngx_connection_t *cc; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send goaway %uL", id); - - cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); - if (cc == NULL) { - return NGX_ERROR; - } - - n = ngx_http_v3_encode_varlen_int(NULL, id); - p = (u_char *) ngx_http_v3_encode_varlen_int(buf, NGX_HTTP_V3_FRAME_GOAWAY); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, id); - n = p - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (cc->send(cc, buf, n) != (ssize_t) n) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send goaway"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, - "failed to send goaway"); - ngx_http_v3_close_uni_stream(cc); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) -{ - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *dc; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 send section acknowledgement %ui", stream_id); - - dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); - if (dc == NULL) { - return NGX_ERROR; - } - - buf[0] = 0x80; - n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 7) - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (dc->send(dc, buf, n) != (ssize_t) n) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "failed to send section acknowledgement"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, - "failed to send section acknowledgement"); - ngx_http_v3_close_uni_stream(dc); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) -{ - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *dc; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 send stream cancellation %ui", stream_id); - - dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); - if (dc == NULL) { - return NGX_ERROR; - } - - buf[0] = 0x40; - n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 6) - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (dc->send(dc, buf, n) != (ssize_t) n) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send stream cancellation"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, - "failed to send stream cancellation"); - ngx_http_v3_close_uni_stream(dc); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) -{ - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *dc; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 send insert count increment %ui", inc); - - dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); - if (dc == NULL) { - return NGX_ERROR; - } - - buf[0] = 0; - n = (u_char *) ngx_http_v3_encode_prefix_int(buf, inc, 6) - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (dc->send(dc, buf, n) != (ssize_t) n) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "failed to send insert count increment"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, - "failed to send insert count increment"); - ngx_http_v3_close_uni_stream(dc); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) -{ - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 MAX_PUSH_ID:%uL", max_push_id); - - if (h3c->max_push_id != (uint64_t) -1 && max_push_id < h3c->max_push_id) { - return NGX_HTTP_V3_ERR_ID_ERROR; - } - - h3c->max_push_id = max_push_id; - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id) -{ - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 GOAWAY:%uL", push_id); - - h3c->goaway_push_id = push_id; - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) -{ - ngx_queue_t *q; - ngx_http_request_t *r; - ngx_http_v3_push_t *push; - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 CANCEL_PUSH:%uL", push_id); - - if (push_id >= h3c->next_push_id) { - return NGX_HTTP_V3_ERR_ID_ERROR; - } - - for (q = ngx_queue_head(&h3c->pushing); - q != ngx_queue_sentinel(&h3c->pushing); - q = ngx_queue_next(&h3c->pushing)) - { - push = (ngx_http_v3_push_t *) q; - - if (push->id != push_id) { - continue; - } - - r = push->connection->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 cancel push"); - - ngx_http_finalize_request(r, NGX_HTTP_CLOSE); - - break; - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) -{ - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 cancel stream %ui", stream_id); - - /* we do not use dynamic tables */ - - return NGX_OK; -} diff --git a/src/http/v3/ngx_http_v3_streams.h b/src/http/v3/ngx_http_v3_streams.h deleted file mode 100644 index 42ff849be..000000000 --- a/src/http/v3/ngx_http_v3_streams.h +++ /dev/null @@ -1,38 +0,0 @@ - -/* - * Copyright (C) Roman Arutyunyan - * Copyright (C) Nginx, Inc. - */ - - -#ifndef _NGX_HTTP_V3_STREAMS_H_INCLUDED_ -#define _NGX_HTTP_V3_STREAMS_H_INCLUDED_ - - -#include -#include -#include - - -void ngx_http_v3_init_uni_stream(ngx_connection_t *c); -ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type); - -ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, - uint64_t push_id); -ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, - uint64_t max_push_id); -ngx_int_t ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id); -ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); -ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); - -ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); -ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); -ngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c, - ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, - ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, - ngx_uint_t inc); - - -#endif /* _NGX_HTTP_V3_STREAMS_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_table.c b/src/http/v3/ngx_http_v3_table.c new file mode 100644 index 000000000..c6d543ac4 --- /dev/null +++ b/src/http/v3/ngx_http_v3_table.c @@ -0,0 +1,678 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) + + +static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); +static void ngx_http_v3_unblock(void *data); +static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c); + + +typedef struct { + ngx_queue_t queue; + ngx_connection_t *connection; + ngx_uint_t *nblocked; +} ngx_http_v3_block_t; + + +static ngx_http_v3_field_t ngx_http_v3_static_table[] = { + + { ngx_string(":authority"), ngx_string("") }, + { ngx_string(":path"), ngx_string("/") }, + { ngx_string("age"), ngx_string("0") }, + { ngx_string("content-disposition"), ngx_string("") }, + { ngx_string("content-length"), ngx_string("0") }, + { ngx_string("cookie"), ngx_string("") }, + { ngx_string("date"), ngx_string("") }, + { ngx_string("etag"), ngx_string("") }, + { ngx_string("if-modified-since"), ngx_string("") }, + { ngx_string("if-none-match"), ngx_string("") }, + { ngx_string("last-modified"), ngx_string("") }, + { ngx_string("link"), ngx_string("") }, + { ngx_string("location"), ngx_string("") }, + { ngx_string("referer"), ngx_string("") }, + { ngx_string("set-cookie"), ngx_string("") }, + { ngx_string(":method"), ngx_string("CONNECT") }, + { ngx_string(":method"), ngx_string("DELETE") }, + { ngx_string(":method"), ngx_string("GET") }, + { ngx_string(":method"), ngx_string("HEAD") }, + { ngx_string(":method"), ngx_string("OPTIONS") }, + { ngx_string(":method"), ngx_string("POST") }, + { ngx_string(":method"), ngx_string("PUT") }, + { ngx_string(":scheme"), ngx_string("http") }, + { ngx_string(":scheme"), ngx_string("https") }, + { ngx_string(":status"), ngx_string("103") }, + { ngx_string(":status"), ngx_string("200") }, + { ngx_string(":status"), ngx_string("304") }, + { ngx_string(":status"), ngx_string("404") }, + { ngx_string(":status"), ngx_string("503") }, + { ngx_string("accept"), ngx_string("*/*") }, + { ngx_string("accept"), + ngx_string("application/dns-message") }, + { ngx_string("accept-encoding"), ngx_string("gzip, deflate, br") }, + { ngx_string("accept-ranges"), ngx_string("bytes") }, + { ngx_string("access-control-allow-headers"), + ngx_string("cache-control") }, + { ngx_string("access-control-allow-headers"), + ngx_string("content-type") }, + { ngx_string("access-control-allow-origin"), + ngx_string("*") }, + { ngx_string("cache-control"), ngx_string("max-age=0") }, + { ngx_string("cache-control"), ngx_string("max-age=2592000") }, + { ngx_string("cache-control"), ngx_string("max-age=604800") }, + { ngx_string("cache-control"), ngx_string("no-cache") }, + { ngx_string("cache-control"), ngx_string("no-store") }, + { ngx_string("cache-control"), + ngx_string("public, max-age=31536000") }, + { ngx_string("content-encoding"), ngx_string("br") }, + { ngx_string("content-encoding"), ngx_string("gzip") }, + { ngx_string("content-type"), + ngx_string("application/dns-message") }, + { ngx_string("content-type"), + ngx_string("application/javascript") }, + { ngx_string("content-type"), ngx_string("application/json") }, + { ngx_string("content-type"), + ngx_string("application/x-www-form-urlencoded") }, + { ngx_string("content-type"), ngx_string("image/gif") }, + { ngx_string("content-type"), ngx_string("image/jpeg") }, + { ngx_string("content-type"), ngx_string("image/png") }, + { ngx_string("content-type"), ngx_string("text/css") }, + { ngx_string("content-type"), + ngx_string("text/html;charset=utf-8") }, + { ngx_string("content-type"), ngx_string("text/plain") }, + { ngx_string("content-type"), + ngx_string("text/plain;charset=utf-8") }, + { ngx_string("range"), ngx_string("bytes=0-") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000;includesubdomains") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000;includesubdomains;preload") }, + { ngx_string("vary"), ngx_string("accept-encoding") }, + { ngx_string("vary"), ngx_string("origin") }, + { ngx_string("x-content-type-options"), + ngx_string("nosniff") }, + { ngx_string("x-xss-protection"), ngx_string("1;mode=block") }, + { ngx_string(":status"), ngx_string("100") }, + { ngx_string(":status"), ngx_string("204") }, + { ngx_string(":status"), ngx_string("206") }, + { ngx_string(":status"), ngx_string("302") }, + { ngx_string(":status"), ngx_string("400") }, + { ngx_string(":status"), ngx_string("403") }, + { ngx_string(":status"), ngx_string("421") }, + { ngx_string(":status"), ngx_string("425") }, + { ngx_string(":status"), ngx_string("500") }, + { ngx_string("accept-language"), ngx_string("") }, + { ngx_string("access-control-allow-credentials"), + ngx_string("FALSE") }, + { ngx_string("access-control-allow-credentials"), + ngx_string("TRUE") }, + { ngx_string("access-control-allow-headers"), + ngx_string("*") }, + { ngx_string("access-control-allow-methods"), + ngx_string("get") }, + { ngx_string("access-control-allow-methods"), + ngx_string("get, post, options") }, + { ngx_string("access-control-allow-methods"), + ngx_string("options") }, + { ngx_string("access-control-expose-headers"), + ngx_string("content-length") }, + { ngx_string("access-control-request-headers"), + ngx_string("content-type") }, + { ngx_string("access-control-request-method"), + ngx_string("get") }, + { ngx_string("access-control-request-method"), + ngx_string("post") }, + { ngx_string("alt-svc"), ngx_string("clear") }, + { ngx_string("authorization"), ngx_string("") }, + { ngx_string("content-security-policy"), + ngx_string("script-src 'none';object-src 'none';base-uri 'none'") }, + { ngx_string("early-data"), ngx_string("1") }, + { ngx_string("expect-ct"), ngx_string("") }, + { ngx_string("forwarded"), ngx_string("") }, + { ngx_string("if-range"), ngx_string("") }, + { ngx_string("origin"), ngx_string("") }, + { ngx_string("purpose"), ngx_string("prefetch") }, + { ngx_string("server"), ngx_string("") }, + { ngx_string("timing-allow-origin"), ngx_string("*") }, + { ngx_string("upgrade-insecure-requests"), + ngx_string("1") }, + { ngx_string("user-agent"), ngx_string("") }, + { ngx_string("x-forwarded-for"), ngx_string("") }, + { ngx_string("x-frame-options"), ngx_string("deny") }, + { ngx_string("x-frame-options"), ngx_string("sameorigin") } +}; + + +ngx_int_t +ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value) +{ + ngx_str_t name; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + if (dynamic) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ref insert dynamic[%ui] \"%V\"", index, value); + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->base + dt->nelts <= index) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + index = dt->base + dt->nelts - 1 - index; + + if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ref insert static[%ui] \"%V\"", index, value); + + if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + } + + return ngx_http_v3_insert(c, &name, value); +} + + +ngx_int_t +ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) +{ + u_char *p; + size_t size; + ngx_http_v3_field_t *field; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + size = ngx_http_v3_table_entry_size(name, value); + + if (ngx_http_v3_evict(c, size) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 insert [%ui] \"%V\":\"%V\", size:%uz", + dt->base + dt->nelts, name, value, size); + + p = ngx_alloc(sizeof(ngx_http_v3_field_t) + name->len + value->len, + c->log); + if (p == NULL) { + return NGX_ERROR; + } + + field = (ngx_http_v3_field_t *) p; + + field->name.data = p + sizeof(ngx_http_v3_field_t); + field->name.len = name->len; + field->value.data = ngx_cpymem(field->name.data, name->data, name->len); + field->value.len = value->len; + ngx_memcpy(field->value.data, value->data, value->len); + + dt->elts[dt->nelts++] = field; + dt->size += size; + + /* TODO increment can be sent less often */ + + if (ngx_http_v3_send_inc_insert_count(c, 1) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_http_v3_new_entry(c) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) +{ + ngx_uint_t max, prev_max; + ngx_http_v3_field_t **elts; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_dynamic_table_t *dt; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 set capacity %ui", capacity); + + h3c = ngx_http_v3_get_session(c); + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + if (capacity > h3scf->max_table_capacity) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded http3_max_table_capacity limit"); + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + dt = &h3c->table; + + if (dt->size > capacity) { + if (ngx_http_v3_evict(c, dt->size - capacity) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + } + + max = capacity / 32; + prev_max = dt->capacity / 32; + + if (max > prev_max) { + elts = ngx_alloc(max * sizeof(void *), c->log); + if (elts == NULL) { + return NGX_ERROR; + } + + if (dt->elts) { + ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *)); + ngx_free(dt->elts); + } + + dt->elts = elts; + } + + dt->capacity = capacity; + + return NGX_OK; +} + + +void +ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c) +{ + ngx_uint_t n; + ngx_http_v3_dynamic_table_t *dt; + + dt = &h3c->table; + + if (dt->elts == NULL) { + return; + } + + for (n = 0; n < dt->nelts; n++) { + ngx_free(dt->elts[n]); + } + + ngx_free(dt->elts); +} + + +static ngx_int_t +ngx_http_v3_evict(ngx_connection_t *c, size_t need) +{ + size_t size, target; + ngx_uint_t n; + ngx_http_v3_field_t *field; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (need > dt->capacity) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "not enough dynamic table capacity"); + return NGX_ERROR; + } + + target = dt->capacity - need; + n = 0; + + while (dt->size > target) { + field = dt->elts[n++]; + size = ngx_http_v3_table_entry_size(&field->name, &field->value); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 evict [%ui] \"%V\":\"%V\" size:%uz", + dt->base, &field->name, &field->value, size); + + ngx_free(field); + dt->size -= size; + } + + if (n) { + dt->nelts -= n; + dt->base += n; + ngx_memmove(dt->elts, &dt->elts[n], dt->nelts * sizeof(void *)); + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) +{ + ngx_str_t name, value; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->base + dt->nelts <= index) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + index = dt->base + dt->nelts - 1 - index; + + if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + return ngx_http_v3_insert(c, &name, &value); +} + + +ngx_int_t +ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ack section %ui", stream_id); + + /* we do not use dynamic tables */ + + return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; +} + + +ngx_int_t +ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 increment insert count %ui", inc); + + /* we do not use dynamic tables */ + + return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; +} + + +ngx_int_t +ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value) +{ + ngx_uint_t nelts; + ngx_http_v3_field_t *field; + + nelts = sizeof(ngx_http_v3_static_table) + / sizeof(ngx_http_v3_static_table[0]); + + if (index >= nelts) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 static[%ui] lookup out of bounds: %ui", + index, nelts); + return NGX_ERROR; + } + + field = &ngx_http_v3_static_table[index]; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 static[%ui] lookup \"%V\":\"%V\"", + index, &field->name, &field->value); + + if (name) { + *name = field->name; + } + + if (value) { + *value = field->value; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, + ngx_str_t *value) +{ + ngx_http_v3_field_t *field; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (index < dt->base || index - dt->base >= dt->nelts) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 dynamic[%ui] lookup out of bounds: [%ui,%ui]", + index, dt->base, dt->base + dt->nelts); + return NGX_ERROR; + } + + field = dt->elts[index - dt->base]; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 dynamic[%ui] lookup \"%V\":\"%V\"", + index, &field->name, &field->value); + + if (name) { + *name = field->name; + } + + if (value) { + *value = field->value; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) +{ + ngx_uint_t max_entries, full_range, max_value, + max_wrapped, req_insert_count; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + /* QPACK 4.5.1.1. Required Insert Count */ + + if (*insert_count == 0) { + return NGX_OK; + } + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + max_entries = h3scf->max_table_capacity / 32; + full_range = 2 * max_entries; + + if (*insert_count > full_range) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + max_value = dt->base + dt->nelts + max_entries; + max_wrapped = (max_value / full_range) * full_range; + req_insert_count = max_wrapped + *insert_count - 1; + + if (req_insert_count > max_value) { + if (req_insert_count <= full_range) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + req_insert_count -= full_range; + } + + if (req_insert_count == 0) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 decode insert_count %ui -> %ui", + *insert_count, req_insert_count); + + *insert_count = req_insert_count; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) +{ + size_t n; + ngx_pool_cleanup_t *cln; + ngx_http_v3_block_t *block; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + n = dt->base + dt->nelts; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 check insert count req:%ui, have:%ui", + insert_count, n); + + if (n >= insert_count) { + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 block stream"); + + block = NULL; + + for (cln = c->pool->cleanup; cln; cln = cln->next) { + if (cln->handler == ngx_http_v3_unblock) { + block = cln->data; + break; + } + } + + if (block == NULL) { + cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_v3_unblock; + + block = cln->data; + block->queue.prev = NULL; + block->connection = c; + block->nblocked = &h3c->nblocked; + } + + if (block->queue.prev == NULL) { + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + if (h3c->nblocked == h3scf->max_blocked_streams) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded http3_max_blocked_streams limit"); + + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED, + "too many blocked streams"); + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + h3c->nblocked++; + ngx_queue_insert_tail(&h3c->blocked, &block->queue); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 blocked:%ui", h3c->nblocked); + + return NGX_BUSY; +} + + +static void +ngx_http_v3_unblock(void *data) +{ + ngx_http_v3_block_t *block = data; + + if (block->queue.prev) { + ngx_queue_remove(&block->queue); + block->queue.prev = NULL; + (*block->nblocked)--; + } +} + + +static ngx_int_t +ngx_http_v3_new_entry(ngx_connection_t *c) +{ + ngx_queue_t *q; + ngx_connection_t *bc; + ngx_http_v3_block_t *block; + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 new dynamic entry, blocked:%ui", h3c->nblocked); + + while (!ngx_queue_empty(&h3c->blocked)) { + q = ngx_queue_head(&h3c->blocked); + block = (ngx_http_v3_block_t *) q; + bc = block->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, bc->log, 0, "http3 unblock stream"); + + ngx_http_v3_unblock(block); + ngx_post_event(bc->read, &ngx_posted_events); + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value) +{ + switch (id) { + + case NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param QPACK_MAX_TABLE_CAPACITY:%uL", value); + break; + + case NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param SETTINGS_MAX_HEADER_LIST_SIZE:%uL", value); + break; + + case NGX_HTTP_V3_PARAM_BLOCKED_STREAMS: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param QPACK_BLOCKED_STREAMS:%uL", value); + break; + + default: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param #%uL:%uL", id, value); + } + + return NGX_OK; +} diff --git a/src/http/v3/ngx_http_v3_table.h b/src/http/v3/ngx_http_v3_table.h new file mode 100644 index 000000000..6f88b35b9 --- /dev/null +++ b/src/http/v3/ngx_http_v3_table.h @@ -0,0 +1,53 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_TABLE_H_INCLUDED_ +#define _NGX_HTTP_V3_TABLE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_str_t name; + ngx_str_t value; +} ngx_http_v3_field_t; + + +typedef struct { + ngx_http_v3_field_t **elts; + ngx_uint_t nelts; + ngx_uint_t base; + size_t size; + size_t capacity; +} ngx_http_v3_dynamic_table_t; + + +void ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c); +ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value); +ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value); +ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); +ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index); +ngx_int_t ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); +ngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_v3_decode_insert_count(ngx_connection_t *c, + ngx_uint_t *insert_count); +ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, + ngx_uint_t insert_count); +ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, + uint64_t value); + + +#endif /* _NGX_HTTP_V3_TABLE_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c deleted file mode 100644 index c6d543ac4..000000000 --- a/src/http/v3/ngx_http_v3_tables.c +++ /dev/null @@ -1,678 +0,0 @@ - -/* - * Copyright (C) Roman Arutyunyan - * Copyright (C) Nginx, Inc. - */ - - -#include -#include -#include - - -#define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) - - -static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); -static void ngx_http_v3_unblock(void *data); -static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c); - - -typedef struct { - ngx_queue_t queue; - ngx_connection_t *connection; - ngx_uint_t *nblocked; -} ngx_http_v3_block_t; - - -static ngx_http_v3_field_t ngx_http_v3_static_table[] = { - - { ngx_string(":authority"), ngx_string("") }, - { ngx_string(":path"), ngx_string("/") }, - { ngx_string("age"), ngx_string("0") }, - { ngx_string("content-disposition"), ngx_string("") }, - { ngx_string("content-length"), ngx_string("0") }, - { ngx_string("cookie"), ngx_string("") }, - { ngx_string("date"), ngx_string("") }, - { ngx_string("etag"), ngx_string("") }, - { ngx_string("if-modified-since"), ngx_string("") }, - { ngx_string("if-none-match"), ngx_string("") }, - { ngx_string("last-modified"), ngx_string("") }, - { ngx_string("link"), ngx_string("") }, - { ngx_string("location"), ngx_string("") }, - { ngx_string("referer"), ngx_string("") }, - { ngx_string("set-cookie"), ngx_string("") }, - { ngx_string(":method"), ngx_string("CONNECT") }, - { ngx_string(":method"), ngx_string("DELETE") }, - { ngx_string(":method"), ngx_string("GET") }, - { ngx_string(":method"), ngx_string("HEAD") }, - { ngx_string(":method"), ngx_string("OPTIONS") }, - { ngx_string(":method"), ngx_string("POST") }, - { ngx_string(":method"), ngx_string("PUT") }, - { ngx_string(":scheme"), ngx_string("http") }, - { ngx_string(":scheme"), ngx_string("https") }, - { ngx_string(":status"), ngx_string("103") }, - { ngx_string(":status"), ngx_string("200") }, - { ngx_string(":status"), ngx_string("304") }, - { ngx_string(":status"), ngx_string("404") }, - { ngx_string(":status"), ngx_string("503") }, - { ngx_string("accept"), ngx_string("*/*") }, - { ngx_string("accept"), - ngx_string("application/dns-message") }, - { ngx_string("accept-encoding"), ngx_string("gzip, deflate, br") }, - { ngx_string("accept-ranges"), ngx_string("bytes") }, - { ngx_string("access-control-allow-headers"), - ngx_string("cache-control") }, - { ngx_string("access-control-allow-headers"), - ngx_string("content-type") }, - { ngx_string("access-control-allow-origin"), - ngx_string("*") }, - { ngx_string("cache-control"), ngx_string("max-age=0") }, - { ngx_string("cache-control"), ngx_string("max-age=2592000") }, - { ngx_string("cache-control"), ngx_string("max-age=604800") }, - { ngx_string("cache-control"), ngx_string("no-cache") }, - { ngx_string("cache-control"), ngx_string("no-store") }, - { ngx_string("cache-control"), - ngx_string("public, max-age=31536000") }, - { ngx_string("content-encoding"), ngx_string("br") }, - { ngx_string("content-encoding"), ngx_string("gzip") }, - { ngx_string("content-type"), - ngx_string("application/dns-message") }, - { ngx_string("content-type"), - ngx_string("application/javascript") }, - { ngx_string("content-type"), ngx_string("application/json") }, - { ngx_string("content-type"), - ngx_string("application/x-www-form-urlencoded") }, - { ngx_string("content-type"), ngx_string("image/gif") }, - { ngx_string("content-type"), ngx_string("image/jpeg") }, - { ngx_string("content-type"), ngx_string("image/png") }, - { ngx_string("content-type"), ngx_string("text/css") }, - { ngx_string("content-type"), - ngx_string("text/html;charset=utf-8") }, - { ngx_string("content-type"), ngx_string("text/plain") }, - { ngx_string("content-type"), - ngx_string("text/plain;charset=utf-8") }, - { ngx_string("range"), ngx_string("bytes=0-") }, - { ngx_string("strict-transport-security"), - ngx_string("max-age=31536000") }, - { ngx_string("strict-transport-security"), - ngx_string("max-age=31536000;includesubdomains") }, - { ngx_string("strict-transport-security"), - ngx_string("max-age=31536000;includesubdomains;preload") }, - { ngx_string("vary"), ngx_string("accept-encoding") }, - { ngx_string("vary"), ngx_string("origin") }, - { ngx_string("x-content-type-options"), - ngx_string("nosniff") }, - { ngx_string("x-xss-protection"), ngx_string("1;mode=block") }, - { ngx_string(":status"), ngx_string("100") }, - { ngx_string(":status"), ngx_string("204") }, - { ngx_string(":status"), ngx_string("206") }, - { ngx_string(":status"), ngx_string("302") }, - { ngx_string(":status"), ngx_string("400") }, - { ngx_string(":status"), ngx_string("403") }, - { ngx_string(":status"), ngx_string("421") }, - { ngx_string(":status"), ngx_string("425") }, - { ngx_string(":status"), ngx_string("500") }, - { ngx_string("accept-language"), ngx_string("") }, - { ngx_string("access-control-allow-credentials"), - ngx_string("FALSE") }, - { ngx_string("access-control-allow-credentials"), - ngx_string("TRUE") }, - { ngx_string("access-control-allow-headers"), - ngx_string("*") }, - { ngx_string("access-control-allow-methods"), - ngx_string("get") }, - { ngx_string("access-control-allow-methods"), - ngx_string("get, post, options") }, - { ngx_string("access-control-allow-methods"), - ngx_string("options") }, - { ngx_string("access-control-expose-headers"), - ngx_string("content-length") }, - { ngx_string("access-control-request-headers"), - ngx_string("content-type") }, - { ngx_string("access-control-request-method"), - ngx_string("get") }, - { ngx_string("access-control-request-method"), - ngx_string("post") }, - { ngx_string("alt-svc"), ngx_string("clear") }, - { ngx_string("authorization"), ngx_string("") }, - { ngx_string("content-security-policy"), - ngx_string("script-src 'none';object-src 'none';base-uri 'none'") }, - { ngx_string("early-data"), ngx_string("1") }, - { ngx_string("expect-ct"), ngx_string("") }, - { ngx_string("forwarded"), ngx_string("") }, - { ngx_string("if-range"), ngx_string("") }, - { ngx_string("origin"), ngx_string("") }, - { ngx_string("purpose"), ngx_string("prefetch") }, - { ngx_string("server"), ngx_string("") }, - { ngx_string("timing-allow-origin"), ngx_string("*") }, - { ngx_string("upgrade-insecure-requests"), - ngx_string("1") }, - { ngx_string("user-agent"), ngx_string("") }, - { ngx_string("x-forwarded-for"), ngx_string("") }, - { ngx_string("x-frame-options"), ngx_string("deny") }, - { ngx_string("x-frame-options"), ngx_string("sameorigin") } -}; - - -ngx_int_t -ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, - ngx_uint_t index, ngx_str_t *value) -{ - ngx_str_t name; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - if (dynamic) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 ref insert dynamic[%ui] \"%V\"", index, value); - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - if (dt->base + dt->nelts <= index) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - - index = dt->base + dt->nelts - 1 - index; - - if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - - } else { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 ref insert static[%ui] \"%V\"", index, value); - - if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - } - - return ngx_http_v3_insert(c, &name, value); -} - - -ngx_int_t -ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) -{ - u_char *p; - size_t size; - ngx_http_v3_field_t *field; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - size = ngx_http_v3_table_entry_size(name, value); - - if (ngx_http_v3_evict(c, size) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 insert [%ui] \"%V\":\"%V\", size:%uz", - dt->base + dt->nelts, name, value, size); - - p = ngx_alloc(sizeof(ngx_http_v3_field_t) + name->len + value->len, - c->log); - if (p == NULL) { - return NGX_ERROR; - } - - field = (ngx_http_v3_field_t *) p; - - field->name.data = p + sizeof(ngx_http_v3_field_t); - field->name.len = name->len; - field->value.data = ngx_cpymem(field->name.data, name->data, name->len); - field->value.len = value->len; - ngx_memcpy(field->value.data, value->data, value->len); - - dt->elts[dt->nelts++] = field; - dt->size += size; - - /* TODO increment can be sent less often */ - - if (ngx_http_v3_send_inc_insert_count(c, 1) != NGX_OK) { - return NGX_ERROR; - } - - if (ngx_http_v3_new_entry(c) != NGX_OK) { - return NGX_ERROR; - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) -{ - ngx_uint_t max, prev_max; - ngx_http_v3_field_t **elts; - ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_dynamic_table_t *dt; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 set capacity %ui", capacity); - - h3c = ngx_http_v3_get_session(c); - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - - if (capacity > h3scf->max_table_capacity) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client exceeded http3_max_table_capacity limit"); - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - - dt = &h3c->table; - - if (dt->size > capacity) { - if (ngx_http_v3_evict(c, dt->size - capacity) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - } - - max = capacity / 32; - prev_max = dt->capacity / 32; - - if (max > prev_max) { - elts = ngx_alloc(max * sizeof(void *), c->log); - if (elts == NULL) { - return NGX_ERROR; - } - - if (dt->elts) { - ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *)); - ngx_free(dt->elts); - } - - dt->elts = elts; - } - - dt->capacity = capacity; - - return NGX_OK; -} - - -void -ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c) -{ - ngx_uint_t n; - ngx_http_v3_dynamic_table_t *dt; - - dt = &h3c->table; - - if (dt->elts == NULL) { - return; - } - - for (n = 0; n < dt->nelts; n++) { - ngx_free(dt->elts[n]); - } - - ngx_free(dt->elts); -} - - -static ngx_int_t -ngx_http_v3_evict(ngx_connection_t *c, size_t need) -{ - size_t size, target; - ngx_uint_t n; - ngx_http_v3_field_t *field; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - if (need > dt->capacity) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "not enough dynamic table capacity"); - return NGX_ERROR; - } - - target = dt->capacity - need; - n = 0; - - while (dt->size > target) { - field = dt->elts[n++]; - size = ngx_http_v3_table_entry_size(&field->name, &field->value); - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 evict [%ui] \"%V\":\"%V\" size:%uz", - dt->base, &field->name, &field->value, size); - - ngx_free(field); - dt->size -= size; - } - - if (n) { - dt->nelts -= n; - dt->base += n; - ngx_memmove(dt->elts, &dt->elts[n], dt->nelts * sizeof(void *)); - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) -{ - ngx_str_t name, value; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - if (dt->base + dt->nelts <= index) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - - index = dt->base + dt->nelts - 1 - index; - - if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - - return ngx_http_v3_insert(c, &name, &value); -} - - -ngx_int_t -ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) -{ - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 ack section %ui", stream_id); - - /* we do not use dynamic tables */ - - return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; -} - - -ngx_int_t -ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) -{ - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 increment insert count %ui", inc); - - /* we do not use dynamic tables */ - - return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; -} - - -ngx_int_t -ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, - ngx_str_t *name, ngx_str_t *value) -{ - ngx_uint_t nelts; - ngx_http_v3_field_t *field; - - nelts = sizeof(ngx_http_v3_static_table) - / sizeof(ngx_http_v3_static_table[0]); - - if (index >= nelts) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 static[%ui] lookup out of bounds: %ui", - index, nelts); - return NGX_ERROR; - } - - field = &ngx_http_v3_static_table[index]; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 static[%ui] lookup \"%V\":\"%V\"", - index, &field->name, &field->value); - - if (name) { - *name = field->name; - } - - if (value) { - *value = field->value; - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, - ngx_str_t *value) -{ - ngx_http_v3_field_t *field; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - if (index < dt->base || index - dt->base >= dt->nelts) { - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 dynamic[%ui] lookup out of bounds: [%ui,%ui]", - index, dt->base, dt->base + dt->nelts); - return NGX_ERROR; - } - - field = dt->elts[index - dt->base]; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 dynamic[%ui] lookup \"%V\":\"%V\"", - index, &field->name, &field->value); - - if (name) { - *name = field->name; - } - - if (value) { - *value = field->value; - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) -{ - ngx_uint_t max_entries, full_range, max_value, - max_wrapped, req_insert_count; - ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - /* QPACK 4.5.1.1. Required Insert Count */ - - if (*insert_count == 0) { - return NGX_OK; - } - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - - max_entries = h3scf->max_table_capacity / 32; - full_range = 2 * max_entries; - - if (*insert_count > full_range) { - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - max_value = dt->base + dt->nelts + max_entries; - max_wrapped = (max_value / full_range) * full_range; - req_insert_count = max_wrapped + *insert_count - 1; - - if (req_insert_count > max_value) { - if (req_insert_count <= full_range) { - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - req_insert_count -= full_range; - } - - if (req_insert_count == 0) { - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 decode insert_count %ui -> %ui", - *insert_count, req_insert_count); - - *insert_count = req_insert_count; - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) -{ - size_t n; - ngx_pool_cleanup_t *cln; - ngx_http_v3_block_t *block; - ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_dynamic_table_t *dt; - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - n = dt->base + dt->nelts; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 check insert count req:%ui, have:%ui", - insert_count, n); - - if (n >= insert_count) { - return NGX_OK; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 block stream"); - - block = NULL; - - for (cln = c->pool->cleanup; cln; cln = cln->next) { - if (cln->handler == ngx_http_v3_unblock) { - block = cln->data; - break; - } - } - - if (block == NULL) { - cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t)); - if (cln == NULL) { - return NGX_ERROR; - } - - cln->handler = ngx_http_v3_unblock; - - block = cln->data; - block->queue.prev = NULL; - block->connection = c; - block->nblocked = &h3c->nblocked; - } - - if (block->queue.prev == NULL) { - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - - if (h3c->nblocked == h3scf->max_blocked_streams) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client exceeded http3_max_blocked_streams limit"); - - ngx_http_v3_finalize_connection(c, - NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED, - "too many blocked streams"); - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - h3c->nblocked++; - ngx_queue_insert_tail(&h3c->blocked, &block->queue); - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 blocked:%ui", h3c->nblocked); - - return NGX_BUSY; -} - - -static void -ngx_http_v3_unblock(void *data) -{ - ngx_http_v3_block_t *block = data; - - if (block->queue.prev) { - ngx_queue_remove(&block->queue); - block->queue.prev = NULL; - (*block->nblocked)--; - } -} - - -static ngx_int_t -ngx_http_v3_new_entry(ngx_connection_t *c) -{ - ngx_queue_t *q; - ngx_connection_t *bc; - ngx_http_v3_block_t *block; - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 new dynamic entry, blocked:%ui", h3c->nblocked); - - while (!ngx_queue_empty(&h3c->blocked)) { - q = ngx_queue_head(&h3c->blocked); - block = (ngx_http_v3_block_t *) q; - bc = block->connection; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, bc->log, 0, "http3 unblock stream"); - - ngx_http_v3_unblock(block); - ngx_post_event(bc->read, &ngx_posted_events); - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value) -{ - switch (id) { - - case NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY: - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 param QPACK_MAX_TABLE_CAPACITY:%uL", value); - break; - - case NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE: - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 param SETTINGS_MAX_HEADER_LIST_SIZE:%uL", value); - break; - - case NGX_HTTP_V3_PARAM_BLOCKED_STREAMS: - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 param QPACK_BLOCKED_STREAMS:%uL", value); - break; - - default: - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 param #%uL:%uL", id, value); - } - - return NGX_OK; -} diff --git a/src/http/v3/ngx_http_v3_tables.h b/src/http/v3/ngx_http_v3_tables.h deleted file mode 100644 index 991cc12c9..000000000 --- a/src/http/v3/ngx_http_v3_tables.h +++ /dev/null @@ -1,53 +0,0 @@ - -/* - * Copyright (C) Roman Arutyunyan - * Copyright (C) Nginx, Inc. - */ - - -#ifndef _NGX_HTTP_V3_TABLES_H_INCLUDED_ -#define _NGX_HTTP_V3_TABLES_H_INCLUDED_ - - -#include -#include -#include - - -typedef struct { - ngx_str_t name; - ngx_str_t value; -} ngx_http_v3_field_t; - - -typedef struct { - ngx_http_v3_field_t **elts; - ngx_uint_t nelts; - ngx_uint_t base; - size_t size; - size_t capacity; -} ngx_http_v3_dynamic_table_t; - - -void ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c); -ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, - ngx_uint_t index, ngx_str_t *value); -ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, - ngx_str_t *value); -ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); -ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index); -ngx_int_t ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); -ngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, - ngx_str_t *name, ngx_str_t *value); -ngx_int_t ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, - ngx_str_t *name, ngx_str_t *value); -ngx_int_t ngx_http_v3_decode_insert_count(ngx_connection_t *c, - ngx_uint_t *insert_count); -ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, - ngx_uint_t insert_count); -ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, - uint64_t value); - - -#endif /* _NGX_HTTP_V3_TABLES_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c new file mode 100644 index 000000000..f0a599655 --- /dev/null +++ b/src/http/v3/ngx_http_v3_uni.c @@ -0,0 +1,733 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_http_v3_parse_uni_t parse; + ngx_int_t index; +} ngx_http_v3_uni_stream_t; + + +typedef struct { + ngx_queue_t queue; + uint64_t id; + ngx_connection_t *connection; + ngx_uint_t *npushing; +} ngx_http_v3_push_t; + + +static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); +static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); +static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); +static void ngx_http_v3_push_cleanup(void *data); +static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, + ngx_uint_t type); + + +void +ngx_http_v3_init_uni_stream(ngx_connection_t *c) +{ + uint64_t n; + ngx_http_v3_uni_stream_t *us; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); + + n = c->quic->id >> 2; + + if (n >= NGX_HTTP_V3_MAX_UNI_STREAMS) { + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "reached maximum number of uni streams"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); + return; + } + + c->quic->cancelable = 1; + + us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); + if (us == NULL) { + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "memory allocation error"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); + return; + } + + us->index = -1; + + c->data = us; + + c->read->handler = ngx_http_v3_uni_read_handler; + c->write->handler = ngx_http_v3_dummy_write_handler; + + ngx_http_v3_uni_read_handler(c->read); +} + + +static void +ngx_http_v3_close_uni_stream(ngx_connection_t *c) +{ + ngx_pool_t *pool; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 close stream"); + + us = c->data; + + if (us && us->index >= 0) { + h3c = ngx_http_v3_get_session(c); + h3c->known_streams[us->index] = NULL; + } + + c->destroyed = 1; + + pool = c->pool; + + ngx_close_connection(c); + + ngx_destroy_pool(pool); +} + + +ngx_int_t +ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type) +{ + ngx_int_t index; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + h3c = ngx_http_v3_get_session(c); + + switch (type) { + + case NGX_HTTP_V3_STREAM_ENCODER: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 encoder stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_ENCODER; + break; + + case NGX_HTTP_V3_STREAM_DECODER: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 decoder stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_DECODER; + break; + + case NGX_HTTP_V3_STREAM_CONTROL: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 control stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_CONTROL; + + break; + + default: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 stream 0x%02xL", type); + + if (h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_ENCODER] == NULL + || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_DECODER] == NULL + || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_CONTROL] == NULL) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "missing mandatory stream"); + return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; + } + + index = -1; + } + + if (index >= 0) { + if (h3c->known_streams[index]) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); + return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; + } + + h3c->known_streams[index] = c; + + us = c->data; + us->index = index; + } + + return NGX_OK; +} + + +static void +ngx_http_v3_uni_read_handler(ngx_event_t *rev) +{ + u_char buf[128]; + ssize_t n; + ngx_buf_t b; + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + c = rev->data; + us = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler"); + + ngx_memzero(&b, sizeof(ngx_buf_t)); + + while (rev->ready) { + + n = c->recv(c, buf, sizeof(buf)); + + if (n == NGX_ERROR) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; + goto failed; + } + + if (n == 0) { + if (us->index >= 0) { + rc = NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM; + goto failed; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read eof"); + ngx_http_v3_close_uni_stream(c); + return; + } + + if (n == NGX_AGAIN) { + break; + } + + b.pos = buf; + b.last = buf + n; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (ngx_http_v3_check_flood(c) != NGX_OK) { + ngx_http_v3_close_uni_stream(c); + return; + } + + rc = ngx_http_v3_parse_uni(c, &us->parse, &b); + + if (rc == NGX_DONE) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 read done"); + ngx_http_v3_close_uni_stream(c); + return; + } + + if (rc > 0) { + goto failed; + } + + if (rc != NGX_AGAIN) { + rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; + goto failed; + } + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; + goto failed; + } + + return; + +failed: + + ngx_http_v3_finalize_connection(c, rc, "stream error"); + ngx_http_v3_close_uni_stream(c); +} + + +static void +ngx_http_v3_dummy_write_handler(ngx_event_t *wev) +{ + ngx_connection_t *c; + + c = wev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy write handler"); + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + NULL); + ngx_http_v3_close_uni_stream(c); + } +} + + +/* XXX async & buffered stream writes */ + +ngx_connection_t * +ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 2]; + size_t n; + ngx_connection_t *sc; + ngx_pool_cleanup_t *cln; + ngx_http_v3_push_t *push; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 create push stream id:%uL", push_id); + + sc = ngx_quic_open_stream(c, 0); + if (sc == NULL) { + goto failed; + } + + p = buf; + p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_STREAM_PUSH); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, push_id); + n = p - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (sc->send(sc, buf, n) != (ssize_t) n) { + goto failed; + } + + cln = ngx_pool_cleanup_add(sc->pool, sizeof(ngx_http_v3_push_t)); + if (cln == NULL) { + goto failed; + } + + h3c->npushing++; + + cln->handler = ngx_http_v3_push_cleanup; + + push = cln->data; + push->id = push_id; + push->connection = sc; + push->npushing = &h3c->npushing; + + ngx_queue_insert_tail(&h3c->pushing, &push->queue); + + return sc; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create push stream"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "failed to create push stream"); + if (sc) { + ngx_http_v3_close_uni_stream(sc); + } + + return NULL; +} + + +static void +ngx_http_v3_push_cleanup(void *data) +{ + ngx_http_v3_push_t *push = data; + + ngx_queue_remove(&push->queue); + (*push->npushing)--; +} + + +static ngx_connection_t * +ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) +{ + u_char buf[NGX_HTTP_V3_VARLEN_INT_LEN]; + size_t n; + ngx_int_t index; + ngx_connection_t *sc; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + switch (type) { + case NGX_HTTP_V3_STREAM_ENCODER: + index = NGX_HTTP_V3_STREAM_SERVER_ENCODER; + break; + case NGX_HTTP_V3_STREAM_DECODER: + index = NGX_HTTP_V3_STREAM_SERVER_DECODER; + break; + case NGX_HTTP_V3_STREAM_CONTROL: + index = NGX_HTTP_V3_STREAM_SERVER_CONTROL; + break; + default: + index = -1; + } + + h3c = ngx_http_v3_get_session(c); + + if (index >= 0) { + if (h3c->known_streams[index]) { + return h3c->known_streams[index]; + } + } + + sc = ngx_quic_open_stream(c, 0); + if (sc == NULL) { + goto failed; + } + + sc->quic->cancelable = 1; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 create uni stream, type:%ui", type); + + us = ngx_pcalloc(sc->pool, sizeof(ngx_http_v3_uni_stream_t)); + if (us == NULL) { + goto failed; + } + + us->index = index; + + sc->data = us; + + sc->read->handler = ngx_http_v3_uni_read_handler; + sc->write->handler = ngx_http_v3_dummy_write_handler; + + if (index >= 0) { + h3c->known_streams[index] = sc; + } + + n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (sc->send(sc, buf, n) != (ssize_t) n) { + goto failed; + } + + return sc; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create server stream"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "failed to create server stream"); + if (sc) { + ngx_http_v3_close_uni_stream(sc); + } + + return NULL; +} + + +ngx_int_t +ngx_http_v3_send_settings(ngx_connection_t *c) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; + size_t n; + ngx_connection_t *cc; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); + + cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); + if (cc == NULL) { + return NGX_ERROR; + } + + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + n = ngx_http_v3_encode_varlen_int(NULL, + NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); + n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_table_capacity); + n += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); + n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_blocked_streams); + + p = (u_char *) ngx_http_v3_encode_varlen_int(buf, + NGX_HTTP_V3_FRAME_SETTINGS); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, + NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_table_capacity); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, + NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_blocked_streams); + n = p - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (cc->send(cc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send settings"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send settings"); + ngx_http_v3_close_uni_stream(cc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3]; + size_t n; + ngx_connection_t *cc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send goaway %uL", id); + + cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); + if (cc == NULL) { + return NGX_ERROR; + } + + n = ngx_http_v3_encode_varlen_int(NULL, id); + p = (u_char *) ngx_http_v3_encode_varlen_int(buf, NGX_HTTP_V3_FRAME_GOAWAY); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, id); + n = p - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (cc->send(cc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send goaway"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send goaway"); + ngx_http_v3_close_uni_stream(cc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 send section acknowledgement %ui", stream_id); + + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0x80; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 7) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "failed to send section acknowledgement"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send section acknowledgement"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 send stream cancellation %ui", stream_id); + + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0x40; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 6) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send stream cancellation"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send stream cancellation"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 send insert count increment %ui", inc); + + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, inc, 6) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "failed to send insert count increment"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send insert count increment"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) +{ + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 MAX_PUSH_ID:%uL", max_push_id); + + if (h3c->max_push_id != (uint64_t) -1 && max_push_id < h3c->max_push_id) { + return NGX_HTTP_V3_ERR_ID_ERROR; + } + + h3c->max_push_id = max_push_id; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id) +{ + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 GOAWAY:%uL", push_id); + + h3c->goaway_push_id = push_id; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) +{ + ngx_queue_t *q; + ngx_http_request_t *r; + ngx_http_v3_push_t *push; + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 CANCEL_PUSH:%uL", push_id); + + if (push_id >= h3c->next_push_id) { + return NGX_HTTP_V3_ERR_ID_ERROR; + } + + for (q = ngx_queue_head(&h3c->pushing); + q != ngx_queue_sentinel(&h3c->pushing); + q = ngx_queue_next(&h3c->pushing)) + { + push = (ngx_http_v3_push_t *) q; + + if (push->id != push_id) { + continue; + } + + r = push->connection->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 cancel push"); + + ngx_http_finalize_request(r, NGX_HTTP_CLOSE); + + break; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 cancel stream %ui", stream_id); + + /* we do not use dynamic tables */ + + return NGX_OK; +} diff --git a/src/http/v3/ngx_http_v3_uni.h b/src/http/v3/ngx_http_v3_uni.h new file mode 100644 index 000000000..8c1eb99d3 --- /dev/null +++ b/src/http/v3/ngx_http_v3_uni.h @@ -0,0 +1,38 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_UNI_H_INCLUDED_ +#define _NGX_HTTP_V3_UNI_H_INCLUDED_ + + +#include +#include +#include + + +void ngx_http_v3_init_uni_stream(ngx_connection_t *c); +ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type); + +ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, + uint64_t push_id); +ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, + uint64_t max_push_id); +ngx_int_t ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id); +ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); +ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); + +ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); +ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); +ngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c, + ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, + ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, + ngx_uint_t inc); + + +#endif /* _NGX_HTTP_V3_UNI_H_INCLUDED_ */ -- cgit From 9860a82b195afb9c8d7a98536c752927b677736b Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 7 Dec 2021 15:49:30 +0300 Subject: HTTP/3: avoid sending stream cancellation for pushed streams. --- src/http/v3/ngx_http_v3_request.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index e103a7eca..6faa3ee0b 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -405,7 +405,9 @@ ngx_http_v3_reset_connection(ngx_connection_t *c) } #endif - if (h3scf->max_table_capacity > 0 && !c->read->eof) { + if (h3scf->max_table_capacity > 0 && !c->read->eof + && (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) + { (void) ngx_http_v3_send_cancel_stream(c, c->quic->id); } -- cgit From a42a62fc583ad311d06a90e3bcd12d63df0701a4 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 7 Dec 2021 15:49:51 +0300 Subject: QUIC: clear SSL_OP_ENABLE_MIDDLEBOX_COMPAT on SSL context switch. The SSL_OP_ENABLE_MIDDLEBOX_COMPAT option is provided by QuicTLS and enabled by default in the newly created SSL contexts. SSL_set_quic_method() is used to clear it, which is required for SSL handshake to work on QUIC connections. Switching context in the ngx_http_ssl_servername() SNI callback overrides SSL options from the new SSL context. This results in the option set again. Fix is to explicitly clear it when switching to another SSL context. Initially reported here (in Russian): http://mailman.nginx.org/pipermail/nginx-ru/2021-November/063989.html --- src/http/ngx_http_request.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index e8907c100..32a26379c 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -953,6 +953,14 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) #ifdef SSL_OP_NO_RENEGOTIATION SSL_set_options(ssl_conn, SSL_OP_NO_RENEGOTIATION); +#endif + +#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT +#if (NGX_HTTP_QUIC) + if (c->listening->quic) { + SSL_clear_options(ssl_conn, SSL_OP_ENABLE_MIDDLEBOX_COMPAT); + } +#endif #endif } -- cgit From dea9a208c308ba29135ed1dab4eb1768e98b8b57 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 8 Dec 2021 17:04:56 +0300 Subject: HTTP/3: cleanup after "listen .. quic" removal in be08b858086a. --- src/http/ngx_http_core_module.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index 822c7d264..6ee05f321 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -238,7 +238,6 @@ struct ngx_http_addr_conf_s { ngx_http_virtual_names_t *virtual_names; unsigned ssl:1; - unsigned quic:1; unsigned http2:1; unsigned http3:1; unsigned proxy_protocol:1; -- cgit From 702a0986f393390d25368180e619e40160fe92da Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 9 Dec 2021 11:15:25 +0300 Subject: QUIC: fixed e06283038ec8 mis-merge. The NGX_HTTP_QUIC macro was removed in 33226ac61076. --- src/http/ngx_http_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 32a26379c..12a8cd144 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -956,7 +956,7 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) #endif #ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT -#if (NGX_HTTP_QUIC) +#if (NGX_HTTP_V3) if (c->listening->quic) { SSL_clear_options(ssl_conn, SSL_OP_ENABLE_MIDDLEBOX_COMPAT); } -- cgit From 05a32b5ec44e8fb54ea1976ec025a6ea650c36f9 Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Tue, 28 Dec 2021 15:01:02 +0300 Subject: Fixed a mismerge in 5c86189a1c1b. --- src/http/ngx_http.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index a5bdeaadd..91731c812 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -176,6 +176,7 @@ ngx_int_t ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types, ngx_uint_t ngx_http_degraded(ngx_http_request_t *); #endif + #if (NGX_HTTP_V2 || NGX_HTTP_V3) ngx_int_t ngx_http_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst, ngx_uint_t last, ngx_log_t *log); @@ -183,6 +184,7 @@ size_t ngx_http_huff_encode(u_char *src, size_t len, u_char *dst, ngx_uint_t lower); #endif + extern ngx_module_t ngx_http_module; extern ngx_str_t ngx_http_html_default_types[]; -- cgit From 7f0fdd4e149380c9439e63bea7625c5071781af6 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 29 Dec 2021 15:33:51 +0300 Subject: Style. --- src/http/v3/ngx_http_v3_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 6faa3ee0b..b97caec95 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -628,7 +628,7 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, } } - if (name->len && name->data[0] == ':') { + if (name->len && name->data[0] == ':') { return ngx_http_v3_process_pseudo_header(r, name, value); } -- cgit From b1356ade078a8e4092943a2b7b5d3f92dd4def93 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 30 Dec 2021 12:59:32 +0300 Subject: HTTP/3: improved processing of multiple Cookie field lines. As per draft-ietf-quic-http, 4.1.1.2, and similar to HTTP/2 specification, they ought to be concatenated. This closely follows ngx_http_v2_module. --- src/http/v3/ngx_http_v3.h | 1 + src/http/v3/ngx_http_v3_request.c | 165 ++++++++++++++++++++++++++++++++++---- 2 files changed, 152 insertions(+), 14 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index b7951e9bb..956c824a2 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -123,6 +123,7 @@ struct ngx_http_v3_parse_s { size_t header_limit; ngx_http_v3_parse_headers_t headers; ngx_http_v3_parse_data_t body; + ngx_array_t *cookies; }; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index b97caec95..4dbda3596 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -25,6 +25,8 @@ static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); static ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r); static ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_cookie(ngx_http_request_t *r, ngx_str_t *value); +static ngx_int_t ngx_http_v3_construct_cookie_header(ngx_http_request_t *r); static void ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r); static ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r, @@ -601,6 +603,8 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, ngx_http_core_srv_conf_t *cscf; ngx_http_core_main_conf_t *cmcf; + static ngx_str_t cookie = ngx_string("cookie"); + len = name->len + value->len; if (len > r->v3_parse->header_limit) { @@ -636,24 +640,34 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, return NGX_ERROR; } - h = ngx_list_push(&r->headers_in.headers); - if (h == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_ERROR; - } + if (name->len == cookie.len + && ngx_memcmp(name->data, cookie.data, cookie.len) == 0) + { + if (ngx_http_v3_cookie(r, value) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } - h->key = *name; - h->value = *value; - h->lowcase_key = h->key.data; - h->hash = ngx_hash_key(h->key.data, h->key.len); + } else { + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + h->key = *name; + h->value = *value; + h->lowcase_key = h->key.data; + h->hash = ngx_hash_key(h->key.data, h->key.len); - hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, - h->lowcase_key, h->key.len); + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { - return NGX_ERROR; + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { + return NGX_ERROR; + } } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -981,6 +995,10 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r) return NGX_ERROR; } + if (ngx_http_v3_construct_cookie_header(r) != NGX_OK) { + return NGX_ERROR; + } + if (r->headers_in.server.len == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent neither \":authority\" nor \"Host\" header"); @@ -1056,6 +1074,125 @@ failed: } +static ngx_int_t +ngx_http_v3_cookie(ngx_http_request_t *r, ngx_str_t *value) +{ + ngx_str_t *val; + ngx_array_t *cookies; + + cookies = r->v3_parse->cookies; + + if (cookies == NULL) { + cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t)); + if (cookies == NULL) { + return NGX_ERROR; + } + + r->v3_parse->cookies = cookies; + } + + val = ngx_array_push(cookies); + if (val == NULL) { + return NGX_ERROR; + } + + *val = *value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_construct_cookie_header(ngx_http_request_t *r) +{ + u_char *buf, *p, *end; + size_t len; + ngx_str_t *vals; + ngx_uint_t i; + ngx_array_t *cookies; + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + static ngx_str_t cookie = ngx_string("cookie"); + + cookies = r->v3_parse->cookies; + + if (cookies == NULL) { + return NGX_OK; + } + + vals = cookies->elts; + + i = 0; + len = 0; + + do { + len += vals[i].len + 2; + } while (++i != cookies->nelts); + + len -= 2; + + buf = ngx_pnalloc(r->pool, len + 1); + if (buf == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + p = buf; + end = buf + len; + + for (i = 0; /* void */ ; i++) { + + p = ngx_cpymem(p, vals[i].data, vals[i].len); + + if (p == end) { + *p = '\0'; + break; + } + + *p++ = ';'; *p++ = ' '; + } + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash( + ngx_hash('c', 'o'), 'o'), 'k'), 'i'), 'e'); + + h->key.len = cookie.len; + h->key.data = cookie.data; + + h->value.len = len; + h->value.data = buf; + + h->lowcase_key = cookie.data; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (hh->handler(r, h, hh->offset) != NGX_OK) { + /* + * request has been finalized already + * in ngx_http_process_multi_header_lines() + */ + return NGX_ERROR; + } + + return NGX_OK; +} + + ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r) { -- cgit From 109166e4fa41cb35a91107b0be075aec22a567eb Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 12 Jan 2022 11:54:39 +0300 Subject: QUIC: modified HTTP version test. The new condition produces smaller diff to the default branch and is similar to HTTP/2 case. --- src/http/ngx_http_request_body.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index d2bec7820..afb042395 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -942,7 +942,14 @@ ngx_http_test_expect(ngx_http_request_t *r) if (r->expect_tested || r->headers_in.expect == NULL - || r->http_version != NGX_HTTP_VERSION_11) + || r->http_version < NGX_HTTP_VERSION_11 +#if (NGX_HTTP_V2) + || r->stream != NULL +#endif +#if (NGX_HTTP_V3) + || r->connection->quic != NULL +#endif + ) { return NGX_OK; } -- cgit From 5ab94d4219fb2119770d024731a53dba6871b25c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 12 Jan 2022 11:57:06 +0300 Subject: HTTP/3: simplified code. --- src/http/ngx_http_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 12a8cd144..fd3e880f8 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2970,7 +2970,7 @@ ngx_http_test_reading(ngx_http_request_t *r) #if (NGX_HTTP_V3) if (c->quic) { - if (c->read->error) { + if (rev->error) { err = 0; goto closed; } -- cgit From 38cfe35779e4a8a6288d61bdf4a2e892dc0ce046 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 12 Jan 2022 11:57:46 +0300 Subject: HTTP/3: set c->error on read error in ngx_http_test_reading(). Similar to other error/eof cases. --- src/http/ngx_http_request.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index fd3e880f8..bc6c077db 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2971,6 +2971,7 @@ ngx_http_test_reading(ngx_http_request_t *r) if (c->quic) { if (rev->error) { + c->error = 1; err = 0; goto closed; } -- cgit From a7a3a8cc1778fee8597b34ce5265453ceda81b08 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 13 Jan 2022 15:57:15 +0300 Subject: HTTP/3: removed useless warning regarding OpenSSL library. After 0e6528551f26, it became impossible to run into this path. --- src/http/ngx_http.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 4886a88bf..082a849a2 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1360,17 +1360,6 @@ ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, &lsopt->addr_text); } -#endif - -#if (NGX_HTTP_V3 && !defined NGX_QUIC) - - if (lsopt->http3) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "nginx was built with OpenSSL that lacks QUIC " - "support, HTTP/3 is not enabled for %V", - &lsopt->addr_text); - } - #endif addr = ngx_array_push(&port->addrs); -- cgit From 1f97aa71ecaa75dfd646495a13534b10405b500c Mon Sep 17 00:00:00 2001 From: Vladimir Homutov Date: Tue, 18 Jan 2022 12:49:55 +0300 Subject: QUIC: the "quic_active_connection_id_limit" directive. The directive sets corresponding transport parameter and limits number of created client ids. --- src/http/v3/ngx_http_v3_module.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 455b613e1..d274a3bf2 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -104,6 +104,13 @@ static ngx_command_t ngx_http_v3_commands[] = { 0, 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 }; @@ -240,6 +247,7 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) h3scf->quic.gso_enabled = NGX_CONF_UNSET; h3scf->quic.stream_close_code = NGX_HTTP_V3_ERR_NO_ERROR; h3scf->quic.stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; + h3scf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT; return h3scf; } @@ -280,6 +288,10 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_str_value(conf->quic.host_key, prev->quic.host_key, ""); + ngx_conf_merge_uint_value(conf->quic.active_connection_id_limit, + prev->quic.active_connection_id_limit, + 2); + if (conf->quic.host_key.len == 0) { conf->quic.host_key.len = NGX_QUIC_DEFAULT_HOST_KEY_LEN; -- cgit From 5efdec71581f9402dd7132267d68240ab2f8870b Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 26 Jan 2022 14:15:40 +0300 Subject: HTTP/3: removed draft versions support in ALPN. --- src/http/modules/ngx_http_ssl_module.c | 15 --------------- src/http/v3/ngx_http_v3.h | 3 --- 2 files changed, 18 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 1ecbe439f..64ad9ee2f 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -419,9 +419,6 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { -#if (NGX_HTTP_V3) - const char *fmt; -#endif unsigned int srvlen; unsigned char *srv; #if (NGX_DEBUG) @@ -466,23 +463,11 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, if (h3scf->hq) { srv = (unsigned char *) NGX_HTTP_V3_HQ_ALPN_PROTO; srvlen = sizeof(NGX_HTTP_V3_HQ_ALPN_PROTO) - 1; - fmt = NGX_HTTP_V3_HQ_ALPN_DRAFT_FMT; } else #endif { srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO; srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1; - fmt = NGX_HTTP_V3_ALPN_DRAFT_FMT; - } - - /* QUIC draft */ - - if (ngx_quic_version(c) > 1) { - srv = ngx_pnalloc(c->pool, sizeof("\x05h3-xx") - 1); - if (srv == NULL) { - return SSL_TLSEXT_ERR_NOACK; - } - srvlen = ngx_sprintf(srv, fmt, ngx_quic_version(c)) - srv; } } else diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 956c824a2..ea78ac1f3 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -20,10 +20,7 @@ #define NGX_HTTP_V3_ALPN_PROTO "\x02h3" -#define NGX_HTTP_V3_ALPN_DRAFT_FMT "\x05h3-%02uD" - #define NGX_HTTP_V3_HQ_ALPN_PROTO "\x0Ahq-interop" -#define NGX_HTTP_V3_HQ_ALPN_DRAFT_FMT "\x05hq-%02uD" #define NGX_HTTP_V3_VARLEN_INT_LEN 4 #define NGX_HTTP_V3_PREFIX_INT_LEN 11 -- cgit From acb585518d868a5051c1caa85add006d975751f5 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 31 Jan 2022 09:16:47 +0300 Subject: QUIC: allowed main QUIC connection for some operations. Operations like ngx_quic_open_stream(), ngx_http_quic_get_connection(), ngx_http_v3_finalize_connection(), ngx_http_v3_shutdown_connection() used to receive a QUIC stream connection. Now they can receive the main QUIC connection as well. This is useful when calling them from a stream context. --- src/http/v3/ngx_http_v3.c | 4 ++-- src/http/v3/ngx_http_v3.h | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index 97d8a5e34..29b07e025 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -78,8 +78,8 @@ ngx_http_v3_keepalive_handler(ngx_event_t *ev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 keepalive handler"); - ngx_quic_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, - "keepalive timeout"); + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "keepalive timeout"); } diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index ea78ac1f3..b54d9aee0 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -78,7 +78,8 @@ #define ngx_http_quic_get_connection(c) \ - ((ngx_http_connection_t *) (c)->quic->parent->data) + ((ngx_http_connection_t *) ((c)->quic ? (c)->quic->parent->data \ + : (c)->data)) #define ngx_http_v3_get_session(c) ngx_http_quic_get_connection(c)->v3_session @@ -91,10 +92,12 @@ module) #define ngx_http_v3_finalize_connection(c, code, reason) \ - ngx_quic_finalize_connection(c->quic->parent, code, reason) + ngx_quic_finalize_connection((c)->quic ? (c)->quic->parent : (c), \ + code, reason) #define ngx_http_v3_shutdown_connection(c, code, reason) \ - ngx_quic_shutdown_connection(c->quic->parent, code, reason) + ngx_quic_shutdown_connection((c)->quic ? (c)->quic->parent : (c), \ + code, reason) typedef struct { -- cgit From c1b172f1d67f806c84dc088ddc57ed76514e85dd Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 27 Jan 2022 12:20:47 +0300 Subject: HTTP/3: delayed Insert Count Increment instruction. Sending the instruction is delayed until the end of the current event cycle. Delaying the instruction is allowed by quic-qpack-21, section 2.2.2.3. The goal is to reduce the amount of data sent back to client by accumulating several inserts in one instruction and sometimes not sending the instruction at all, if Section Acknowledgement was sent just before it. --- src/http/v3/ngx_http_v3.c | 8 +++++++ src/http/v3/ngx_http_v3_parse.c | 2 ++ src/http/v3/ngx_http_v3_table.c | 49 +++++++++++++++++++++++++++++++++++++---- src/http/v3/ngx_http_v3_table.h | 5 +++++ 4 files changed, 60 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index 29b07e025..84bb8f601 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -47,6 +47,10 @@ ngx_http_v3_init_session(ngx_connection_t *c) h3c->keepalive.handler = ngx_http_v3_keepalive_handler; h3c->keepalive.cancelable = 1; + h3c->table.send_insert_count.log = pc->log; + h3c->table.send_insert_count.data = pc; + h3c->table.send_insert_count.handler = ngx_http_v3_inc_insert_count_handler; + cln = ngx_pool_cleanup_add(pc->pool, 0); if (cln == NULL) { goto failed; @@ -93,6 +97,10 @@ ngx_http_v3_cleanup_session(void *data) if (h3c->keepalive.timer_set) { ngx_del_timer(&h3c->keepalive); } + + if (h3c->table.send_insert_count.posted) { + ngx_delete_posted_event(&h3c->table.send_insert_count); + } } diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 25c1edf6d..cd70bd3bf 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -395,6 +395,8 @@ done: if (ngx_http_v3_send_ack_section(c, c->quic->id) != NGX_OK) { return NGX_ERROR; } + + ngx_http_v3_ack_insert_count(c, st->prefix.insert_count); } st->state = sw_start; diff --git a/src/http/v3/ngx_http_v3_table.c b/src/http/v3/ngx_http_v3_table.c index c6d543ac4..22dc37901 100644 --- a/src/http/v3/ngx_http_v3_table.c +++ b/src/http/v3/ngx_http_v3_table.c @@ -232,11 +232,9 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) dt->elts[dt->nelts++] = field; dt->size += size; - /* TODO increment can be sent less often */ + dt->insert_count++; - if (ngx_http_v3_send_inc_insert_count(c, 1) != NGX_OK) { - return NGX_ERROR; - } + ngx_post_event(&dt->send_insert_count, &ngx_posted_events); if (ngx_http_v3_new_entry(c) != NGX_OK) { return NGX_ERROR; @@ -246,6 +244,34 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) } +void +ngx_http_v3_inc_insert_count_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + c = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 inc insert count handler"); + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->insert_count > dt->ack_insert_count) { + if (ngx_http_v3_send_inc_insert_count(c, + dt->insert_count - dt->ack_insert_count) + != NGX_OK) + { + return; + } + + dt->ack_insert_count = dt->insert_count; + } +} + + ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) { @@ -607,6 +633,21 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) } +void +ngx_http_v3_ack_insert_count(ngx_connection_t *c, uint64_t insert_count) +{ + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->ack_insert_count < insert_count) { + dt->ack_insert_count = insert_count; + } +} + + static void ngx_http_v3_unblock(void *data) { diff --git a/src/http/v3/ngx_http_v3_table.h b/src/http/v3/ngx_http_v3_table.h index 6f88b35b9..1c2fb17b9 100644 --- a/src/http/v3/ngx_http_v3_table.h +++ b/src/http/v3/ngx_http_v3_table.h @@ -26,9 +26,13 @@ typedef struct { ngx_uint_t base; size_t size; size_t capacity; + uint64_t insert_count; + uint64_t ack_insert_count; + ngx_event_t send_insert_count; } ngx_http_v3_dynamic_table_t; +void ngx_http_v3_inc_insert_count_handler(ngx_event_t *ev); void ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c); ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *value); @@ -46,6 +50,7 @@ ngx_int_t ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count); ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count); +void ngx_http_v3_ack_insert_count(ngx_connection_t *c, uint64_t insert_count); ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value); -- cgit From 77efe512fa18580bef6f8d795bc1c1cfff5d0097 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 31 Jan 2022 09:46:30 +0300 Subject: HTTP/3: proper uni stream closure detection. Previously, closure detection for server-initiated uni streams was not properly implemented. Instead, HTTP/3 code relied on QUIC code posting the read event and setting rev->error when it needed to close the stream. Then, regular uni stream read handler called c->recv() and received error, which closed the stream. This was an ad-hoc solution. If, for whatever reason, the read handler was called earlier, c->recv() would return 0, which would also close the stream. Now server-initiated uni streams have a separate read event handler for tracking stream closure. The handler calls c->recv(), which normally returns 0, but may return error in case of closure. --- src/http/v3/ngx_http_v3_uni.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c index f0a599655..bd7eb278b 100644 --- a/src/http/v3/ngx_http_v3_uni.c +++ b/src/http/v3/ngx_http_v3_uni.c @@ -26,7 +26,8 @@ typedef struct { static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); -static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); +static void ngx_http_v3_uni_dummy_read_handler(ngx_event_t *wev); +static void ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev); static void ngx_http_v3_push_cleanup(void *data); static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type); @@ -68,7 +69,7 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) c->data = us; c->read->handler = ngx_http_v3_uni_read_handler; - c->write->handler = ngx_http_v3_dummy_write_handler; + c->write->handler = ngx_http_v3_uni_dummy_write_handler; ngx_http_v3_uni_read_handler(c->read); } @@ -252,7 +253,33 @@ failed: static void -ngx_http_v3_dummy_write_handler(ngx_event_t *wev) +ngx_http_v3_uni_dummy_read_handler(ngx_event_t *rev) +{ + u_char ch; + ngx_connection_t *c; + + c = rev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy read handler"); + + if (rev->ready) { + if (c->recv(c, &ch, 1) != 0) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, NULL); + ngx_http_v3_close_uni_stream(c); + return; + } + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + NULL); + ngx_http_v3_close_uni_stream(c); + } +} + + +static void +ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev) { ngx_connection_t *c; @@ -393,8 +420,8 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) sc->data = us; - sc->read->handler = ngx_http_v3_uni_read_handler; - sc->write->handler = ngx_http_v3_dummy_write_handler; + sc->read->handler = ngx_http_v3_uni_dummy_read_handler; + sc->write->handler = ngx_http_v3_uni_dummy_write_handler; if (index >= 0) { h3c->known_streams[index] = sc; @@ -409,6 +436,8 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) goto failed; } + ngx_post_event(sc->read, &ngx_posted_events); + return sc; failed: -- cgit From 7c4b4f4d540de7959311ab0f6bfafac5ab46f67b Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Sat, 5 Feb 2022 12:54:54 +0300 Subject: QUIC: stream lingering. Now ngx_quic_stream_t is decoupled from ngx_connection_t in a way that it can persist after connection is closed by application. During this period, server is expecting stream final size from client for correct flow control. Also, buffered output is sent to client as more flow control credit is granted. --- src/http/v3/ngx_http_v3_uni.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c index bd7eb278b..96b7d7ebf 100644 --- a/src/http/v3/ngx_http_v3_uni.c +++ b/src/http/v3/ngx_http_v3_uni.c @@ -295,8 +295,6 @@ ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev) } -/* XXX async & buffered stream writes */ - ngx_connection_t * ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) { -- cgit From 9d81ef744cdaacf1e52bcaec4224d375af5ba59b Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 20 Apr 2022 16:01:17 +0400 Subject: QUIC: separate UDP framework for QUIC. Previously, QUIC used the existing UDP framework, which was created for UDP in Stream. However the way QUIC connections are created and looked up is different from the way UDP connections in Stream are created and looked up. Now these two implementations are decoupled. --- src/http/ngx_http.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/http') diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 082a849a2..134eed944 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1822,7 +1822,14 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) ls->wildcard = addr->opt.wildcard; #if (NGX_HTTP_V3) + ls->quic = addr->opt.http3; + + if (ls->quic) { + ngx_rbtree_init(&ls->rbtree, &ls->sentinel, + ngx_quic_rbtree_insert_value); + } + #endif return ls; -- cgit From 7123ff29167b3c51643438c33c4f9a9a39dd22a6 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 26 May 2022 16:17:56 +0400 Subject: HTTP/3: require that field section base index is not negative. RFC 9204 explicitly requires that. --- src/http/v3/ngx_http_v3_parse.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index cd70bd3bf..7dc53493c 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -474,7 +474,13 @@ done: } if (st->sign) { + if (st->insert_count <= st->delta_base) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent negative base"); + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + st->base = st->insert_count - st->delta_base - 1; + } else { st->base = st->insert_count + st->delta_base; } -- cgit From 854e41fec24e1f292ec5a951e7bfc9377afc0905 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 8 Jun 2022 16:19:01 +0400 Subject: HTTP/3: updated SETTINGS_MAX_FIELD_SECTION_SIZE name. --- src/http/v3/ngx_http_v3.h | 2 +- src/http/v3/ngx_http_v3_table.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index b54d9aee0..a3544ebc4 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -39,7 +39,7 @@ #define NGX_HTTP_V3_FRAME_MAX_PUSH_ID 0x0d #define NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY 0x01 -#define NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE 0x06 +#define NGX_HTTP_V3_PARAM_MAX_FIELD_SECTION_SIZE 0x06 #define NGX_HTTP_V3_PARAM_BLOCKED_STREAMS 0x07 #define NGX_HTTP_V3_MAX_TABLE_CAPACITY 4096 diff --git a/src/http/v3/ngx_http_v3_table.c b/src/http/v3/ngx_http_v3_table.c index 22dc37901..7e07a15b9 100644 --- a/src/http/v3/ngx_http_v3_table.c +++ b/src/http/v3/ngx_http_v3_table.c @@ -699,9 +699,10 @@ ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value) "http3 param QPACK_MAX_TABLE_CAPACITY:%uL", value); break; - case NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE: + case NGX_HTTP_V3_PARAM_MAX_FIELD_SECTION_SIZE: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 param SETTINGS_MAX_HEADER_LIST_SIZE:%uL", value); + "http3 param SETTINGS_MAX_FIELD_SECTION_SIZE:%uL", + value); break; case NGX_HTTP_V3_PARAM_BLOCKED_STREAMS: -- cgit From 5cde1259b2de257ce380813f03ef874c37632423 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 3 Aug 2022 16:59:51 +0400 Subject: HTTP/3: skip empty request body buffers (ticket #2374). When client DATA frame header and its content come in different QUIC packets, it may happen that only the header is processed by the first ngx_http_v3_request_body_filter() call. In this case an empty request body buffer is added to r->request_body->bufs, which is later reused in a subsequent ngx_http_v3_request_body_filter() call without being removed from the body chain. As a result, rb->request_body->bufs ends up with two copies of the same buffer. The fix is to avoid adding empty request body buffers to r->request_body->bufs. --- src/http/v3/ngx_http_v3_request.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 4dbda3596..14802249b 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1552,15 +1552,17 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) } /* rc == NGX_OK */ - } - if (max != -1 && (uint64_t) (max - rb->received) < st->length) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "client intended to send too large " - "body: %O+%ui bytes", - rb->received, st->length); + if (max != -1 && (uint64_t) (max - rb->received) < st->length) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client intended to send too large " + "body: %O+%ui bytes", + rb->received, st->length); - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + continue; } if (b -- cgit From 0da752661530c145b3c5d1a739115b1968219732 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 1 Nov 2022 17:00:35 +0400 Subject: Set default listen socket type in http. The type field was added in 7999d3fbb765 at early stages of QUIC implementation and was not initialized for default listen. Missing initialization resulted in default listen socket creation error. --- src/http/ngx_http_core_module.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/http') diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 78a9e5ea7..248fa80ea 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -3008,6 +3008,7 @@ ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) lsopt.socklen = sizeof(struct sockaddr_in); lsopt.backlog = NGX_LISTEN_BACKLOG; + lsopt.type = SOCK_STREAM; lsopt.rcvbuf = -1; lsopt.sndbuf = -1; #if (NGX_HAVE_SETFIB) -- cgit From 36d80a52693904d829d9a4e27361bcd29e41d48d Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 22 Nov 2022 14:10:04 +0400 Subject: HTTP/3: fixed server_name regex captures (ticket #2407). Previously, HTTP/3 stream connection didn't inherit the servername regex from the main QUIC connection saved when processing SNI and using regular expressions in server names. As a result, it didn't execute to set regex captures when choosing the virtual server while parsing HTTP/3 headers. --- src/http/v3/ngx_http_v3_request.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 14802249b..7921d8dc5 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -81,6 +81,7 @@ ngx_http_v3_init(ngx_connection_t *c) if (phc->ssl_servername) { hc->ssl_servername = phc->ssl_servername; + hc->ssl_servername_regex = phc->ssl_servername_regex; hc->conf_ctx = phc->conf_ctx; ngx_set_connection_log(c, clcf->error_log); -- cgit From 7d73c50a2d11314270663ebfa4665719c66634f4 Mon Sep 17 00:00:00 2001 From: Jiuzhou Cui Date: Fri, 25 Nov 2022 15:07:23 +0800 Subject: HTTP/3: fixed build without NGX_PCRE (broken by 0f5fc7a320db). --- src/http/v3/ngx_http_v3_request.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 7921d8dc5..969fdf356 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -81,7 +81,9 @@ ngx_http_v3_init(ngx_connection_t *c) if (phc->ssl_servername) { hc->ssl_servername = phc->ssl_servername; +#if (NGX_PCRE) hc->ssl_servername_regex = phc->ssl_servername_regex; +#endif hc->conf_ctx = phc->conf_ctx; ngx_set_connection_log(c, clcf->error_log); -- cgit From dc82bed893cc4292c459d41269882b621b98f5b3 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 29 Nov 2022 17:46:46 +0400 Subject: QUIC: reusable mode for main connection. The connection is automatically switched to this mode by transport layer when there are no non-cancelable streams. Currently, cancelable streams are HTTP/3 encoder/decoder/control streams. --- src/http/v3/ngx_http_v3_uni.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c index 96b7d7ebf..d6a6e97e6 100644 --- a/src/http/v3/ngx_http_v3_uni.c +++ b/src/http/v3/ngx_http_v3_uni.c @@ -52,7 +52,7 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) return; } - c->quic->cancelable = 1; + ngx_quic_cancelable_stream(c); us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { @@ -182,6 +182,11 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler"); + if (c->close) { + ngx_http_v3_close_uni_stream(c); + return; + } + ngx_memzero(&b, sizeof(ngx_buf_t)); while (rev->ready) { @@ -262,6 +267,11 @@ ngx_http_v3_uni_dummy_read_handler(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy read handler"); + if (c->close) { + ngx_http_v3_close_uni_stream(c); + return; + } + if (rev->ready) { if (c->recv(c, &ch, 1) != 0) { ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, NULL); @@ -404,7 +414,7 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) goto failed; } - sc->quic->cancelable = 1; + ngx_quic_cancelable_stream(sc); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create uni stream, type:%ui", type); -- cgit From dd4c31fc34bd8390dd3fa9e804afc15b57b69135 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 19 Oct 2022 17:45:30 +0400 Subject: HTTP/3: unified hq code with regular HTTP/3 code. The change removes hq-specific request handler. Now hq requests are handled by the HTTP/3 request handler. --- src/http/v3/ngx_http_v3.c | 24 ++++++-- src/http/v3/ngx_http_v3.h | 5 +- src/http/v3/ngx_http_v3_request.c | 118 ++++++++------------------------------ src/http/v3/ngx_http_v3_uni.c | 15 +++++ 4 files changed, 62 insertions(+), 100 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index 84bb8f601..b9956e68f 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -17,10 +17,13 @@ static void ngx_http_v3_cleanup_session(void *data); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c) { - ngx_connection_t *pc; - ngx_pool_cleanup_t *cln; - ngx_http_connection_t *hc; - ngx_http_v3_session_t *h3c; + ngx_connection_t *pc; + ngx_pool_cleanup_t *cln; + ngx_http_connection_t *hc; + ngx_http_v3_session_t *h3c; +#if (NGX_HTTP_V3_HQ) + ngx_http_v3_srv_conf_t *h3scf; +#endif pc = c->quic->parent; hc = pc->data; @@ -39,6 +42,13 @@ ngx_http_v3_init_session(ngx_connection_t *c) h3c->max_push_id = (uint64_t) -1; h3c->goaway_push_id = (uint64_t) -1; +#if (NGX_HTTP_V3_HQ) + h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); + if (h3scf->hq) { + h3c->hq = 1; + } +#endif + ngx_queue_init(&h3c->blocked); ngx_queue_init(&h3c->pushing); @@ -61,6 +71,12 @@ ngx_http_v3_init_session(ngx_connection_t *c) hc->v3_session = h3c; +#if (NGX_HTTP_V3_HQ) + if (h3c->hq) { + return NGX_OK; + } +#endif + return ngx_http_v3_send_settings(c); failed: diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index a3544ebc4..7ffd38768 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -145,7 +145,10 @@ struct ngx_http_v3_session_s { off_t total_bytes; off_t payload_bytes; - ngx_uint_t goaway; /* unsigned goaway:1; */ + unsigned goaway:1; +#if (NGX_HTTP_V3_HQ) + unsigned hq:1; +#endif ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; }; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 969fdf356..bdb5331fe 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,9 +10,6 @@ #include -#if (NGX_HTTP_V3_HQ) -static void ngx_http_v3_init_hq_stream(ngx_connection_t *c); -#endif static void ngx_http_v3_init_request_stream(ngx_connection_t *c); static void ngx_http_v3_wait_request_handler(ngx_event_t *rev); static void ngx_http_v3_cleanup_request(void *data); @@ -89,13 +86,6 @@ ngx_http_v3_init(ngx_connection_t *c) ngx_set_connection_log(c, clcf->error_log); } -#if (NGX_HTTP_V3_HQ) - if (h3scf->hq) { - ngx_http_v3_init_hq_stream(c); - return; - } -#endif - if (ngx_http_v3_init_session(c) != NGX_OK) { ngx_http_close_connection(c); return; @@ -110,83 +100,12 @@ ngx_http_v3_init(ngx_connection_t *c) } -#if (NGX_HTTP_V3_HQ) - -static void -ngx_http_v3_init_hq_stream(ngx_connection_t *c) -{ - uint64_t n; - ngx_event_t *rev; - ngx_http_connection_t *hc; - ngx_http_core_loc_conf_t *clcf; - ngx_http_core_srv_conf_t *cscf; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init hq stream"); - -#if (NGX_STAT_STUB) - (void) ngx_atomic_fetch_add(ngx_stat_active, 1); -#endif - - hc = c->data; - - /* Use HTTP/3 General Protocol Error Code 0x101 for finalization */ - - if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { - ngx_quic_finalize_connection(c->quic->parent, - NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR, - "unexpected uni stream"); - ngx_http_close_connection(c); - return; - } - - clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); - - n = c->quic->id >> 2; - - if (n >= clcf->keepalive_requests) { - ngx_quic_finalize_connection(c->quic->parent, - NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR, - "reached maximum number of requests"); - ngx_http_close_connection(c); - return; - } - - if (ngx_current_msec - c->quic->parent->start_time - > clcf->keepalive_time) - { - ngx_quic_finalize_connection(c->quic->parent, - NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR, - "reached maximum time for requests"); - ngx_http_close_connection(c); - return; - } - - rev = c->read; - - if (rev->ready) { - rev->handler(rev); - return; - } - - cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); - - ngx_add_timer(rev, cscf->client_header_timeout); - ngx_reusable_connection(c, 1); - - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_close_connection(c); - return; - } -} - -#endif - - static void ngx_http_v3_init_request_stream(ngx_connection_t *c) { uint64_t n; ngx_event_t *rev; + ngx_connection_t *pc; ngx_http_connection_t *hc; ngx_http_v3_session_t *h3c; ngx_http_core_loc_conf_t *clcf; @@ -219,15 +138,21 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) return; } + pc = c->quic->parent; + if (n + 1 == clcf->keepalive_requests - || ngx_current_msec - c->quic->parent->start_time - > clcf->keepalive_time) + || ngx_current_msec - pc->start_time > clcf->keepalive_time) { h3c->goaway = 1; - if (ngx_http_v3_send_goaway(c, (n + 1) << 2) != NGX_OK) { - ngx_http_close_connection(c); - return; +#if (NGX_HTTP_V3_HQ) + if (!h3c->hq) +#endif + { + if (ngx_http_v3_send_goaway(c, (n + 1) << 2) != NGX_OK) { + ngx_http_close_connection(c); + return; + } } ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, @@ -235,8 +160,14 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) } rev = c->read; - rev->handler = ngx_http_v3_wait_request_handler; - c->write->handler = ngx_http_empty_handler; + +#if (NGX_HTTP_V3_HQ) + if (!h3c->hq) +#endif + { + rev->handler = ngx_http_v3_wait_request_handler; + c->write->handler = ngx_http_empty_handler; + } if (rev->ready) { rev->handler(rev); @@ -264,8 +195,8 @@ ngx_http_v3_wait_request_handler(ngx_event_t *rev) ngx_connection_t *c; ngx_pool_cleanup_t *cln; ngx_http_request_t *r; - ngx_http_connection_t *hc; ngx_http_v3_session_t *h3c; + ngx_http_connection_t *hc; ngx_http_core_srv_conf_t *cscf; c = rev->data; @@ -404,13 +335,10 @@ ngx_http_v3_reset_connection(ngx_connection_t *c) h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + if (h3scf->max_table_capacity > 0 && !c->read->eof #if (NGX_HTTP_V3_HQ) - if (h3scf->hq) { - return; - } + && !h3scf->hq #endif - - if (h3scf->max_table_capacity > 0 && !c->read->eof && (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { (void) ngx_http_v3_send_cancel_stream(c, c->quic->id); diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c index d6a6e97e6..d0e392de4 100644 --- a/src/http/v3/ngx_http_v3_uni.c +++ b/src/http/v3/ngx_http_v3_uni.c @@ -37,8 +37,23 @@ void ngx_http_v3_init_uni_stream(ngx_connection_t *c) { uint64_t n; +#if (NGX_HTTP_V3_HQ) + ngx_http_v3_session_t *h3c; +#endif ngx_http_v3_uni_stream_t *us; +#if (NGX_HTTP_V3_HQ) + h3c = ngx_http_v3_get_session(c); + if (h3c->hq) { + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "uni stream in hq mode"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); + return; + } +#endif + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); n = c->quic->id >> 2; -- cgit From fed44881d3bf5126b49144dba58f2830e0fe9866 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 19 Oct 2022 17:45:18 +0400 Subject: QUIC: idle mode for main connection. Now main QUIC connection for HTTP/3 always has c->idle flag set. This allows the connection to receive worker shutdown notification. It is passed to application level via a new conf->shutdown() callback. The HTTP/3 shutdown callback sends GOAWAY to client and gracefully shuts down the QUIC connection. --- src/http/v3/ngx_http_v3.h | 2 ++ src/http/v3/ngx_http_v3_module.c | 2 ++ src/http/v3/ngx_http_v3_request.c | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 38 insertions(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 7ffd38768..818ca2bf5 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -141,6 +141,7 @@ struct ngx_http_v3_session_s { uint64_t next_push_id; uint64_t max_push_id; uint64_t goaway_push_id; + uint64_t next_request_id; off_t total_bytes; off_t payload_bytes; @@ -158,6 +159,7 @@ void ngx_http_v3_init(ngx_connection_t *c); void ngx_http_v3_reset_connection(ngx_connection_t *c); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); ngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c); +void ngx_http_v3_shutdown(ngx_connection_t *c); ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r); ngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r); diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index d274a3bf2..d87a1c9ef 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -249,6 +249,8 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) h3scf->quic.stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; h3scf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT; + h3scf->quic.shutdown = ngx_http_v3_shutdown; + return h3scf; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index bdb5331fe..c75bc19c5 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -100,6 +100,37 @@ ngx_http_v3_init(ngx_connection_t *c) } +void +ngx_http_v3_shutdown(ngx_connection_t *c) +{ + ngx_http_v3_session_t *h3c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 shutdown"); + + h3c = ngx_http_v3_get_session(c); + + if (h3c == NULL) { + ngx_quic_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "connection shutdown"); + return; + } + + if (!h3c->goaway) { + h3c->goaway = 1; + +#if (NGX_HTTP_V3_HQ) + if (!h3c->hq) +#endif + { + (void) ngx_http_v3_send_goaway(c, h3c->next_request_id); + } + + ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "connection shutdown"); + } +} + + static void ngx_http_v3_init_request_stream(ngx_connection_t *c) { @@ -140,6 +171,8 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) pc = c->quic->parent; + h3c->next_request_id = c->quic->id + 0x04; + if (n + 1 == clcf->keepalive_requests || ngx_current_msec - pc->start_time > clcf->keepalive_time) { @@ -149,7 +182,7 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) if (!h3c->hq) #endif { - if (ngx_http_v3_send_goaway(c, (n + 1) << 2) != NGX_OK) { + if (ngx_http_v3_send_goaway(c, h3c->next_request_id) != NGX_OK) { ngx_http_close_connection(c); return; } -- cgit From 7e3aa23991cbab236617f15ab3602c1b43b2aae1 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 30 Nov 2022 14:09:08 +0400 Subject: QUIC: removed cancelable flag from QUIC and HTTP/3 events. All these events are created in context of a client connection and are deleted when the connection is closed. Setting ev->cancelable could trigger premature connection closure and a socket leak alert. --- src/http/v3/ngx_http_v3.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index b9956e68f..70f397985 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -55,7 +55,6 @@ ngx_http_v3_init_session(ngx_connection_t *c) h3c->keepalive.log = pc->log; h3c->keepalive.data = pc; h3c->keepalive.handler = ngx_http_v3_keepalive_handler; - h3c->keepalive.cancelable = 1; h3c->table.send_insert_count.log = pc->log; h3c->table.send_insert_count.data = pc; -- cgit From 8a1deaca78d65f4db82f856e22dcf879d1cec479 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 22 Aug 2022 14:09:03 +0400 Subject: HTTP/3: renamed functions. ngx_http_v3_init() is renamed ngx_http_v3_init_stream(). ngx_http_v3_reset_connection() is renamed to ngx_http_v3_reset_stream(). --- src/http/ngx_http_request.c | 4 ++-- src/http/v3/ngx_http_v3.h | 4 ++-- src/http/v3/ngx_http_v3_request.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 86da94262..8cc3cff24 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -326,7 +326,7 @@ ngx_http_init_connection(ngx_connection_t *c) #if (NGX_HTTP_V3) if (hc->addr_conf->http3) { - ngx_http_v3_init(c); + ngx_http_v3_init_stream(c); return; } #endif @@ -3786,7 +3786,7 @@ ngx_http_close_connection(ngx_connection_t *c) #if (NGX_HTTP_V3) if (c->quic) { - ngx_http_v3_reset_connection(c); + ngx_http_v3_reset_stream(c); } #endif diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 818ca2bf5..8c6c57edf 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -155,8 +155,8 @@ struct ngx_http_v3_session_s { }; -void ngx_http_v3_init(ngx_connection_t *c); -void ngx_http_v3_reset_connection(ngx_connection_t *c); +void ngx_http_v3_init_stream(ngx_connection_t *c); +void ngx_http_v3_reset_stream(ngx_connection_t *c); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); ngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c); void ngx_http_v3_shutdown(ngx_connection_t *c); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index c75bc19c5..cbfede836 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -55,7 +55,7 @@ static const struct { void -ngx_http_v3_init(ngx_connection_t *c) +ngx_http_v3_init_stream(ngx_connection_t *c) { ngx_http_connection_t *hc, *phc; ngx_http_v3_srv_conf_t *h3scf; @@ -362,7 +362,7 @@ ngx_http_v3_wait_request_handler(ngx_event_t *rev) void -ngx_http_v3_reset_connection(ngx_connection_t *c) +ngx_http_v3_reset_stream(ngx_connection_t *c) { ngx_http_v3_srv_conf_t *h3scf; -- cgit From 64ccdf45288c46b5f8e12426d3802c44d789d115 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 30 Nov 2022 12:51:15 +0400 Subject: QUIC: application init() callback. It's called after handshake completion or prior to the first early data stream creation. The callback should initialize application-level data before creating streams. HTTP/3 callback implementation sets keepalive timer and sends SETTINGS. Also, this allows to limit max handshake time in ngx_http_v3_init_stream(). --- src/http/v3/ngx_http_v3.c | 28 ++++++++-------------------- src/http/v3/ngx_http_v3.h | 1 + src/http/v3/ngx_http_v3_module.c | 1 + src/http/v3/ngx_http_v3_request.c | 38 +++++++++++++++++++++++++++++++++----- 4 files changed, 43 insertions(+), 25 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index 70f397985..b0cf15b76 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -17,7 +17,6 @@ static void ngx_http_v3_cleanup_session(void *data); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c) { - ngx_connection_t *pc; ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; ngx_http_v3_session_t *h3c; @@ -25,16 +24,11 @@ ngx_http_v3_init_session(ngx_connection_t *c) ngx_http_v3_srv_conf_t *h3scf; #endif - pc = c->quic->parent; - hc = pc->data; - - if (hc->v3_session) { - return NGX_OK; - } + hc = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init session"); - h3c = ngx_pcalloc(pc->pool, sizeof(ngx_http_v3_session_t)); + h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_session_t)); if (h3c == NULL) { goto failed; } @@ -52,15 +46,15 @@ ngx_http_v3_init_session(ngx_connection_t *c) ngx_queue_init(&h3c->blocked); ngx_queue_init(&h3c->pushing); - h3c->keepalive.log = pc->log; - h3c->keepalive.data = pc; + h3c->keepalive.log = c->log; + h3c->keepalive.data = c; h3c->keepalive.handler = ngx_http_v3_keepalive_handler; - h3c->table.send_insert_count.log = pc->log; - h3c->table.send_insert_count.data = pc; + h3c->table.send_insert_count.log = c->log; + h3c->table.send_insert_count.data = c; h3c->table.send_insert_count.handler = ngx_http_v3_inc_insert_count_handler; - cln = ngx_pool_cleanup_add(pc->pool, 0); + cln = ngx_pool_cleanup_add(c->pool, 0); if (cln == NULL) { goto failed; } @@ -70,13 +64,7 @@ ngx_http_v3_init_session(ngx_connection_t *c) hc->v3_session = h3c; -#if (NGX_HTTP_V3_HQ) - if (h3c->hq) { - return NGX_OK; - } -#endif - - return ngx_http_v3_send_settings(c); + return NGX_OK; failed: diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 8c6c57edf..207a8c25b 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -159,6 +159,7 @@ void ngx_http_v3_init_stream(ngx_connection_t *c); void ngx_http_v3_reset_stream(ngx_connection_t *c); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); ngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c); +ngx_int_t ngx_http_v3_init(ngx_connection_t *c); void ngx_http_v3_shutdown(ngx_connection_t *c); ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r); diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index d87a1c9ef..ed6becf31 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -249,6 +249,7 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) h3scf->quic.stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; h3scf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT; + h3scf->quic.init = ngx_http_v3_init; h3scf->quic.shutdown = ngx_http_v3_shutdown; return h3scf; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index cbfede836..2e7dd811d 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -57,18 +57,29 @@ static const struct { void ngx_http_v3_init_stream(ngx_connection_t *c) { + ngx_http_v3_session_t *h3c; ngx_http_connection_t *hc, *phc; ngx_http_v3_srv_conf_t *h3scf; ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; hc = c->data; hc->ssl = 1; clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); if (c->quic == NULL) { + if (ngx_http_v3_init_session(c) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + h3c = hc->v3_session; + ngx_add_timer(&h3c->keepalive, cscf->client_header_timeout); + h3scf->quic.timeout = clcf->keepalive_timeout; ngx_quic_run(c, &h3scf->quic); return; @@ -86,11 +97,6 @@ ngx_http_v3_init_stream(ngx_connection_t *c) ngx_set_connection_log(c, clcf->error_log); } - if (ngx_http_v3_init_session(c) != NGX_OK) { - ngx_http_close_connection(c); - return; - } - if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { ngx_http_v3_init_uni_stream(c); @@ -100,6 +106,28 @@ ngx_http_v3_init_stream(ngx_connection_t *c) } +ngx_int_t +ngx_http_v3_init(ngx_connection_t *c) +{ + ngx_http_v3_session_t *h3c; + ngx_http_core_loc_conf_t *clcf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init"); + + h3c = ngx_http_v3_get_session(c); + clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); + ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout); + +#if (NGX_HTTP_V3_HQ) + if (h3c->hq) { + return NGX_OK; + } +#endif + + return ngx_http_v3_send_settings(c); +} + + void ngx_http_v3_shutdown(ngx_connection_t *c) { -- cgit From 36f7b31f9578c0d393cfe82d4e23c76a7539f34e Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 25 Oct 2022 12:52:09 +0400 Subject: HTTP/3: implement keepalive for hq. Previously, keepalive timer was deleted in ngx_http_v3_wait_request_handler() and set in request cleanup handler. This worked for HTTP/3 connections, but not for hq connections. Now keepalive timer is deleted in ngx_http_v3_init_request_stream() and set in connection cleanup handler, which works both for HTTP/3 and hq. --- src/http/v3/ngx_http_v3_request.c | 47 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 17 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 2e7dd811d..f05198903 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -12,6 +12,7 @@ static void ngx_http_v3_init_request_stream(ngx_connection_t *c); static void ngx_http_v3_wait_request_handler(ngx_event_t *rev); +static void ngx_http_v3_cleanup_connection(void *data); static void ngx_http_v3_cleanup_request(void *data); static void ngx_http_v3_process_request(ngx_event_t *rev); static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, @@ -165,6 +166,7 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) uint64_t n; ngx_event_t *rev; ngx_connection_t *pc; + ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; ngx_http_v3_session_t *h3c; ngx_http_core_loc_conf_t *clcf; @@ -220,6 +222,21 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) "reached maximum number of requests"); } + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + ngx_http_close_connection(c); + return; + } + + cln->handler = ngx_http_v3_cleanup_connection; + cln->data = c; + + h3c->nrequests++; + + if (h3c->keepalive.timer_set) { + ngx_del_timer(&h3c->keepalive); + } + rev = c->read; #if (NGX_HTTP_V3_HQ) @@ -256,7 +273,6 @@ ngx_http_v3_wait_request_handler(ngx_event_t *rev) ngx_connection_t *c; ngx_pool_cleanup_t *cln; ngx_http_request_t *r; - ngx_http_v3_session_t *h3c; ngx_http_connection_t *hc; ngx_http_core_srv_conf_t *cscf; @@ -377,13 +393,6 @@ ngx_http_v3_wait_request_handler(ngx_event_t *rev) cln->handler = ngx_http_v3_cleanup_request; cln->data = r; - h3c = ngx_http_v3_get_session(c); - h3c->nrequests++; - - if (h3c->keepalive.timer_set) { - ngx_del_timer(&h3c->keepalive); - } - rev->handler = ngx_http_v3_process_request; ngx_http_v3_process_request(rev); } @@ -418,20 +427,13 @@ ngx_http_v3_reset_stream(ngx_connection_t *c) static void -ngx_http_v3_cleanup_request(void *data) +ngx_http_v3_cleanup_connection(void *data) { - ngx_http_request_t *r = data; + ngx_connection_t *c = data; - ngx_connection_t *c; ngx_http_v3_session_t *h3c; ngx_http_core_loc_conf_t *clcf; - c = r->connection; - - if (!r->response_sent) { - c->error = 1; - } - h3c = ngx_http_v3_get_session(c); if (--h3c->nrequests == 0) { @@ -441,6 +443,17 @@ ngx_http_v3_cleanup_request(void *data) } +static void +ngx_http_v3_cleanup_request(void *data) +{ + ngx_http_request_t *r = data; + + if (!r->response_sent) { + r->connection->error = 1; + } +} + + static void ngx_http_v3_process_request(ngx_event_t *rev) { -- cgit From d04f45ac5bddb034ec8c5b0874a7358a991d1b77 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 3 Jan 2023 16:24:45 +0400 Subject: HTTP/3: handled insertion reference to a going to be evicted entry. As per RFC 9204, section 3.2.2, a new entry can reference an entry in the dynamic table that will be evicted when adding this new entry into the dynamic table. Previously, such inserts resulted in use-after-free since the old entry was evicted before the insertion (ticket #2431). Now it's evicted after the insertion. This change fixes Insert with Name Reference and Duplicate encoder instructions. --- src/http/v3/ngx_http_v3_table.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_table.c b/src/http/v3/ngx_http_v3_table.c index 7e07a15b9..f49a8fc5e 100644 --- a/src/http/v3/ngx_http_v3_table.c +++ b/src/http/v3/ngx_http_v3_table.c @@ -13,7 +13,7 @@ #define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) -static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); +static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t target); static void ngx_http_v3_unblock(void *data); static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c); @@ -204,13 +204,15 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) size = ngx_http_v3_table_entry_size(name, value); - if (ngx_http_v3_evict(c, size) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - h3c = ngx_http_v3_get_session(c); dt = &h3c->table; + if (size > dt->capacity) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "not enough dynamic table capacity"); + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 insert [%ui] \"%V\":\"%V\", size:%uz", dt->base + dt->nelts, name, value, size); @@ -234,6 +236,10 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) dt->insert_count++; + if (ngx_http_v3_evict(c, dt->capacity) != NGX_OK) { + return NGX_ERROR; + } + ngx_post_event(&dt->send_insert_count, &ngx_posted_events); if (ngx_http_v3_new_entry(c) != NGX_OK) { @@ -293,14 +299,11 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } - dt = &h3c->table; - - if (dt->size > capacity) { - if (ngx_http_v3_evict(c, dt->size - capacity) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } + if (ngx_http_v3_evict(c, capacity) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } + dt = &h3c->table; max = capacity / 32; prev_max = dt->capacity / 32; @@ -345,9 +348,9 @@ ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c) static ngx_int_t -ngx_http_v3_evict(ngx_connection_t *c, size_t need) +ngx_http_v3_evict(ngx_connection_t *c, size_t target) { - size_t size, target; + size_t size; ngx_uint_t n; ngx_http_v3_field_t *field; ngx_http_v3_session_t *h3c; @@ -355,14 +358,6 @@ ngx_http_v3_evict(ngx_connection_t *c, size_t need) h3c = ngx_http_v3_get_session(c); dt = &h3c->table; - - if (need > dt->capacity) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "not enough dynamic table capacity"); - return NGX_ERROR; - } - - target = dt->capacity - need; n = 0; while (dt->size > target) { -- cgit From 1fe0913fccedfffade10a88d3fb3033339a42900 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Jan 2023 17:59:16 +0400 Subject: HTTP/3: fixed $connection_time. Previously, start_time wasn't set for a new stream. The fix is to derive it from the parent connection. Also it's used to simplify tracking keepalive_time. --- src/http/v3/ngx_http_v3_request.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index f05198903..8a5aeeb14 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -165,7 +165,6 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) { uint64_t n; ngx_event_t *rev; - ngx_connection_t *pc; ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; ngx_http_v3_session_t *h3c; @@ -199,12 +198,10 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) return; } - pc = c->quic->parent; - h3c->next_request_id = c->quic->id + 0x04; if (n + 1 == clcf->keepalive_requests - || ngx_current_msec - pc->start_time > clcf->keepalive_time) + || ngx_current_msec - c->start_time > clcf->keepalive_time) { h3c->goaway = 1; -- cgit From faa655f211612fcdac938d58095a4ab7f03969a9 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 5 Jan 2023 18:15:46 +0400 Subject: HTTP/3: trigger 400 (Bad Request) on stream error while blocked. Previously, stream was closed with NGX_HTTP_CLOSE. However, in a similar case when recv() returns eof or error, status 400 is triggered. --- src/http/v3/ngx_http_v3_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 8a5aeeb14..e8b84eabd 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -551,7 +551,7 @@ ngx_http_v3_process_request(ngx_event_t *rev) if (rc == NGX_BUSY) { if (rev->error) { - ngx_http_close_request(r, NGX_HTTP_CLOSE); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); break; } -- cgit From 0065ba68b08b8cf4eaf3c18266d1a93182f196ed Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 5 Jan 2023 19:03:22 +0400 Subject: HTTP/3: insert count block timeout. Previously, there was no timeout for a request stream blocked on insert count, which could result in infinite wait. Now client_header_timeout is set when stream is first blocked. --- src/http/v3/ngx_http_v3_request.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index e8b84eabd..7bf61459f 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -555,6 +555,12 @@ ngx_http_v3_process_request(ngx_event_t *rev) break; } + if (!rev->timer_set) { + cscf = ngx_http_get_module_srv_conf(r, + ngx_http_core_module); + ngx_add_timer(rev, cscf->client_header_timeout); + } + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } -- cgit From a36ebf7e95baebf445b0973bd270bc009b0b0e9a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 22 Feb 2023 19:16:53 +0400 Subject: QUIC: OpenSSL compatibility layer. The change allows to compile QUIC with OpenSSL which lacks BoringSSL QUIC API. This implementation does not support 0-RTT. --- src/http/modules/ngx_http_ssl_module.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 265cb3162..62ec13cf0 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -9,6 +9,10 @@ #include #include +#if (NGX_QUIC_OPENSSL_COMPAT) +#include +#endif + typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); @@ -1317,16 +1321,22 @@ ngx_http_ssl_init(ngx_conf_t *cf) continue; } + cscf = addr[a].default_server; + sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; + if (addr[a].opt.http3) { name = "http3"; +#if (NGX_QUIC_OPENSSL_COMPAT) + if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) { + return NGX_ERROR; + } +#endif + } else { name = "ssl"; } - cscf = addr[a].default_server; - sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; - if (sscf->certificates) { if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) { -- cgit From 815ef96124176baef4e940c4beaec158305b368a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 27 Feb 2023 14:00:56 +0400 Subject: HTTP/3: "quic" parameter of "listen" directive. Now "listen" directve has a new "quic" parameter which enables QUIC protocol for the address. Further, to enable HTTP/3, a new directive "http3" is introduced. The hq-interop protocol is enabled by "http3_hq" as before. Now application protocol is chosen by ALPN. Previously used "http3" parameter of "listen" is deprecated. --- src/http/modules/ngx_http_ssl_module.c | 29 +++++++++------ src/http/ngx_http.c | 7 +++- src/http/ngx_http_core_module.c | 21 ++++++++++- src/http/ngx_http_core_module.h | 2 + src/http/ngx_http_request.c | 2 +- src/http/v3/ngx_http_v3.c | 16 ++------ src/http/v3/ngx_http_v3.h | 8 ++-- src/http/v3/ngx_http_v3_module.c | 50 ++++++++++++------------- src/http/v3/ngx_http_v3_request.c | 67 ++++++++++++++++++++++------------ src/http/v3/ngx_http_v3_uni.c | 4 -- 10 files changed, 121 insertions(+), 85 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 62ec13cf0..8167157e2 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -431,7 +431,7 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, #if (NGX_HTTP_V2 || NGX_HTTP_V3) ngx_http_connection_t *hc; #endif -#if (NGX_HTTP_V3 && NGX_HTTP_V3_HQ) +#if (NGX_HTTP_V3) ngx_http_v3_srv_conf_t *h3scf; #endif #if (NGX_HTTP_V2 || NGX_HTTP_V3 || NGX_DEBUG) @@ -459,19 +459,26 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, } else #endif #if (NGX_HTTP_V3) - if (hc->addr_conf->http3) { + if (hc->addr_conf->quic) { -#if (NGX_HTTP_V3_HQ) h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); - if (h3scf->hq) { + if (h3scf->enable && h3scf->enable_hq) { + srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO + NGX_HTTP_V3_HQ_ALPN_PROTO; + srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO NGX_HTTP_V3_HQ_ALPN_PROTO) + - 1; + + } else if (h3scf->enable_hq) { srv = (unsigned char *) NGX_HTTP_V3_HQ_ALPN_PROTO; srvlen = sizeof(NGX_HTTP_V3_HQ_ALPN_PROTO) - 1; - } else -#endif - { + + } else if (h3scf->enable || hc->addr_conf->http3) { srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO; srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1; + + } else { + return SSL_TLSEXT_ERR_ALERT_FATAL; } } else @@ -1317,15 +1324,15 @@ ngx_http_ssl_init(ngx_conf_t *cf) addr = port[p].addrs.elts; for (a = 0; a < port[p].addrs.nelts; a++) { - if (!addr[a].opt.ssl && !addr[a].opt.http3) { + if (!addr[a].opt.ssl && !addr[a].opt.quic) { continue; } cscf = addr[a].default_server; sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; - if (addr[a].opt.http3) { - name = "http3"; + if (addr[a].opt.quic) { + name = "quic"; #if (NGX_QUIC_OPENSSL_COMPAT) if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) { @@ -1339,7 +1346,7 @@ ngx_http_ssl_init(ngx_conf_t *cf) if (sscf->certificates) { - if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) { + if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "\"ssl_protocols\" must enable TLSv1.3 for " "the \"listen ... %s\" directive in %s:%ui", diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 134eed944..8ccf3c198 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1242,6 +1242,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #endif #if (NGX_HTTP_V3) ngx_uint_t http3; + ngx_uint_t quic; #endif /* @@ -1280,6 +1281,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #endif #if (NGX_HTTP_V3) http3 = lsopt->http3 || addr[i].opt.http3; + quic = lsopt->quic || addr[i].opt.quic; #endif if (lsopt->set) { @@ -1319,6 +1321,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, #endif #if (NGX_HTTP_V3) addr[i].opt.http3 = http3; + addr[i].opt.quic = quic; #endif return NGX_OK; @@ -1823,7 +1826,7 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) #if (NGX_HTTP_V3) - ls->quic = addr->opt.http3; + ls->quic = addr->opt.quic; if (ls->quic) { ngx_rbtree_init(&ls->rbtree, &ls->sentinel, @@ -1866,6 +1869,7 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, #endif #if (NGX_HTTP_V3) addrs[i].conf.http3 = addr[i].opt.http3; + addrs[i].conf.quic = addr[i].opt.quic; #endif addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; @@ -1934,6 +1938,7 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, #endif #if (NGX_HTTP_V3) addrs6[i].conf.http3 = addr[i].opt.http3; + addrs6[i].conf.quic = addr[i].opt.quic; #endif addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 21966e698..f22d3bdab 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -4191,6 +4191,10 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (ngx_strcmp(value[n].data, "http3") == 0) { #if (NGX_HTTP_V3) + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "the \"http3\" parameter is deprecated, " + "use \"quic\" parameter instead"); + lsopt.quic = 1; lsopt.http3 = 1; lsopt.type = SOCK_DGRAM; continue; @@ -4202,6 +4206,19 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #endif } + if (ngx_strcmp(value[n].data, "quic") == 0) { +#if (NGX_HTTP_V3) + lsopt.quic = 1; + lsopt.type = SOCK_DGRAM; + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"quic\" parameter requires " + "ngx_http_v3_module"); + return NGX_CONF_ERROR; +#endif + } + if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) { if (ngx_strcmp(&value[n].data[13], "on") == 0) { @@ -4304,8 +4321,8 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } #if (NGX_HTTP_SSL && NGX_HTTP_V3) - if (lsopt.ssl && lsopt.http3) { - return "\"ssl\" parameter is incompatible with \"http3\""; + if (lsopt.ssl && lsopt.quic) { + return "\"ssl\" parameter is incompatible with \"quic\""; } #endif diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index 430a70266..1bc84b0df 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -76,6 +76,7 @@ typedef struct { unsigned ssl:1; unsigned http2:1; unsigned http3:1; + unsigned quic:1; #if (NGX_HAVE_INET6) unsigned ipv6only:1; #endif @@ -240,6 +241,7 @@ struct ngx_http_addr_conf_s { unsigned ssl:1; unsigned http2:1; unsigned http3:1; + unsigned quic:1; unsigned proxy_protocol:1; }; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 8cc3cff24..6cfae3435 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -325,7 +325,7 @@ ngx_http_init_connection(ngx_connection_t *c) #endif #if (NGX_HTTP_V3) - if (hc->addr_conf->http3) { + if (hc->addr_conf->quic) { ngx_http_v3_init_stream(c); return; } diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index b0cf15b76..6d4bddb38 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -17,12 +17,9 @@ static void ngx_http_v3_cleanup_session(void *data); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c) { - ngx_pool_cleanup_t *cln; - ngx_http_connection_t *hc; - ngx_http_v3_session_t *h3c; -#if (NGX_HTTP_V3_HQ) - ngx_http_v3_srv_conf_t *h3scf; -#endif + ngx_pool_cleanup_t *cln; + ngx_http_connection_t *hc; + ngx_http_v3_session_t *h3c; hc = c->data; @@ -36,13 +33,6 @@ ngx_http_v3_init_session(ngx_connection_t *c) h3c->max_push_id = (uint64_t) -1; h3c->goaway_push_id = (uint64_t) -1; -#if (NGX_HTTP_V3_HQ) - h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); - if (h3scf->hq) { - h3c->hq = 1; - } -#endif - ngx_queue_init(&h3c->blocked); ngx_queue_init(&h3c->pushing); diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 207a8c25b..2bb717cc8 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -21,6 +21,7 @@ #define NGX_HTTP_V3_ALPN_PROTO "\x02h3" #define NGX_HTTP_V3_HQ_ALPN_PROTO "\x0Ahq-interop" +#define NGX_HTTP_V3_HQ_PROTO "hq-interop" #define NGX_HTTP_V3_VARLEN_INT_LEN 4 #define NGX_HTTP_V3_PREFIX_INT_LEN 11 @@ -101,13 +102,12 @@ typedef struct { + ngx_flag_t enable; + ngx_flag_t enable_hq; size_t max_table_capacity; ngx_uint_t max_blocked_streams; ngx_uint_t max_concurrent_pushes; ngx_uint_t max_concurrent_streams; -#if (NGX_HTTP_V3_HQ) - ngx_flag_t hq; -#endif ngx_quic_conf_t quic; } ngx_http_v3_srv_conf_t; @@ -147,9 +147,7 @@ struct ngx_http_v3_session_s { off_t payload_bytes; unsigned goaway:1; -#if (NGX_HTTP_V3_HQ) unsigned hq:1; -#endif ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; }; diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index ed6becf31..02b88b479 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -32,6 +32,20 @@ static ngx_conf_post_t ngx_http_quic_mtu_post = static ngx_command_t ngx_http_v3_commands[] = { + { ngx_string("http3"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, enable), + NULL }, + + { ngx_string("http3_hq"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, enable_hq), + NULL }, + { ngx_string("http3_max_concurrent_pushes"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, @@ -46,15 +60,6 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, max_concurrent_streams), NULL }, -#if (NGX_HTTP_V3_HQ) - { ngx_string("http3_hq"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, hq), - NULL }, -#endif - { ngx_string("http3_push"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_v3_push, @@ -160,14 +165,12 @@ static ngx_int_t ngx_http_v3_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - if (r->connection->quic) { -#if (NGX_HTTP_V3_HQ) + ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; - - h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); + if (r->connection->quic) { + h3c = ngx_http_v3_get_session(r->connection); - if (h3scf->hq) { + if (h3c->hq) { v->len = sizeof("hq") - 1; v->valid = 1; v->no_cacheable = 0; @@ -177,8 +180,6 @@ ngx_http_v3_variable(ngx_http_request_t *r, return NGX_OK; } -#endif - v->len = sizeof("h3") - 1; v->valid = 1; v->no_cacheable = 0; @@ -232,12 +233,12 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) * h3scf->quic.timeout = 0; * h3scf->max_blocked_streams = 0; */ + + h3scf->enable = NGX_CONF_UNSET; + h3scf->enable_hq = NGX_CONF_UNSET; h3scf->max_table_capacity = NGX_HTTP_V3_MAX_TABLE_CAPACITY; h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT; h3scf->max_concurrent_streams = NGX_CONF_UNSET_UINT; -#if (NGX_HTTP_V3_HQ) - h3scf->hq = NGX_CONF_UNSET; -#endif h3scf->quic.mtu = NGX_CONF_UNSET_SIZE; h3scf->quic.stream_buffer_size = NGX_CONF_UNSET_SIZE; @@ -264,6 +265,10 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_ssl_srv_conf_t *sscf; + ngx_conf_merge_value(conf->enable, prev->enable, 1); + + ngx_conf_merge_value(conf->enable_hq, prev->enable_hq, 0); + ngx_conf_merge_uint_value(conf->max_concurrent_pushes, prev->max_concurrent_pushes, 10); @@ -272,11 +277,6 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) conf->max_blocked_streams = conf->max_concurrent_streams; -#if (NGX_HTTP_V3_HQ) - ngx_conf_merge_value(conf->hq, prev->hq, 0); -#endif - - ngx_conf_merge_size_value(conf->quic.mtu, prev->quic.mtu, NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 7bf61459f..ff6b40734 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -110,7 +110,10 @@ ngx_http_v3_init_stream(ngx_connection_t *c) ngx_int_t ngx_http_v3_init(ngx_connection_t *c) { + unsigned int len; + const unsigned char *data; ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; ngx_http_core_loc_conf_t *clcf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init"); @@ -119,11 +122,23 @@ ngx_http_v3_init(ngx_connection_t *c) clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout); -#if (NGX_HTTP_V3_HQ) - if (h3c->hq) { - return NGX_OK; + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + if (h3scf->enable_hq) { + if (!h3scf->enable) { + h3c->hq = 1; + return NGX_OK; + } + + SSL_get0_alpn_selected(c->ssl->connection, &data, &len); + + if (len == sizeof(NGX_HTTP_V3_HQ_PROTO) - 1 + && ngx_strncmp(data, NGX_HTTP_V3_HQ_PROTO, len) == 0) + { + h3c->hq = 1; + return NGX_OK; + } } -#endif return ngx_http_v3_send_settings(c); } @@ -147,10 +162,7 @@ ngx_http_v3_shutdown(ngx_connection_t *c) if (!h3c->goaway) { h3c->goaway = 1; -#if (NGX_HTTP_V3_HQ) - if (!h3c->hq) -#endif - { + if (!h3c->hq) { (void) ngx_http_v3_send_goaway(c, h3c->next_request_id); } @@ -205,10 +217,7 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) { h3c->goaway = 1; -#if (NGX_HTTP_V3_HQ) - if (!h3c->hq) -#endif - { + if (!h3c->hq) { if (ngx_http_v3_send_goaway(c, h3c->next_request_id) != NGX_OK) { ngx_http_close_connection(c); return; @@ -236,10 +245,7 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) rev = c->read; -#if (NGX_HTTP_V3_HQ) - if (!h3c->hq) -#endif - { + if (!h3c->hq) { rev->handler = ngx_http_v3_wait_request_handler; c->write->handler = ngx_http_empty_handler; } @@ -398,14 +404,14 @@ ngx_http_v3_wait_request_handler(ngx_event_t *rev) void ngx_http_v3_reset_stream(ngx_connection_t *c) { + ngx_http_v3_session_t *h3c; ngx_http_v3_srv_conf_t *h3scf; h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - if (h3scf->max_table_capacity > 0 && !c->read->eof -#if (NGX_HTTP_V3_HQ) - && !h3scf->hq -#endif + h3c = ngx_http_v3_get_session(c); + + if (h3scf->max_table_capacity > 0 && !c->read->eof && !h3c->hq && (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { (void) ngx_http_v3_send_cancel_stream(c, c->quic->id); @@ -993,9 +999,11 @@ failed: static ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r) { - ssize_t n; - ngx_buf_t *b; - ngx_connection_t *c; + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; c = r->connection; @@ -1003,6 +1011,19 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r) return NGX_ERROR; } + h3c = ngx_http_v3_get_session(c); + h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); + + if (!r->http_connection->addr_conf->http3) { + if ((h3c->hq && !h3scf->enable_hq) || (!h3c->hq && !h3scf->enable)) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client attempted to request the server name " + "for which the negotiated protocol is disabled"); + ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST); + return NGX_ERROR; + } + } + if (ngx_http_v3_construct_cookie_header(r) != NGX_OK) { return NGX_ERROR; } diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c index d0e392de4..f00caaad8 100644 --- a/src/http/v3/ngx_http_v3_uni.c +++ b/src/http/v3/ngx_http_v3_uni.c @@ -37,12 +37,9 @@ void ngx_http_v3_init_uni_stream(ngx_connection_t *c) { uint64_t n; -#if (NGX_HTTP_V3_HQ) ngx_http_v3_session_t *h3c; -#endif ngx_http_v3_uni_stream_t *us; -#if (NGX_HTTP_V3_HQ) h3c = ngx_http_v3_get_session(c); if (h3c->hq) { ngx_http_v3_finalize_connection(c, @@ -52,7 +49,6 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) ngx_http_v3_close_uni_stream(c); return; } -#endif ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); -- cgit From a5f9b45aee3c2bdbd3fcd4f8fc6b6903b1214705 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 26 Jan 2023 15:25:33 +0400 Subject: HTTP/3: trigger more compatibility errors for "listen quic". Now "ssl", "proxy_protocol" and "http2" are not allowed with "quic" in "listen" directive. Previously, only "ssl" was not allowed. --- src/http/ngx_http_core_module.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index f22d3bdab..3251e6da9 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -4320,10 +4320,26 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } -#if (NGX_HTTP_SSL && NGX_HTTP_V3) - if (lsopt.ssl && lsopt.quic) { - return "\"ssl\" parameter is incompatible with \"quic\""; +#if (NGX_HTTP_V3) + + if (lsopt.quic) { +#if (NGX_HTTP_SSL) + if (lsopt.ssl) { + return "\"ssl\" parameter is incompatible with \"quic\""; + } +#endif + +#if (NGX_HTTP_V2) + if (lsopt.http2) { + return "\"http2\" parameter is incompatible with \"quic\""; + } +#endif + + if (lsopt.proxy_protocol) { + return "\"proxy_protocol\" parameter is incompatible with \"quic\""; + } } + #endif for (n = 0; n < u.naddrs; n++) { -- cgit From 4d472cd792cc9699e014995c9f41c3e3e048e975 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 24 Mar 2023 19:49:50 +0400 Subject: HTTP/3: fixed OpenSSL compatibility layer initialization. SSL context is not present if the default server has neither certificates nor ssl_reject_handshake enabled. Previously, this led to null pointer dereference before it would be caught with configuration checks. Additionally, non-default servers with distinct SSL contexts need to initialize compatibility layer in order to complete a QUIC handshake. --- src/http/modules/ngx_http_ssl_module.c | 40 ++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 8167157e2..d92ec403e 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -56,6 +56,10 @@ static char *ngx_http_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data); static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf); +#if (NGX_QUIC_OPENSSL_COMPAT) +static ngx_int_t ngx_http_ssl_quic_compat_init(ngx_conf_t *cf, + ngx_http_conf_addr_t *addr); +#endif static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = { @@ -1328,14 +1332,11 @@ ngx_http_ssl_init(ngx_conf_t *cf) continue; } - cscf = addr[a].default_server; - sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; - if (addr[a].opt.quic) { name = "quic"; #if (NGX_QUIC_OPENSSL_COMPAT) - if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) { + if (ngx_http_ssl_quic_compat_init(cf, &addr[a]) != NGX_OK) { return NGX_ERROR; } #endif @@ -1344,6 +1345,9 @@ ngx_http_ssl_init(ngx_conf_t *cf) name = "ssl"; } + cscf = addr[a].default_server; + sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; + if (sscf->certificates) { if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) { @@ -1391,3 +1395,31 @@ ngx_http_ssl_init(ngx_conf_t *cf) return NGX_OK; } + + +#if (NGX_QUIC_OPENSSL_COMPAT) + +static ngx_int_t +ngx_http_ssl_quic_compat_init(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) +{ + ngx_uint_t s; + ngx_http_ssl_srv_conf_t *sscf; + ngx_http_core_srv_conf_t **cscfp, *cscf; + + cscfp = addr->servers.elts; + for (s = 0; s < addr->servers.nelts; s++) { + + cscf = cscfp[s]; + sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; + + if (sscf->certificates || sscf->reject_handshake) { + if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) { + return NGX_ERROR; + } + } + } + + return NGX_OK; +} + +#endif -- cgit From ba15b2af1bc130725a1069c5c263779db350e9b0 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 6 Apr 2023 18:18:41 +0400 Subject: HTTP/3: fixed CANCEL_PUSH handling. --- src/http/v3/ngx_http_v3_uni.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c index f00caaad8..22ddaf3eb 100644 --- a/src/http/v3/ngx_http_v3_uni.c +++ b/src/http/v3/ngx_http_v3_uni.c @@ -747,7 +747,7 @@ ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) for (q = ngx_queue_head(&h3c->pushing); q != ngx_queue_sentinel(&h3c->pushing); - q = ngx_queue_next(&h3c->pushing)) + q = ngx_queue_next(q)) { push = (ngx_http_v3_push_t *) q; -- cgit From ea51d2fce8798c4bfc0d56a566ea73a024b3b125 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 4 May 2023 15:52:22 +0400 Subject: HTTP/3: fixed ngx_http_v3_init_session() error handling. A QUIC connection is not usable yet at this early stage of spin up. --- src/http/v3/ngx_http_v3.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index 6d4bddb38..db0be75da 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -59,9 +59,6 @@ ngx_http_v3_init_session(ngx_connection_t *c) failed: ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create http3 session"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - "failed to create http3 session"); return NGX_ERROR; } -- cgit From 6cc803e713698b4b09ab46ccd7ae986faa55c386 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 11 May 2023 10:37:51 +0400 Subject: QUIC: removed "quic_mtu" directive. The directive used to set the value of the "max_udp_payload_size" transport parameter. According to RFC 9000, Section 18.2, the value specifies the size of buffer for reading incoming datagrams: This limit does act as an additional constraint on datagram size in the same way as the path MTU, but it is a property of the endpoint and not the path; see Section 14. It is expected that this is the space an endpoint dedicates to holding incoming packets. Current QUIC implementation uses the maximum possible buffer size (65527) for reading datagrams. --- src/http/v3/ngx_http_v3_module.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 02b88b479..8569f07a0 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -16,8 +16,6 @@ static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); 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 char *ngx_http_quic_mtu(ngx_conf_t *cf, void *post, - void *data); static char *ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void *ngx_http_v3_create_loc_conf(ngx_conf_t *cf); @@ -26,10 +24,6 @@ static char *ngx_http_v3_merge_loc_conf(ngx_conf_t *cf, void *parent, static char *ngx_http_v3_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static ngx_conf_post_t ngx_http_quic_mtu_post = - { ngx_http_quic_mtu }; - - static ngx_command_t ngx_http_v3_commands[] = { { ngx_string("http3"), @@ -95,13 +89,6 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, quic.gso_enabled), NULL }, - { ngx_string("quic_mtu"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v3_srv_conf_t, quic.mtu), - &ngx_http_quic_mtu_post }, - { ngx_string("quic_host_key"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_http_quic_host_key, @@ -240,7 +227,6 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT; h3scf->max_concurrent_streams = NGX_CONF_UNSET_UINT; - h3scf->quic.mtu = NGX_CONF_UNSET_SIZE; h3scf->quic.stream_buffer_size = NGX_CONF_UNSET_SIZE; h3scf->quic.max_concurrent_streams_bidi = NGX_CONF_UNSET_UINT; h3scf->quic.max_concurrent_streams_uni = NGX_HTTP_V3_MAX_UNI_STREAMS; @@ -277,9 +263,6 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) conf->max_blocked_streams = conf->max_concurrent_streams; - ngx_conf_merge_size_value(conf->quic.mtu, prev->quic.mtu, - NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); - ngx_conf_merge_size_value(conf->quic.stream_buffer_size, prev->quic.stream_buffer_size, 65536); @@ -334,26 +317,6 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } -static char * -ngx_http_quic_mtu(ngx_conf_t *cf, void *post, void *data) -{ - size_t *sp = data; - - if (*sp < NGX_QUIC_MIN_INITIAL_SIZE - || *sp > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"quic_mtu\" must be between %d and %d", - NGX_QUIC_MIN_INITIAL_SIZE, - NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); - - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} - - static char * ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { -- cgit From 2ce3eeeeb76318e414b62d399da70872d2de23d8 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 11 May 2023 13:22:10 +0400 Subject: HTTP/3: removed "http3" parameter of "listen" directive. The parameter has been deprecated since c851a2ed5ce8. --- src/http/modules/ngx_http_ssl_module.c | 2 +- src/http/ngx_http.c | 5 ----- src/http/ngx_http_core_module.c | 17 ----------------- src/http/ngx_http_core_module.h | 2 -- src/http/v3/ngx_http_v3_request.c | 14 ++++++-------- 5 files changed, 7 insertions(+), 33 deletions(-) (limited to 'src/http') diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index c0d6bae06..d2ca475d3 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -477,7 +477,7 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, srv = (unsigned char *) NGX_HTTP_V3_HQ_ALPN_PROTO; srvlen = sizeof(NGX_HTTP_V3_HQ_ALPN_PROTO) - 1; - } else if (h3scf->enable || hc->addr_conf->http3) { + } else if (h3scf->enable) { srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO; srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1; diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index d08002b81..34bfc539e 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1242,7 +1242,6 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, ngx_uint_t http2; #endif #if (NGX_HTTP_V3) - ngx_uint_t http3; ngx_uint_t quic; #endif @@ -1287,7 +1286,6 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, protocols_prev |= addr[i].opt.http2 << 2; #endif #if (NGX_HTTP_V3) - http3 = lsopt->http3 || addr[i].opt.http3; quic = lsopt->quic || addr[i].opt.quic; #endif @@ -1378,7 +1376,6 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, addr[i].opt.http2 = http2; #endif #if (NGX_HTTP_V3) - addr[i].opt.http3 = http3; addr[i].opt.quic = quic; #endif @@ -1929,7 +1926,6 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, addrs[i].conf.http2 = addr[i].opt.http2; #endif #if (NGX_HTTP_V3) - addrs[i].conf.http3 = addr[i].opt.http3; addrs[i].conf.quic = addr[i].opt.quic; #endif addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; @@ -1998,7 +1994,6 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, addrs6[i].conf.http2 = addr[i].opt.http2; #endif #if (NGX_HTTP_V3) - addrs6[i].conf.http3 = addr[i].opt.http3; addrs6[i].conf.quic = addr[i].opt.quic; #endif addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 1f9fec4fe..bd8f7666a 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -4186,23 +4186,6 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #endif } - if (ngx_strcmp(value[n].data, "http3") == 0) { -#if (NGX_HTTP_V3) - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "the \"http3\" parameter is deprecated, " - "use \"quic\" parameter instead"); - lsopt.quic = 1; - lsopt.http3 = 1; - lsopt.type = SOCK_DGRAM; - continue; -#else - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the \"http3\" parameter requires " - "ngx_http_v3_module"); - return NGX_CONF_ERROR; -#endif - } - if (ngx_strcmp(value[n].data, "quic") == 0) { #if (NGX_HTTP_V3) lsopt.quic = 1; diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index b612a8d4d..765e7ff60 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -75,7 +75,6 @@ typedef struct { unsigned wildcard:1; unsigned ssl:1; unsigned http2:1; - unsigned http3:1; unsigned quic:1; #if (NGX_HAVE_INET6) unsigned ipv6only:1; @@ -240,7 +239,6 @@ struct ngx_http_addr_conf_s { unsigned ssl:1; unsigned http2:1; - unsigned http3:1; unsigned quic:1; unsigned proxy_protocol:1; }; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index ff6b40734..6f72dc402 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1014,14 +1014,12 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r) h3c = ngx_http_v3_get_session(c); h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); - if (!r->http_connection->addr_conf->http3) { - if ((h3c->hq && !h3scf->enable_hq) || (!h3c->hq && !h3scf->enable)) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client attempted to request the server name " - "for which the negotiated protocol is disabled"); - ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST); - return NGX_ERROR; - } + if ((h3c->hq && !h3scf->enable_hq) || (!h3c->hq && !h3scf->enable)) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client attempted to request the server name " + "for which the negotiated protocol is disabled"); + ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST); + return NGX_ERROR; } if (ngx_http_v3_construct_cookie_header(r) != NGX_OK) { -- cgit From 089d1f653001419ea9d0b463434a89007ec805bd Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 11 May 2023 18:48:01 +0300 Subject: QUIC: style. --- src/http/v3/ngx_http_v3.h | 4 ++-- src/http/v3/ngx_http_v3_parse.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 2bb717cc8..2c05f089f 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -85,11 +85,11 @@ #define ngx_http_v3_get_session(c) ngx_http_quic_get_connection(c)->v3_session #define ngx_http_v3_get_module_loc_conf(c, module) \ - ngx_http_get_module_loc_conf(ngx_http_quic_get_connection(c)->conf_ctx, \ + ngx_http_get_module_loc_conf(ngx_http_quic_get_connection(c)->conf_ctx, \ module) #define ngx_http_v3_get_module_srv_conf(c, module) \ - ngx_http_get_module_srv_conf(ngx_http_quic_get_connection(c)->conf_ctx, \ + ngx_http_get_module_srv_conf(ngx_http_quic_get_connection(c)->conf_ctx, \ module) #define ngx_http_v3_finalize_connection(c, code, reason) \ diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 7dc53493c..7ea284f8a 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -868,7 +868,8 @@ ngx_http_v3_parse_field_l(ngx_connection_t *c, case sw_start: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field l"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field l"); if (b->pos == b->last) { return NGX_AGAIN; -- cgit From 0a3c79614521d7612b63eff4e09c25ed219fb65b Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Sun, 14 May 2023 12:30:11 +0400 Subject: Common tree insert function for QUIC and UDP connections. Previously, ngx_udp_rbtree_insert_value() was used for plain UDP and ngx_quic_rbtree_insert_value() was used for QUIC. Because of this it was impossible to initialize connection tree in ngx_create_listening() since this function is not aware what kind of listening it creates. Now ngx_udp_rbtree_insert_value() is used for both QUIC and UDP. To make is possible, a generic key field is added to ngx_udp_connection_t. It keeps client address for UDP and connection ID for QUIC. --- src/http/ngx_http.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src/http') diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 34bfc539e..d835f896e 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1883,14 +1883,7 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) ls->wildcard = addr->opt.wildcard; #if (NGX_HTTP_V3) - ls->quic = addr->opt.quic; - - if (ls->quic) { - ngx_rbtree_init(&ls->rbtree, &ls->sentinel, - ngx_quic_rbtree_insert_value); - } - #endif return ls; -- cgit From e4edf78bac950213e00bb97ac46ece807f3cc073 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 12 May 2023 10:02:10 +0400 Subject: HTTP/3: removed server push support. --- src/http/v3/ngx_http_v3.c | 4 - src/http/v3/ngx_http_v3.h | 13 - src/http/v3/ngx_http_v3_filter_module.c | 685 -------------------------------- src/http/v3/ngx_http_v3_module.c | 132 +----- src/http/v3/ngx_http_v3_parse.c | 91 +---- src/http/v3/ngx_http_v3_uni.c | 157 -------- src/http/v3/ngx_http_v3_uni.h | 6 - 7 files changed, 6 insertions(+), 1082 deletions(-) (limited to 'src/http') diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index db0be75da..eb86b2da5 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -30,11 +30,7 @@ ngx_http_v3_init_session(ngx_connection_t *c) goto failed; } - h3c->max_push_id = (uint64_t) -1; - h3c->goaway_push_id = (uint64_t) -1; - ngx_queue_init(&h3c->blocked); - ngx_queue_init(&h3c->pushing); h3c->keepalive.log = c->log; h3c->keepalive.data = c; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 2c05f089f..94b0d3e78 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -106,19 +106,11 @@ typedef struct { ngx_flag_t enable_hq; size_t max_table_capacity; ngx_uint_t max_blocked_streams; - ngx_uint_t max_concurrent_pushes; ngx_uint_t max_concurrent_streams; ngx_quic_conf_t quic; } ngx_http_v3_srv_conf_t; -typedef struct { - ngx_flag_t push_preload; - ngx_flag_t push; - ngx_array_t *pushes; -} ngx_http_v3_loc_conf_t; - - struct ngx_http_v3_parse_s { size_t header_limit; ngx_http_v3_parse_headers_t headers; @@ -136,11 +128,6 @@ struct ngx_http_v3_session_s { ngx_queue_t blocked; ngx_uint_t nblocked; - ngx_queue_t pushing; - ngx_uint_t npushing; - uint64_t next_push_id; - uint64_t max_push_id; - uint64_t goaway_push_id; uint64_t next_request_id; off_t total_bytes; diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 41b1704bf..7addf275d 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -36,17 +36,6 @@ typedef struct { static ngx_int_t ngx_http_v3_header_filter(ngx_http_request_t *r); -static ngx_int_t ngx_http_v3_push_resources(ngx_http_request_t *r, - ngx_chain_t ***out); -static ngx_int_t ngx_http_v3_push_resource(ngx_http_request_t *r, - ngx_str_t *path, ngx_chain_t ***out); -static ngx_int_t ngx_http_v3_create_push_request( - ngx_http_request_t *pr, ngx_str_t *path, uint64_t push_id); -static ngx_int_t ngx_http_v3_set_push_header(ngx_http_request_t *r, - const char *name, ngx_str_t *value); -static void ngx_http_v3_push_request_handler(ngx_event_t *ev); -static ngx_chain_t *ngx_http_v3_create_push_promise(ngx_http_request_t *r, - ngx_str_t *path, uint64_t push_id); static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in); static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r, @@ -155,14 +144,6 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) out = NULL; ll = &out; - if ((c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0 - && r->method != NGX_HTTP_HEAD) - { - if (ngx_http_v3_push_resources(r, &ll) != NGX_OK) { - return NGX_ERROR; - } - } - len = ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0); if (r->headers_out.status == NGX_HTTP_OK) { @@ -606,672 +587,6 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) } -static ngx_int_t -ngx_http_v3_push_resources(ngx_http_request_t *r, ngx_chain_t ***out) -{ - u_char *start, *end, *last; - ngx_str_t path; - ngx_int_t rc; - ngx_uint_t i, push; - ngx_table_elt_t *h; - ngx_http_v3_loc_conf_t *h3lcf; - ngx_http_complex_value_t *pushes; - - h3lcf = ngx_http_get_module_loc_conf(r, ngx_http_v3_module); - - if (h3lcf->pushes) { - pushes = h3lcf->pushes->elts; - - for (i = 0; i < h3lcf->pushes->nelts; i++) { - - if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) { - return NGX_ERROR; - } - - if (path.len == 0) { - continue; - } - - if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) { - continue; - } - - rc = ngx_http_v3_push_resource(r, &path, out); - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc == NGX_ABORT) { - return NGX_OK; - } - - /* NGX_OK, NGX_DECLINED */ - } - } - - if (!h3lcf->push_preload) { - return NGX_OK; - } - - for (h = r->headers_out.link; h; h = h->next) { - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 parse link: \"%V\"", &h->value); - - start = h->value.data; - end = h->value.data + h->value.len; - - next_link: - - while (start < end && *start == ' ') { start++; } - - if (start == end || *start++ != '<') { - continue; - } - - while (start < end && *start == ' ') { start++; } - - for (last = start; last < end && *last != '>'; last++) { - /* void */ - } - - if (last == start || last == end) { - continue; - } - - path.len = last - start; - path.data = start; - - start = last + 1; - - while (start < end && *start == ' ') { start++; } - - if (start == end) { - continue; - } - - if (*start == ',') { - start++; - goto next_link; - } - - if (*start++ != ';') { - continue; - } - - last = ngx_strlchr(start, end, ','); - - if (last == NULL) { - last = end; - } - - push = 0; - - for ( ;; ) { - - while (start < last && *start == ' ') { start++; } - - if (last - start >= 6 - && ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0) - { - start += 6; - - if (start == last || *start == ' ' || *start == ';') { - push = 0; - break; - } - - goto next_param; - } - - if (last - start >= 11 - && ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0) - { - start += 11; - - if (start == last || *start == ' ' || *start == ';') { - push = 1; - } - - goto next_param; - } - - if (last - start >= 4 - && ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0) - { - start += 4; - - while (start < last && *start == ' ') { start++; } - - if (start == last || *start++ != '"') { - goto next_param; - } - - for ( ;; ) { - - while (start < last && *start == ' ') { start++; } - - if (last - start >= 7 - && ngx_strncasecmp(start, (u_char *) "preload", 7) == 0) - { - start += 7; - - if (start < last && (*start == ' ' || *start == '"')) { - push = 1; - break; - } - } - - while (start < last && *start != ' ' && *start != '"') { - start++; - } - - if (start == last) { - break; - } - - if (*start == '"') { - break; - } - - start++; - } - } - - next_param: - - start = ngx_strlchr(start, last, ';'); - - if (start == NULL) { - break; - } - - start++; - } - - if (push) { - while (path.len && path.data[path.len - 1] == ' ') { - path.len--; - } - } - - if (push && path.len - && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/')) - { - rc = ngx_http_v3_push_resource(r, &path, out); - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc == NGX_ABORT) { - return NGX_OK; - } - - /* NGX_OK, NGX_DECLINED */ - } - - if (last < end) { - start = last + 1; - goto next_link; - } - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, - ngx_chain_t ***ll) -{ - uint64_t push_id; - ngx_int_t rc; - ngx_chain_t *cl; - ngx_connection_t *c; - ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; - - c = r->connection; - h3c = ngx_http_v3_get_session(c); - h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); - - ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 push \"%V\" pushing:%ui/%ui id:%uL/%L", - path, h3c->npushing, h3scf->max_concurrent_pushes, - h3c->next_push_id, h3c->max_push_id); - - if (!ngx_path_separator(path->data[0])) { - ngx_log_error(NGX_LOG_WARN, c->log, 0, - "non-absolute path \"%V\" not pushed", path); - return NGX_DECLINED; - } - - if (h3c->max_push_id == (uint64_t) -1 - || h3c->next_push_id > h3c->max_push_id) - { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 abort pushes due to max_push_id"); - return NGX_ABORT; - } - - if (h3c->goaway_push_id != (uint64_t) -1) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 abort pushes due to goaway"); - return NGX_ABORT; - } - - if (h3c->npushing >= h3scf->max_concurrent_pushes) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 abort pushes due to max_concurrent_pushes"); - return NGX_ABORT; - } - - if (r->headers_in.server.len == 0) { - return NGX_ABORT; - } - - push_id = h3c->next_push_id++; - - rc = ngx_http_v3_create_push_request(r, path, push_id); - if (rc != NGX_OK) { - return rc; - } - - cl = ngx_http_v3_create_push_promise(r, path, push_id); - if (cl == NULL) { - return NGX_ERROR; - } - - for (**ll = cl; **ll; *ll = &(**ll)->next); - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, - uint64_t push_id) -{ - ngx_connection_t *c, *pc; - ngx_http_request_t *r; - ngx_http_log_ctx_t *ctx; - ngx_http_connection_t *hc, *phc; - ngx_http_core_srv_conf_t *cscf; - - pc = pr->connection; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "http3 create push request id:%uL", push_id); - - c = ngx_http_v3_create_push_stream(pc, push_id); - if (c == NULL) { - return NGX_ABORT; - } - -#if (NGX_STAT_STUB) - (void) ngx_atomic_fetch_add(ngx_stat_active, 1); -#endif - - hc = ngx_palloc(c->pool, sizeof(ngx_http_connection_t)); - if (hc == NULL) { - ngx_http_close_connection(c); - return NGX_ERROR; - } - - phc = ngx_http_quic_get_connection(pc); - ngx_memcpy(hc, phc, sizeof(ngx_http_connection_t)); - c->data = hc; - - ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); - if (ctx == NULL) { - ngx_http_close_connection(c); - return NGX_ERROR; - } - - ctx->connection = c; - ctx->request = NULL; - ctx->current_request = NULL; - - c->log->handler = pc->log->handler; - c->log->data = ctx; - c->log->action = "processing pushed request headers"; - - c->log_error = NGX_ERROR_INFO; - - r = ngx_http_create_request(c); - if (r == NULL) { - ngx_http_close_connection(c); - return NGX_ERROR; - } - - c->data = r; - - ngx_str_set(&r->http_protocol, "HTTP/3.0"); - - r->http_version = NGX_HTTP_VERSION_30; - r->method_name = ngx_http_core_get_method; - r->method = NGX_HTTP_GET; - - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - - r->header_in = ngx_create_temp_buf(r->pool, - cscf->client_header_buffer_size); - if (r->header_in == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_ERROR; - } - - if (ngx_list_init(&r->headers_in.headers, r->pool, 4, - sizeof(ngx_table_elt_t)) - != NGX_OK) - { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_ERROR; - } - - r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; - - r->schema.data = ngx_pstrdup(r->pool, &pr->schema); - if (r->schema.data == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_ERROR; - } - - r->schema.len = pr->schema.len; - - r->uri_start = ngx_pstrdup(r->pool, path); - if (r->uri_start == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_ERROR; - } - - r->uri_end = r->uri_start + path->len; - - if (ngx_http_parse_uri(r) != NGX_OK) { - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; - } - - if (ngx_http_process_request_uri(r) != NGX_OK) { - return NGX_ERROR; - } - - if (ngx_http_v3_set_push_header(r, "host", &pr->headers_in.server) - != NGX_OK) - { - return NGX_ERROR; - } - - if (pr->headers_in.accept_encoding) { - if (ngx_http_v3_set_push_header(r, "accept-encoding", - &pr->headers_in.accept_encoding->value) - != NGX_OK) - { - return NGX_ERROR; - } - } - - if (pr->headers_in.accept_language) { - if (ngx_http_v3_set_push_header(r, "accept-language", - &pr->headers_in.accept_language->value) - != NGX_OK) - { - return NGX_ERROR; - } - } - - if (pr->headers_in.user_agent) { - if (ngx_http_v3_set_push_header(r, "user-agent", - &pr->headers_in.user_agent->value) - != NGX_OK) - { - return NGX_ERROR; - } - } - - c->read->handler = ngx_http_v3_push_request_handler; - c->read->handler = ngx_http_v3_push_request_handler; - - ngx_post_event(c->read, &ngx_posted_events); - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_v3_set_push_header(ngx_http_request_t *r, const char *name, - ngx_str_t *value) -{ - u_char *p; - ngx_table_elt_t *h; - ngx_http_header_t *hh; - ngx_http_core_main_conf_t *cmcf; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 push header \"%s\": \"%V\"", name, value); - - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - - p = ngx_pnalloc(r->pool, value->len + 1); - if (p == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_ERROR; - } - - ngx_memcpy(p, value->data, value->len); - p[value->len] = '\0'; - - h = ngx_list_push(&r->headers_in.headers); - if (h == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_ERROR; - } - - h->key.data = (u_char *) name; - h->key.len = ngx_strlen(name); - h->hash = ngx_hash_key(h->key.data, h->key.len); - h->lowcase_key = (u_char *) name; - h->value.data = p; - h->value.len = value->len; - - hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, - h->lowcase_key, h->key.len); - - if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { - return NGX_ERROR; - } - - return NGX_OK; -} - - -static void -ngx_http_v3_push_request_handler(ngx_event_t *ev) -{ - ngx_connection_t *c; - ngx_http_request_t *r; - - c = ev->data; - r = c->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 push request handler"); - - ngx_http_process_request(r); -} - - -static ngx_chain_t * -ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, - uint64_t push_id) -{ - size_t n, len; - ngx_buf_t *b; - ngx_chain_t *hl, *cl; - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(r->connection); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 create push promise id:%uL", push_id); - - len = ngx_http_v3_encode_varlen_int(NULL, push_id); - - len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0); - - len += ngx_http_v3_encode_field_ri(NULL, 0, - NGX_HTTP_V3_HEADER_METHOD_GET); - - len += ngx_http_v3_encode_field_lri(NULL, 0, - NGX_HTTP_V3_HEADER_AUTHORITY, - NULL, r->headers_in.server.len); - - if (path->len == 1 && path->data[0] == '/') { - len += ngx_http_v3_encode_field_ri(NULL, 0, - NGX_HTTP_V3_HEADER_PATH_ROOT); - - } else { - len += ngx_http_v3_encode_field_lri(NULL, 0, - NGX_HTTP_V3_HEADER_PATH_ROOT, - NULL, path->len); - } - - if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { - len += ngx_http_v3_encode_field_ri(NULL, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTPS); - - } else if (r->schema.len == 4 - && ngx_strncmp(r->schema.data, "http", 4) == 0) - { - len += ngx_http_v3_encode_field_ri(NULL, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTP); - - } else { - len += ngx_http_v3_encode_field_lri(NULL, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTP, - NULL, r->schema.len); - } - - if (r->headers_in.accept_encoding) { - len += ngx_http_v3_encode_field_lri(NULL, 0, - NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, NULL, - r->headers_in.accept_encoding->value.len); - } - - if (r->headers_in.accept_language) { - len += ngx_http_v3_encode_field_lri(NULL, 0, - NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, NULL, - r->headers_in.accept_language->value.len); - } - - if (r->headers_in.user_agent) { - len += ngx_http_v3_encode_field_lri(NULL, 0, - NGX_HTTP_V3_HEADER_USER_AGENT, NULL, - r->headers_in.user_agent->value.len); - } - - b = ngx_create_temp_buf(r->pool, len); - if (b == NULL) { - return NULL; - } - - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, push_id); - - b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last, - 0, 0, 0); - - b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, - NGX_HTTP_V3_HEADER_METHOD_GET); - - b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, - NGX_HTTP_V3_HEADER_AUTHORITY, - r->headers_in.server.data, - r->headers_in.server.len); - - if (path->len == 1 && path->data[0] == '/') { - b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, - NGX_HTTP_V3_HEADER_PATH_ROOT); - - } else { - b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, - NGX_HTTP_V3_HEADER_PATH_ROOT, - path->data, path->len); - } - - if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { - b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTPS); - - } else if (r->schema.len == 4 - && ngx_strncmp(r->schema.data, "http", 4) == 0) - { - b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTP); - - } else { - b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, - NGX_HTTP_V3_HEADER_SCHEME_HTTP, - r->schema.data, r->schema.len); - } - - if (r->headers_in.accept_encoding) { - b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, - NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, - r->headers_in.accept_encoding->value.data, - r->headers_in.accept_encoding->value.len); - } - - if (r->headers_in.accept_language) { - b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, - NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, - r->headers_in.accept_language->value.data, - r->headers_in.accept_language->value.len); - } - - if (r->headers_in.user_agent) { - b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, - NGX_HTTP_V3_HEADER_USER_AGENT, - r->headers_in.user_agent->value.data, - r->headers_in.user_agent->value.len); - } - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NULL; - } - - cl->buf = b; - cl->next = NULL; - - n = b->last - b->pos; - - h3c->payload_bytes += n; - - len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_PUSH_PROMISE) - + ngx_http_v3_encode_varlen_int(NULL, n); - - b = ngx_create_temp_buf(r->pool, len); - if (b == NULL) { - return NULL; - } - - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, - NGX_HTTP_V3_FRAME_PUSH_PROMISE); - b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); - - hl = ngx_alloc_chain_link(r->pool); - if (hl == NULL) { - return NULL; - } - - hl->buf = b; - hl->next = cl; - - return hl; -} - - static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 8569f07a0..875e5f29b 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -18,10 +18,6 @@ static char *ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child); static char *ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static void *ngx_http_v3_create_loc_conf(ngx_conf_t *cf); -static char *ngx_http_v3_merge_loc_conf(ngx_conf_t *cf, void *parent, - void *child); -static char *ngx_http_v3_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_command_t ngx_http_v3_commands[] = { @@ -40,13 +36,6 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, enable_hq), NULL }, - { ngx_string("http3_max_concurrent_pushes"), - 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, max_concurrent_pushes), - NULL }, - { ngx_string("http3_max_concurrent_streams"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, @@ -54,20 +43,6 @@ static ngx_command_t ngx_http_v3_commands[] = { offsetof(ngx_http_v3_srv_conf_t, max_concurrent_streams), NULL }, - { ngx_string("http3_push"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_v3_push, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("http3_push_preload"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_v3_loc_conf_t, push_preload), - NULL }, - { ngx_string("http3_stream_buffer_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, @@ -117,8 +92,8 @@ static ngx_http_module_t ngx_http_v3_module_ctx = { ngx_http_v3_create_srv_conf, /* create server configuration */ ngx_http_v3_merge_srv_conf, /* merge server configuration */ - ngx_http_v3_create_loc_conf, /* create location configuration */ - ngx_http_v3_merge_loc_conf /* merge location configuration */ + NULL, /* create location configuration */ + NULL /* merge location configuration */ }; @@ -224,7 +199,6 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) h3scf->enable = NGX_CONF_UNSET; h3scf->enable_hq = NGX_CONF_UNSET; h3scf->max_table_capacity = NGX_HTTP_V3_MAX_TABLE_CAPACITY; - h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT; h3scf->max_concurrent_streams = NGX_CONF_UNSET_UINT; h3scf->quic.stream_buffer_size = NGX_CONF_UNSET_SIZE; @@ -255,9 +229,6 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->enable_hq, prev->enable_hq, 0); - ngx_conf_merge_uint_value(conf->max_concurrent_pushes, - prev->max_concurrent_pushes, 10); - ngx_conf_merge_uint_value(conf->max_concurrent_streams, prev->max_concurrent_streams, 128); @@ -416,102 +387,3 @@ failed: return NGX_CONF_ERROR; } - - -static void * -ngx_http_v3_create_loc_conf(ngx_conf_t *cf) -{ - ngx_http_v3_loc_conf_t *h3lcf; - - h3lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_loc_conf_t)); - if (h3lcf == NULL) { - return NULL; - } - - /* - * set by ngx_pcalloc(): - * - * h3lcf->pushes = NULL; - */ - - h3lcf->push_preload = NGX_CONF_UNSET; - h3lcf->push = NGX_CONF_UNSET; - - return h3lcf; -} - - -static char * -ngx_http_v3_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) -{ - ngx_http_v3_loc_conf_t *prev = parent; - ngx_http_v3_loc_conf_t *conf = child; - - ngx_conf_merge_value(conf->push, prev->push, 1); - - if (conf->push && conf->pushes == NULL) { - conf->pushes = prev->pushes; - } - - ngx_conf_merge_value(conf->push_preload, prev->push_preload, 0); - - return NGX_CONF_OK; -} - - -static char * -ngx_http_v3_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_v3_loc_conf_t *h3lcf = conf; - - ngx_str_t *value; - ngx_http_complex_value_t *cv; - ngx_http_compile_complex_value_t ccv; - - value = cf->args->elts; - - if (ngx_strcmp(value[1].data, "off") == 0) { - - if (h3lcf->pushes) { - return "\"off\" parameter cannot be used with URI"; - } - - if (h3lcf->push == 0) { - return "is duplicate"; - } - - h3lcf->push = 0; - return NGX_CONF_OK; - } - - if (h3lcf->push == 0) { - return "URI cannot be used with \"off\" parameter"; - } - - h3lcf->push = 1; - - if (h3lcf->pushes == NULL) { - h3lcf->pushes = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_complex_value_t)); - if (h3lcf->pushes == NULL) { - return NGX_CONF_ERROR; - } - } - - cv = ngx_array_push(h3lcf->pushes); - if (cv == NULL) { - return NGX_CONF_ERROR; - } - - ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - - ccv.cf = cf; - ccv.value = &value[1]; - ccv.complex_value = cv; - - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 7ea284f8a..c51a486c3 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -1159,10 +1159,7 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, sw_first_type, sw_type, sw_length, - sw_cancel_push, sw_settings, - sw_max_push_id, - sw_goaway, sw_skip }; @@ -1212,6 +1209,10 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; } + if (st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH) { + return NGX_HTTP_V3_ERR_ID_ERROR; + } + st->state = sw_length; break; @@ -1233,22 +1234,10 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, switch (st->type) { - case NGX_HTTP_V3_FRAME_CANCEL_PUSH: - st->state = sw_cancel_push; - break; - case NGX_HTTP_V3_FRAME_SETTINGS: st->state = sw_settings; break; - case NGX_HTTP_V3_FRAME_MAX_PUSH_ID: - st->state = sw_max_push_id; - break; - - case NGX_HTTP_V3_FRAME_GOAWAY: - st->state = sw_goaway; - break; - default: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse skip unknown frame"); @@ -1257,30 +1246,6 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, break; - case sw_cancel_push: - - ngx_http_v3_parse_start_local(b, &loc, st->length); - - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc); - - ngx_http_v3_parse_end_local(b, &loc, &st->length); - - if (st->length == 0 && rc == NGX_AGAIN) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } - - if (rc != NGX_DONE) { - return rc; - } - - rc = ngx_http_v3_cancel_push(c, st->vlint.value); - if (rc != NGX_OK) { - return rc; - } - - st->state = sw_type; - break; - case sw_settings: ngx_http_v3_parse_start_local(b, &loc, st->length); @@ -1303,54 +1268,6 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, break; - case sw_max_push_id: - - ngx_http_v3_parse_start_local(b, &loc, st->length); - - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc); - - ngx_http_v3_parse_end_local(b, &loc, &st->length); - - if (st->length == 0 && rc == NGX_AGAIN) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } - - if (rc != NGX_DONE) { - return rc; - } - - rc = ngx_http_v3_set_max_push_id(c, st->vlint.value); - if (rc != NGX_OK) { - return rc; - } - - st->state = sw_type; - break; - - case sw_goaway: - - ngx_http_v3_parse_start_local(b, &loc, st->length); - - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc); - - ngx_http_v3_parse_end_local(b, &loc, &st->length); - - if (st->length == 0 && rc == NGX_AGAIN) { - return NGX_HTTP_V3_ERR_FRAME_ERROR; - } - - if (rc != NGX_DONE) { - return rc; - } - - rc = ngx_http_v3_goaway(c, st->vlint.value); - if (rc != NGX_OK) { - return rc; - } - - st->state = sw_type; - break; - case sw_skip: rc = ngx_http_v3_parse_skip(b, &st->length); diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c index 22ddaf3eb..2fc5b07a4 100644 --- a/src/http/v3/ngx_http_v3_uni.c +++ b/src/http/v3/ngx_http_v3_uni.c @@ -16,19 +16,10 @@ typedef struct { } ngx_http_v3_uni_stream_t; -typedef struct { - ngx_queue_t queue; - uint64_t id; - ngx_connection_t *connection; - ngx_uint_t *npushing; -} ngx_http_v3_push_t; - - static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); static void ngx_http_v3_uni_dummy_read_handler(ngx_event_t *wev); static void ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev); -static void ngx_http_v3_push_cleanup(void *data); static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type); @@ -316,78 +307,6 @@ ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev) } -ngx_connection_t * -ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) -{ - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 2]; - size_t n; - ngx_connection_t *sc; - ngx_pool_cleanup_t *cln; - ngx_http_v3_push_t *push; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 create push stream id:%uL", push_id); - - sc = ngx_quic_open_stream(c, 0); - if (sc == NULL) { - goto failed; - } - - p = buf; - p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_STREAM_PUSH); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, push_id); - n = p - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (sc->send(sc, buf, n) != (ssize_t) n) { - goto failed; - } - - cln = ngx_pool_cleanup_add(sc->pool, sizeof(ngx_http_v3_push_t)); - if (cln == NULL) { - goto failed; - } - - h3c->npushing++; - - cln->handler = ngx_http_v3_push_cleanup; - - push = cln->data; - push->id = push_id; - push->connection = sc; - push->npushing = &h3c->npushing; - - ngx_queue_insert_tail(&h3c->pushing, &push->queue); - - return sc; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create push stream"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, - "failed to create push stream"); - if (sc) { - ngx_http_v3_close_uni_stream(sc); - } - - return NULL; -} - - -static void -ngx_http_v3_push_cleanup(void *data) -{ - ngx_http_v3_push_t *push = data; - - ngx_queue_remove(&push->queue); - (*push->npushing)--; -} - - static ngx_connection_t * ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) { @@ -693,82 +612,6 @@ failed: } -ngx_int_t -ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) -{ - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 MAX_PUSH_ID:%uL", max_push_id); - - if (h3c->max_push_id != (uint64_t) -1 && max_push_id < h3c->max_push_id) { - return NGX_HTTP_V3_ERR_ID_ERROR; - } - - h3c->max_push_id = max_push_id; - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id) -{ - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 GOAWAY:%uL", push_id); - - h3c->goaway_push_id = push_id; - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) -{ - ngx_queue_t *q; - ngx_http_request_t *r; - ngx_http_v3_push_t *push; - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 CANCEL_PUSH:%uL", push_id); - - if (push_id >= h3c->next_push_id) { - return NGX_HTTP_V3_ERR_ID_ERROR; - } - - for (q = ngx_queue_head(&h3c->pushing); - q != ngx_queue_sentinel(&h3c->pushing); - q = ngx_queue_next(q)) - { - push = (ngx_http_v3_push_t *) q; - - if (push->id != push_id) { - continue; - } - - r = push->connection->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 cancel push"); - - ngx_http_finalize_request(r, NGX_HTTP_CLOSE); - - break; - } - - return NGX_OK; -} - - ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) { diff --git a/src/http/v3/ngx_http_v3_uni.h b/src/http/v3/ngx_http_v3_uni.h index 8c1eb99d3..911e153d7 100644 --- a/src/http/v3/ngx_http_v3_uni.h +++ b/src/http/v3/ngx_http_v3_uni.h @@ -17,12 +17,6 @@ void ngx_http_v3_init_uni_stream(ngx_connection_t *c); ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type); -ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, - uint64_t push_id); -ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, - uint64_t max_push_id); -ngx_int_t ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id); -ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); -- cgit