Latest update - 9095

This commit is contained in:
2023-05-19 16:40:20 +09:00
parent 162c842011
commit c54353987b
26 changed files with 17 additions and 2030 deletions
-374
View File
@@ -1,374 +0,0 @@
Experimental QUIC support for nginx
-----------------------------------
1. Introduction
2. Building from sources
3. Configuration
4. Directives
5. Clients
6. Troubleshooting
7. Contributing
8. Links
1. Introduction
This is an experimental QUIC [1] / HTTP/3 [2] support for nginx.
The code is developed in a separate "quic" branch available
at https://hg.nginx.org/nginx-quic. Currently it is based
on nginx mainline 1.23.x. We merge new nginx releases into
this branch regularly.
The project code base is under the same BSD license as nginx.
The code is currently at a beta level of quality, however
there are several production deployments with it.
NGINX Development Team is working on improving HTTP/3 support to
integrate it into the main NGINX codebase. Thus, expect further
updates of this code, including features, changes in behaviour,
bug fixes, and refactoring. NGINX Development team will be
grateful for any feedback and code submissions.
Please contact NGINX Development Team via nginx-devel mailing list [3].
What works now:
IETF QUIC version 1 is supported. Internet drafts are no longer supported.
nginx should be able to respond to HTTP/3 requests over QUIC and
it should be possible to upload and download big files without errors.
+ The handshake completes successfully
+ One endpoint can update keys and its peer responds correctly
+ 0-RTT data is being received and acted on
+ Connection is established using TLS Resume Ticket
+ A handshake that includes a Retry packet completes successfully
+ Stream data is being exchanged and ACK'ed
+ An H3 transaction succeeded
+ One or both endpoints insert entries into dynamic table and
subsequently reference them from header blocks
+ Version Negotiation packet is sent to client with unknown version
+ Lost packets are detected and retransmitted properly
+ Clients may migrate to new address
2. Building from sources
The build is configured using the configure command.
Refer to http://nginx.org/en/docs/configure.html for details.
When configuring nginx, it's possible to enable QUIC and HTTP/3
using the following new configuration options:
--with-http_v3_module - enable QUIC and HTTP/3
--with-stream_quic_module - enable QUIC in Stream
A library that provides QUIC support is recommended to build nginx, there
are several of those available on the market:
+ BoringSSL [4]
+ LibreSSL [5]
+ QuicTLS [6]
Alternatively, nginx can be configured with OpenSSL compatibility
layer, which emulates BoringSSL QUIC API for OpenSSL. This mode is
enabled by default if native QUIC support is not detected.
0-RTT is not supported in OpenSSL compatibility mode.
Clone the NGINX QUIC repository
$ hg clone -b quic https://hg.nginx.org/nginx-quic
$ cd nginx-quic
Use the following command to configure nginx with BoringSSL [4]
$ ./auto/configure --with-debug --with-http_v3_module \
--with-cc-opt="-I../boringssl/include" \
--with-ld-opt="-L../boringssl/build/ssl \
-L../boringssl/build/crypto"
$ make
Alternatively, nginx can be configured with QuicTLS [6]
$ ./auto/configure --with-debug --with-http_v3_module \
--with-cc-opt="-I../quictls/build/include" \
--with-ld-opt="-L../quictls/build/lib"
Alternatively, nginx can be configured with a modern version
of LibreSSL [7]
$ ./auto/configure --with-debug --with-http_v3_module \
--with-cc-opt="-I../libressl/build/include" \
--with-ld-opt="-L../libressl/build/lib"
3. Configuration
The HTTP "listen" directive got a new option "quic" which enables
QUIC as client transport protocol instead of TCP.
The Stream "listen" directive got a new option "quic" which enables
QUIC as client transport protocol instead of TCP or plain UDP.
Along with "quic", it's also possible to specify "reuseport"
option [8] to make it work properly with multiple workers.
To enable address validation:
quic_retry on;
To enable 0-RTT:
ssl_early_data on;
To enable GSO (Generic Segmentation Offloading):
quic_gso on;
To set host key for various tokens:
quic_host_key <filename>;
QUIC requires TLSv1.3 protocol, which is enabled by the default
by "ssl_protocols" directive.
By default, GSO Linux-specific optimization [10] is disabled.
Enable it in case a corresponding network interface is configured to
support GSO.
A number of directives were added that configure HTTP/3:
http3
http3_hq
http3_stream_buffer_size
http3_max_concurrent_pushes
http3_max_concurrent_streams
http3_push
http3_push_preload
In http, an additional variable is available: $http3.
The value of $http3 is "h3" for HTTP/3 connections,
"hq" for hq connections, or an empty string otherwise.
In stream, an additional variable is available: $quic.
The value of $quic is "quic" if QUIC connection is used,
or an empty string otherwise.
Example configuration:
http {
log_format quic '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" "$http3"';
access_log logs/access.log quic;
server {
# for better compatibility it's recommended
# to use the same port for quic and https
listen 8443 quic reuseport;
listen 8443 ssl;
ssl_certificate certs/example.com.crt;
ssl_certificate_key certs/example.com.key;
location / {
# required for browsers to direct them into quic port
add_header Alt-Svc 'h3=":8443"; ma=86400';
}
}
}
4. Directives
Syntax: quic_bpf on | off;
Default: quic_bpf off;
Context: main
Enables routing of QUIC packets using eBPF.
When enabled, this allows to support QUIC connection migration.
The directive is only supported on Linux 5.7+.
Syntax: quic_retry on | off;
Default: quic_retry off;
Context: http | stream, server
Enables the QUIC Address Validation feature. This includes:
- sending a new token in a Retry packet or a NEW_TOKEN frame
- validating a token received in the Initial packet
Syntax: quic_gso on | off;
Default: quic_gso off;
Context: http | stream, server
Enables sending in optimized batch mode using segmentation offloading.
Optimized sending is only supported on Linux featuring UDP_SEGMENT.
Syntax: quic_host_key file;
Default: -
Context: http | stream, server
Specifies a file with the secret key used to encrypt stateless reset and
address validation tokens. By default, a randomly generated key is used.
Syntax: quic_active_connection_id_limit number;
Default: quic_active_connection_id_limit 2;
Context: http | stream, server
Sets the QUIC active_connection_id_limit transport parameter value.
This is the maximum number of connection IDs we are willing to store.
Syntax: quic_timeout time;
Default: quic_timeout 60s;
Context: stream, server
Defines a timeout used to negotiate the QUIC idle timeout.
In the http module, it is taken from the keepalive_timeout directive.
Syntax: quic_stream_buffer_size size;
Default: quic_stream_buffer_size 64k;
Context: stream, server
Syntax: http3_stream_buffer_size size;
Default: http3_stream_buffer_size 64k;
Context: http, server
Sets buffer size for reading and writing of the QUIC STREAM payload.
The buffer size is used to calculate initial flow control limits
in the following QUIC transport parameters:
- initial_max_data
- initial_max_stream_data_bidi_local
- initial_max_stream_data_bidi_remote
- initial_max_stream_data_uni
Syntax: http3_max_concurrent_pushes number;
Default: http3_max_concurrent_pushes 10;
Context: http, server
Limits the maximum number of concurrent push requests in a connection.
Syntax: http3_max_concurrent_streams number;
Default: http3_max_concurrent_streams 128;
Context: http, server
Sets the maximum number of concurrent HTTP/3 streams in a connection.
Syntax: http3_push uri | off;
Default: http3_push off;
Context: http, server, location
Pre-emptively sends (pushes) a request to the specified uri along with
the response to the original request. Only relative URIs with absolute
path will be processed, for example:
http3_push /static/css/main.css;
The uri value can contain variables.
Several http3_push directives can be specified on the same configuration
level. The off parameter cancels the effect of the http3_push directives
inherited from the previous configuration level.
Syntax: http3_push_preload on | off;
Default: http3_push_preload off;
Context: http, server, location
Enables automatic conversion of preload links specified in the “Link”
response header fields into push requests.
Syntax: http3 on | off;
Default: http3 on;
Context: http, server
Enables HTTP/3 protocol negotiation.
Syntax: http3_hq on | off;
Default: http3_hq off;
Context: http, server
Enables HTTP/0.9 protocol negotiation used in QUIC interoperability tests.
5. Clients
* Browsers
Known to work: Firefox 90+ and Chrome 92+ (QUIC version 1)
Beware of strange issues: sometimes browser may decide to ignore QUIC
Cache clearing/restart might help. Always check access.log and
error.log to make sure the browser is using HTTP/3 and not TCP https.
* Console clients
Known to work: ngtcp2, firefox's neqo and chromium's console clients:
$ examples/client 127.0.0.1 8443 https://example.com:8443/index.html
$ ./neqo-client https://127.0.0.1:8443/
$ chromium-build/out/my_build/quic_client http://example.com:8443
In case everyhing is right, the access log should show something like:
127.0.0.1 - - [24/Apr/2020:11:27:29 +0300] "GET / HTTP/3" 200 805 "-"
"nghttp3/ngtcp2 client" "quic"
6. Troubleshooting
Here are some tips that may help to identify problems:
+ Ensure nginx is built with proper SSL library that supports QUIC
+ Ensure nginx is using the proper SSL library in runtime
(`nginx -V` shows what it's using)
+ Ensure a client is actually sending requests over QUIC
(see "Clients" section about browsers and cache)
We recommend to start with simple console client like ngtcp2
to ensure the server is configured properly before trying
with real browsers that may be very picky with certificates,
for example.
+ Build nginx with debug support [9] and check the debug log.
It should contain all details about connection and why it
failed. All related messages contain "quic " prefix and can
be easily filtered out.
+ For a deeper investigation, please enable additional debugging
in src/event/quic/ngx_event_quic_connection.h:
#define NGX_QUIC_DEBUG_PACKETS
#define NGX_QUIC_DEBUG_FRAMES
#define NGX_QUIC_DEBUG_ALLOC
#define NGX_QUIC_DEBUG_CRYPTO
7. Contributing
Please refer to
http://nginx.org/en/docs/contributing_changes.html
8. Links
[1] https://datatracker.ietf.org/doc/html/rfc9000
[2] https://datatracker.ietf.org/doc/html/rfc9114
[3] https://mailman.nginx.org/mailman/listinfo/nginx-devel
[4] https://boringssl.googlesource.com/boringssl/
[5] https://www.libressl.org/
[6] https://github.com/quictls/openssl
[7] https://github.com/libressl-portable/portable/releases/tag/v3.6.0
[8] https://nginx.org/en/docs/http/ngx_http_core_module.html#listen
[9] https://nginx.org/en/docs/debugging_log.html
[10] http://vger.kernel.org/lpc_net2018_talks/willemdebruijn-lpc2018-udpgso-paper-DRAFT-1.pdf
-14
View File
@@ -1079,20 +1079,6 @@ if [ $STREAM != NO ]; then
ngx_module_incs=
if [ $STREAM_QUIC = YES ]; then
USE_OPENSSL_QUIC=YES
have=NGX_STREAM_QUIC . auto/have
STREAM_SSL=YES
ngx_module_name=ngx_stream_quic_module
ngx_module_deps=src/stream/ngx_stream_quic_module.h
ngx_module_srcs=src/stream/ngx_stream_quic_module.c
ngx_module_libs=
ngx_module_link=$STREAM_QUIC
. auto/module
fi
if [ $STREAM_SSL = YES ]; then
USE_OPENSSL=YES
have=NGX_STREAM_SSL . auto/have
-3
View File
@@ -120,7 +120,6 @@ MAIL_SMTP=YES
STREAM=NO
STREAM_SSL=NO
STREAM_QUIC=NO
STREAM_REALIP=NO
STREAM_LIMIT_CONN=YES
STREAM_ACCESS=YES
@@ -326,7 +325,6 @@ use the \"--with-mail_ssl_module\" option instead"
--with-stream) STREAM=YES ;;
--with-stream=dynamic) STREAM=DYNAMIC ;;
--with-stream_ssl_module) STREAM_SSL=YES ;;
--with-stream_quic_module) STREAM_QUIC=YES ;;
--with-stream_realip_module) STREAM_REALIP=YES ;;
--with-stream_geoip_module) STREAM_GEOIP=YES ;;
--with-stream_geoip_module=dynamic)
@@ -550,7 +548,6 @@ cat << END
--with-stream enable TCP/UDP proxy module
--with-stream=dynamic enable dynamic TCP/UDP proxy module
--with-stream_ssl_module enable ngx_stream_ssl_module
--with-stream_quic_module enable ngx_stream_quic_module
--with-stream_realip_module enable ngx_stream_realip_module
--with-stream_geoip_module enable ngx_stream_geoip_module
--with-stream_geoip_module=dynamic enable dynamic ngx_stream_geoip_module
+4
View File
@@ -72,6 +72,10 @@ ngx_create_listening(ngx_conf_t *cf, struct sockaddr *sockaddr,
ngx_memcpy(ls->addr_text.data, text, len);
#if !(NGX_WIN32)
ngx_rbtree_init(&ls->rbtree, &ls->sentinel, ngx_udp_rbtree_insert_value);
#endif
ls->fd = (ngx_socket_t) -1;
ls->type = SOCK_STREAM;
+4 -2
View File
@@ -417,8 +417,8 @@ ngx_udp_rbtree_insert_value(ngx_rbtree_node_t *temp,
udpt = (ngx_udp_connection_t *) temp;
ct = udpt->connection;
rc = ngx_cmp_sockaddr(c->sockaddr, c->socklen,
ct->sockaddr, ct->socklen, 1);
rc = ngx_memn2cmp(udp->key.data, udpt->key.data,
udp->key.len, udpt->key.len);
if (rc == 0 && c->listening->wildcard) {
rc = ngx_cmp_sockaddr(c->local_sockaddr, c->local_socklen,
@@ -471,6 +471,8 @@ ngx_insert_udp_connection(ngx_connection_t *c)
ngx_crc32_final(hash);
udp->node.key = hash;
udp->key.data = (u_char *) c->sockaddr;
udp->key.len = c->socklen;
cln = ngx_pool_cleanup_add(c->pool, 0);
if (cln == NULL) {
+1
View File
@@ -27,6 +27,7 @@ struct ngx_udp_connection_s {
ngx_rbtree_node_t node;
ngx_connection_t *connection;
ngx_buf_t *buffer;
ngx_str_t key;
};
-2
View File
@@ -111,8 +111,6 @@ struct ngx_quic_stream_s {
void ngx_quic_recvmsg(ngx_event_t *ev);
void ngx_quic_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
void ngx_quic_run(ngx_connection_t *c, ngx_quic_conf_t *conf);
ngx_connection_t *ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi);
void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t err,
+1
View File
@@ -179,6 +179,7 @@ ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,
qsock->udp.connection = c;
qsock->udp.node.key = ngx_crc32_long(id.data, id.len);
qsock->udp.key = id;
ngx_rbtree_insert(&c->listening->rbtree, &qsock->udp.node);
-53
View File
@@ -365,59 +365,6 @@ ngx_quic_close_accepted_connection(ngx_connection_t *c)
}
void
ngx_quic_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
ngx_int_t rc;
ngx_connection_t *c, *ct;
ngx_rbtree_node_t **p;
ngx_quic_socket_t *qsock, *qsockt;
for ( ;; ) {
if (node->key < temp->key) {
p = &temp->left;
} else if (node->key > temp->key) {
p = &temp->right;
} else { /* node->key == temp->key */
qsock = (ngx_quic_socket_t *) node;
c = qsock->udp.connection;
qsockt = (ngx_quic_socket_t *) temp;
ct = qsockt->udp.connection;
rc = ngx_memn2cmp(qsock->sid.id, qsockt->sid.id,
qsock->sid.len, qsockt->sid.len);
if (rc == 0 && c->listening->wildcard) {
rc = ngx_cmp_sockaddr(c->local_sockaddr, c->local_socklen,
ct->local_sockaddr, ct->local_socklen, 1);
}
p = (rc < 0) ? &temp->left : &temp->right;
}
if (*p == sentinel) {
break;
}
temp = *p;
}
*p = node;
node->parent = temp;
node->left = sentinel;
node->right = sentinel;
ngx_rbt_red(node);
}
static ngx_connection_t *
ngx_quic_lookup_connection(ngx_listening_t *ls, ngx_str_t *key,
struct sockaddr *local_sockaddr, socklen_t local_socklen)
-7
View File
@@ -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;
-4
View File
@@ -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;
-13
View File
@@ -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;
-685
View File
@@ -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)
{
+2 -130
View File
@@ -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;
}
+4 -87
View File
@@ -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);
-157
View File
@@ -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)
{
-6
View File
@@ -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);
-24
View File
@@ -518,24 +518,6 @@ ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
ls->reuseport = addr[i].opt.reuseport;
#endif
#if (NGX_STREAM_QUIC)
ls->quic = addr[i].opt.quic;
if (ls->quic) {
ngx_rbtree_init(&ls->rbtree, &ls->sentinel,
ngx_quic_rbtree_insert_value);
}
#endif
#if !(NGX_WIN32)
if (!ls->quic) {
ngx_rbtree_init(&ls->rbtree, &ls->sentinel,
ngx_udp_rbtree_insert_value);
}
#endif
stport = ngx_palloc(cf->pool, sizeof(ngx_stream_port_t));
if (stport == NULL) {
return NGX_CONF_ERROR;
@@ -593,9 +575,6 @@ ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
addrs[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_STREAM_SSL)
addrs[i].conf.ssl = addr[i].opt.ssl;
#endif
#if (NGX_STREAM_QUIC)
addrs[i].conf.quic = addr[i].opt.quic;
#endif
addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
addrs[i].conf.addr_text = addr[i].opt.addr_text;
@@ -631,9 +610,6 @@ ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
addrs6[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_STREAM_SSL)
addrs6[i].conf.ssl = addr[i].opt.ssl;
#endif
#if (NGX_STREAM_QUIC)
addrs6[i].conf.quic = addr[i].opt.quic;
#endif
addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
addrs6[i].conf.addr_text = addr[i].opt.addr_text;
-6
View File
@@ -16,10 +16,6 @@
#include <ngx_stream_ssl_module.h>
#endif
#if (NGX_STREAM_QUIC)
#include <ngx_stream_quic_module.h>
#endif
typedef struct ngx_stream_session_s ngx_stream_session_t;
@@ -55,7 +51,6 @@ typedef struct {
unsigned bind:1;
unsigned wildcard:1;
unsigned ssl:1;
unsigned quic:1;
#if (NGX_HAVE_INET6)
unsigned ipv6only:1;
#endif
@@ -81,7 +76,6 @@ typedef struct {
ngx_stream_conf_ctx_t *ctx;
ngx_str_t addr_text;
unsigned ssl:1;
unsigned quic:1;
unsigned proxy_protocol:1;
} ngx_stream_addr_conf_t;
-29
View File
@@ -760,29 +760,6 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#endif
}
if (ngx_strcmp(value[i].data, "quic") == 0) {
#if (NGX_STREAM_QUIC)
ngx_stream_ssl_conf_t *sslcf;
sslcf = ngx_stream_conf_get_module_srv_conf(cf,
ngx_stream_ssl_module);
sslcf->listen = 1;
sslcf->file = cf->conf_file->file.name.data;
sslcf->line = cf->conf_file->line;
ls->quic = 1;
ls->type = SOCK_DGRAM;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the \"quic\" parameter requires "
"ngx_stream_quic_module");
return NGX_CONF_ERROR;
#endif
}
if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {
if (ngx_strcmp(&value[i].data[13], "on") == 0) {
@@ -894,12 +871,6 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
#endif
#if (NGX_STREAM_SSL && NGX_STREAM_QUIC)
if (ls->ssl && ls->quic) {
return "\"ssl\" parameter is incompatible with \"quic\"";
}
#endif
if (ls->so_keepalive) {
return "\"so_keepalive\" parameter is incompatible with \"udp\"";
}
-19
View File
@@ -129,10 +129,6 @@ ngx_stream_init_connection(ngx_connection_t *c)
s->ssl = addr_conf->ssl;
#endif
#if (NGX_STREAM_QUIC)
s->ssl |= addr_conf->quic;
#endif
if (c->buffer) {
s->received += c->buffer->last - c->buffer->pos;
}
@@ -177,21 +173,6 @@ ngx_stream_init_connection(ngx_connection_t *c)
s->start_sec = tp->sec;
s->start_msec = tp->msec;
#if (NGX_STREAM_QUIC)
if (addr_conf->quic) {
ngx_quic_conf_t *qcf;
if (c->quic == NULL) {
qcf = ngx_stream_get_module_srv_conf(addr_conf->ctx,
ngx_stream_quic_module);
ngx_quic_run(c, qcf);
return;
}
}
#endif
rev = c->read;
rev->handler = ngx_stream_session_handler;
-15
View File
@@ -1772,21 +1772,6 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream,
if (dst->type == SOCK_STREAM && pscf->half_close
&& src->read->eof && !u->half_closed && !dst->buffered)
{
#if (NGX_STREAM_QUIC)
if (dst->quic) {
if (ngx_quic_shutdown_stream(dst, NGX_WRITE_SHUTDOWN)
!= NGX_OK)
{
ngx_stream_proxy_finalize(s,
NGX_STREAM_INTERNAL_SERVER_ERROR);
return;
}
} else
#endif
if (ngx_shutdown_socket(dst->fd, NGX_WRITE_SHUTDOWN) == -1) {
ngx_connection_error(c, ngx_socket_errno,
ngx_shutdown_socket_n " failed");
-343
View File
@@ -1,343 +0,0 @@
/*
* Copyright (C) Nginx, Inc.
* Copyright (C) Roman Arutyunyan
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_stream.h>
static ngx_int_t ngx_stream_variable_quic(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_stream_quic_add_variables(ngx_conf_t *cf);
static void *ngx_stream_quic_create_srv_conf(ngx_conf_t *cf);
static char *ngx_stream_quic_merge_srv_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_stream_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_stream_quic_commands[] = {
{ ngx_string("quic_timeout"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_quic_conf_t, timeout),
NULL },
{ ngx_string("quic_stream_buffer_size"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_quic_conf_t, stream_buffer_size),
NULL },
{ ngx_string("quic_retry"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_quic_conf_t, retry),
NULL },
{ ngx_string("quic_gso"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_quic_conf_t, gso_enabled),
NULL },
{ ngx_string("quic_host_key"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
ngx_stream_quic_host_key,
NGX_STREAM_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("quic_active_connection_id_limit"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_quic_conf_t, active_connection_id_limit),
NULL },
ngx_null_command
};
static ngx_stream_module_t ngx_stream_quic_module_ctx = {
ngx_stream_quic_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
ngx_stream_quic_create_srv_conf, /* create server configuration */
ngx_stream_quic_merge_srv_conf, /* merge server configuration */
};
ngx_module_t ngx_stream_quic_module = {
NGX_MODULE_V1,
&ngx_stream_quic_module_ctx, /* module context */
ngx_stream_quic_commands, /* module directives */
NGX_STREAM_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_stream_variable_t ngx_stream_quic_vars[] = {
{ ngx_string("quic"), NULL, ngx_stream_variable_quic, 0, 0, 0 },
ngx_stream_null_variable
};
static ngx_str_t ngx_stream_quic_salt = ngx_string("ngx_quic");
static ngx_int_t
ngx_stream_variable_quic(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data)
{
if (s->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_stream_quic_add_variables(ngx_conf_t *cf)
{
ngx_stream_variable_t *var, *v;
for (v = ngx_stream_quic_vars; v->name.len; v++) {
var = ngx_stream_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_stream_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->host_key = { 0, NULL }
* conf->stream_close_code = 0;
* conf->stream_reject_code_uni = 0;
* conf->stream_reject_code_bidi= 0;
*/
conf->timeout = NGX_CONF_UNSET_MSEC;
conf->stream_buffer_size = NGX_CONF_UNSET_SIZE;
conf->max_concurrent_streams_bidi = NGX_CONF_UNSET_UINT;
conf->max_concurrent_streams_uni = NGX_CONF_UNSET_UINT;
conf->retry = NGX_CONF_UNSET;
conf->gso_enabled = NGX_CONF_UNSET;
conf->active_connection_id_limit = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_stream_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_stream_ssl_conf_t *scf;
ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
ngx_conf_merge_size_value(conf->stream_buffer_size,
prev->stream_buffer_size,
65536);
ngx_conf_merge_uint_value(conf->max_concurrent_streams_bidi,
prev->max_concurrent_streams_bidi, 16);
ngx_conf_merge_uint_value(conf->max_concurrent_streams_uni,
prev->max_concurrent_streams_uni, 3);
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, "");
ngx_conf_merge_uint_value(conf->active_connection_id_limit,
conf->active_connection_id_limit,
2);
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_stream_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_stream_quic_salt,
conf->sr_token_key, NGX_QUIC_SR_KEY_LEN)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
scf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_ssl_module);
conf->ssl = &scf->ssl;
return NGX_CONF_OK;
}
static char *
ngx_stream_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;
}
-20
View File
@@ -1,20 +0,0 @@
/*
* Copyright (C) Roman Arutyunyan
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_STREAM_QUIC_H_INCLUDED_
#define _NGX_STREAM_QUIC_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_stream.h>
extern ngx_module_t ngx_stream_quic_module;
#endif /* _NGX_STREAM_QUIC_H_INCLUDED_ */
-31
View File
@@ -9,10 +9,6 @@
#include <ngx_core.h>
#include <ngx_stream.h>
#if (NGX_QUIC_OPENSSL_COMPAT)
#include <ngx_event_quic_openssl_compat.h>
#endif
typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
ngx_pool_t *pool, ngx_str_t *s);
@@ -1197,10 +1193,7 @@ ngx_stream_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
static ngx_int_t
ngx_stream_ssl_init(ngx_conf_t *cf)
{
ngx_uint_t i;
ngx_stream_listen_t *listen;
ngx_stream_handler_pt *h;
ngx_stream_ssl_conf_t *scf;
ngx_stream_core_main_conf_t *cmcf;
cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
@@ -1212,29 +1205,5 @@ ngx_stream_ssl_init(ngx_conf_t *cf)
*h = ngx_stream_ssl_handler;
listen = cmcf->listen.elts;
for (i = 0; i < cmcf->listen.nelts; i++) {
if (!listen[i].quic) {
continue;
}
scf = listen[i].ctx->srv_conf[ngx_stream_ssl_module.ctx_index];
#if (NGX_QUIC_OPENSSL_COMPAT)
if (ngx_quic_compat_init(cf, scf->ssl.ctx) != NGX_OK) {
return NGX_ERROR;
}
#endif
if (scf->certificates && !(scf->protocols & NGX_SSL_TLSv1_3)) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"\"ssl_protocols\" must enable TLSv1.3 for "
"the \"listen ... quic\" directive in %s:%ui",
scf->file, scf->line);
return NGX_ERROR;
}
}
return NGX_OK;
}
+1 -6
View File
@@ -277,12 +277,7 @@ ngx_stream_write_filter(ngx_stream_session_t *s, ngx_chain_t *in,
*out = chain;
if (chain) {
if (c->shared
#if (NGX_STREAM_QUIC)
&& c->quic == NULL
#endif
)
{
if (c->shared) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"shared connection is busy");
return NGX_ERROR;