From 992c970dc3af115f49c1ce00cd0b33655f74969b Mon Sep 17 00:00:00 2001 From: fjx Date: Mon, 22 Jul 2024 15:44:58 +0800 Subject: [PATCH] =?UTF-8?q?add=20http2=20proxy=20=EF=BC=88cherry=20picked?= =?UTF-8?q?=20commit=20from=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- auto/modules | 2 + src/core/njt_connection.h | 4 + src/core/njt_output_chain.c | 1 + src/http/modules/njt_http_proxy_module.c | 1137 ++++++++++++++++- src/http/modules/njt_http_proxy_module.h | 7 + .../njt_http_upstream_keepalive_module.c | 44 +- src/http/njt_http_core_module.c | 1 + src/http/njt_http_header_filter_module.c | 48 + src/http/njt_http_request.h | 1 + src/http/njt_http_request_body.c | 20 +- src/http/njt_http_upstream.c | 563 ++++++-- src/http/njt_http_upstream.h | 28 + src/http/v2/njt_http_v2.c | 699 ++++++++-- src/http/v2/njt_http_v2.h | 36 + src/http/v2/njt_http_v2_filter_module.c | 65 +- src/http/v2/njt_http_v2_stream.c | 885 +++++++++++++ src/http/v2/njt_http_v2_stream.h | 28 + src/http/v2/njt_http_v2_table.c | 4 +- 18 files changed, 3312 insertions(+), 261 deletions(-) create mode 100644 src/http/v2/njt_http_v2_stream.c create mode 100644 src/http/v2/njt_http_v2_stream.h diff --git a/auto/modules b/auto/modules index be6fc9c3..213aafff 100755 --- a/auto/modules +++ b/auto/modules @@ -432,10 +432,12 @@ if [ $HTTP = YES ]; then njt_module_name=njt_http_v2_module njt_module_incs=src/http/v2 njt_module_deps="src/http/v2/njt_http_v2.h \ + src/http/v2/njt_http_v2_stream.h \ src/http/v2/njt_http_v2_module.h" njt_module_srcs="src/http/v2/njt_http_v2.c \ src/http/v2/njt_http_v2_table.c \ src/http/v2/njt_http_v2_encode.c \ + src/http/v2/njt_http_v2_stream.c \ src/http/v2/njt_http_v2_module.c" njt_module_libs= njt_module_link=$HTTP_V2 diff --git a/src/core/njt_connection.h b/src/core/njt_connection.h index 1f6a4620..b0881ea2 100644 --- a/src/core/njt_connection.h +++ b/src/core/njt_connection.h @@ -165,6 +165,10 @@ struct njt_connection_s { njt_ssl_connection_t *ssl; #endif +#if (NJT_HTTP_V2) + void *stream; +#endif + njt_udp_connection_t *udp; struct sockaddr *local_sockaddr; diff --git a/src/core/njt_output_chain.c b/src/core/njt_output_chain.c index 2943aace..200eff12 100644 --- a/src/core/njt_output_chain.c +++ b/src/core/njt_output_chain.c @@ -506,6 +506,7 @@ njt_output_chain_copy_buf(njt_output_chain_ctx_t *ctx) src = ctx->in->buf; dst = ctx->buf; + dst->last_buf = 0; size = njt_buf_size(src); size = njt_min(size, dst->end - dst->pos); diff --git a/src/http/modules/njt_http_proxy_module.c b/src/http/modules/njt_http_proxy_module.c index 86e6009f..16eab0f7 100644 --- a/src/http/modules/njt_http_proxy_module.c +++ b/src/http/modules/njt_http_proxy_module.c @@ -3,6 +3,7 @@ * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. * Copyright (C) 2021-2023 TMLake(Beijing) Technology Co., Ltd. + * Copyright (C) 2024 JD Technology Information Technology Co., Ltd. */ @@ -133,7 +134,54 @@ static char * njt_http_proxy_ssl_alpn(njt_conf_t *cf, njt_command_t *cmd, void *conf); #endif - +#if (NJT_HTTP_V2) + +/* context for creating http/2 request */ +typedef struct { + /* calculated length of request */ + size_t n; + + /* encode method state */ + njt_str_t method; + + /* encode path state */ + size_t loc_len; + size_t uri_len; + uintptr_t escape; + njt_uint_t unparsed_uri; + + /* tmp buff */ + u_char *tmp; + size_t max_tmp_len; + + /* encode headers state */ + size_t max_head; + njt_http_proxy_headers_t *headers; + njt_http_script_engine_t le; + njt_http_script_engine_t e; + +} njt_http_v2_proxy_ctx_t; + +static njt_int_t njt_http_v2_proxy_create_request(njt_http_request_t *r); +static njt_int_t njt_http_v2_proxy_encode_method(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c, njt_buf_t *b); +static njt_inline njt_uint_t njt_http_v2_map_method(njt_uint_t method); +static njt_int_t njt_http_v2_proxy_encode_path(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c, njt_buf_t *b); +static njt_int_t njt_http_v2_proxy_encode_authority(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c, njt_buf_t *b); +static njt_int_t njt_http_v2_proxy_encode_headers(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c, njt_buf_t *b); +static njt_int_t njt_http_v2_proxy_body_length(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c); +static njt_chain_t *njt_http_v2_proxy_encode_body(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c); +static njt_int_t njt_http_v2_proxy_reinit_request(njt_http_request_t *r); +static njt_int_t njt_http_v2_proxy_process_header(njt_http_request_t *r); +static void njt_http_v2_proxy_abort_request(njt_http_request_t *r); +static void njt_http_v2_proxy_finalize_request(njt_http_request_t *r, + njt_int_t rc); +#endif static njt_conf_post_t njt_http_proxy_lowat_post = @@ -179,6 +227,8 @@ static njt_conf_post_t njt_http_proxy_ssl_conf_command_post = static njt_conf_enum_t njt_http_proxy_http_version[] = { { njt_string("1.0"), NJT_HTTP_VERSION_10 }, { njt_string("1.1"), NJT_HTTP_VERSION_11 }, + { njt_string("2"), NJT_HTTP_VERSION_20 }, + { njt_string("3"), NJT_HTTP_VERSION_30 }, { njt_null_string, 0 } }; @@ -571,7 +621,8 @@ static njt_command_t njt_http_proxy_commands[] = { &njt_http_upstream_ignore_headers_masks }, { njt_string("proxy_http_version"), - NJT_HTTP_MAIN_CONF|NJT_HTTP_SRV_CONF|NJT_HTTP_LOC_CONF|NJT_CONF_TAKE1, + NJT_HTTP_MAIN_CONF|NJT_HTTP_SRV_CONF|NJT_HTTP_LOC_CONF + |NJT_HTTP_LIF_CONF|NJT_HTTP_LMT_CONF|NJT_CONF_TAKE1, njt_conf_set_enum_slot, NJT_HTTP_LOC_CONF_OFFSET, offsetof(njt_http_proxy_loc_conf_t, http_version), @@ -708,7 +759,32 @@ static njt_command_t njt_http_proxy_commands[] = { #endif #endif +#if (NJT_HTTP_V2) + { njt_string("http2_max_concurrent_streams"), + NJT_HTTP_MAIN_CONF|NJT_HTTP_SRV_CONF|NJT_HTTP_LOC_CONF|NJT_CONF_TAKE1, + njt_conf_set_num_slot, + NJT_HTTP_LOC_CONF_OFFSET, + offsetof(njt_http_proxy_loc_conf_t, + upstream.h2_conf.concurrent_streams), + NULL }, + + { njt_string("http2_streams_index_size"), + NJT_HTTP_MAIN_CONF|NJT_HTTP_SRV_CONF|NJT_HTTP_LOC_CONF|NJT_CONF_TAKE1, + njt_conf_set_num_slot, + NJT_HTTP_LOC_CONF_OFFSET, + offsetof(njt_http_proxy_loc_conf_t, + upstream.h2_conf.streams_index_mask), + NULL }, + + { njt_string("http2_streams_recv_window"), + NJT_HTTP_MAIN_CONF|NJT_HTTP_SRV_CONF|NJT_HTTP_LOC_CONF|NJT_CONF_TAKE1, + njt_conf_set_num_slot, + NJT_HTTP_LOC_CONF_OFFSET, + offsetof(njt_http_proxy_loc_conf_t, + upstream.h2_conf.recv_window), + NULL }, +#endif njt_null_command }; @@ -760,6 +836,54 @@ static njt_keyval_t njt_http_proxy_headers[] = { { njt_null_string, njt_null_string } }; +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + +/* + * RFC 9114 4.2 HTTP Fields + * + * An intermediary transforming an HTTP/1.x message to HTTP/3 MUST remove + * connection-specific header fields as discussed in Section 7.6.1 of [HTTP], + * or their messages will be treated by other HTTP/3 endpoints as malformed. + */ +static njt_keyval_t njt_http_v3_proxy_headers[] = { + { njt_string("Content-Length"), njt_string("$proxy_internal_body_length") }, +#if 0 + /* TODO: trailers */ + { njt_string("TE"), njt_string("$v3_proxy_internal_trailers") }, +#endif + { njt_string("Host"), njt_string("") }, + { njt_string("Connection"), njt_string("") }, + { njt_string("Transfer-Encoding"), njt_string("") }, + { njt_string("Keep-Alive"), njt_string("") }, + { njt_string("Expect"), njt_string("") }, + { njt_string("Upgrade"), njt_string("") }, + { njt_null_string, njt_null_string } +}; + +#if (NJT_HTTP_CACHE) + +static njt_keyval_t njt_http_v3_proxy_cache_headers[] = { + { njt_string("Host"), njt_string("") }, + { njt_string("Connection"), njt_string("") }, + { njt_string("Content-Length"), njt_string("$proxy_internal_body_length") }, + { njt_string("Transfer-Encoding"), njt_string("") }, + { njt_string("TE"), njt_string("") }, + { njt_string("Keep-Alive"), njt_string("") }, + { njt_string("Expect"), njt_string("") }, + { njt_string("Upgrade"), njt_string("") }, + { njt_string("If-Modified-Since"), + njt_string("$upstream_cache_last_modified") }, + { njt_string("If-Unmodified-Since"), njt_string("") }, + { njt_string("If-None-Match"), njt_string("$upstream_cache_etag") }, + { njt_string("If-Match"), njt_string("") }, + { njt_string("Range"), njt_string("") }, + { njt_string("If-Range"), njt_string("") }, + { njt_null_string, njt_null_string } +}; + +#endif + +#endif static njt_str_t njt_http_proxy_hide_headers[] = { njt_string("Date"), @@ -917,6 +1041,9 @@ njt_http_proxy_handler(njt_http_request_t *r) u = r->upstream; if (plcf->proxy_lengths == NULL) { +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + ctx->host = plcf->host; +#endif ctx->vars = plcf->vars; u->schema = plcf->vars.schema; #if(NJT_HTTP_DYN_PROXY_PASS) @@ -952,6 +1079,18 @@ njt_http_proxy_handler(njt_http_request_t *r) u->finalize_request = njt_http_proxy_finalize_request; r->state = 0; +#if (NJT_HTTP_V2) + if (plcf->http_version == NJT_HTTP_VERSION_20) { + u->h2 = 1; + + u->create_request = njt_http_v2_proxy_create_request; + u->reinit_request = njt_http_v2_proxy_reinit_request; + u->process_header = njt_http_v2_proxy_process_header; + u->abort_request = njt_http_v2_proxy_abort_request; + u->finalize_request = njt_http_v2_proxy_finalize_request; + } +#endif + if (plcf->redirects) { u->rewrite_redirect = njt_http_proxy_rewrite_redirect; } @@ -979,7 +1118,14 @@ njt_http_proxy_handler(njt_http_request_t *r) if (!plcf->upstream.request_buffering && plcf->body_values == NULL && plcf->upstream.pass_request_body && (!r->headers_in.chunked - || plcf->http_version == NJT_HTTP_VERSION_11)) + || (plcf->http_version == NJT_HTTP_VERSION_11 +#if (NJT_HTTP_V3) + || plcf->http_version == NJT_HTTP_VERSION_30 +#endif +#if (NJT_HTTP_V2) + || plcf->http_version == NJT_HTTP_VERSION_20 +#endif + ))) { r->request_body_no_buffering = 1; } @@ -1096,7 +1242,21 @@ njt_http_proxy_eval(njt_http_request_t *r, njt_http_proxy_ctx_t *ctx, u->resolved->host = url.host; u->resolved->port = (in_port_t) (url.no_port ? port : url.port); u->resolved->no_port = url.no_port; +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + if (url.family != AF_UNIX) { + + if (url.no_port) { + ctx->host = url.host; + + } else { + ctx->host.len = url.host.len + 1 + url.port_text.len; + ctx->host.data = url.host.data; + } + } else { + njt_str_set(&ctx->host, "localhost"); + } +#endif return NJT_OK; } @@ -3528,6 +3688,11 @@ njt_http_proxy_create_loc_conf(njt_conf_t *cf) njt_str_set(&conf->upstream.module, "proxy"); +#if (NJT_HTTP_V2) + conf->upstream.h2_conf.recv_window = NJT_CONF_UNSET_SIZE; + conf->upstream.h2_conf.concurrent_streams = NJT_CONF_UNSET_UINT; + conf->upstream.h2_conf.streams_index_mask = NJT_CONF_UNSET_UINT; +#endif return conf; } @@ -3541,6 +3706,7 @@ njt_http_proxy_merge_loc_conf(njt_conf_t *cf, void *parent, void *child) u_char *p; size_t size; njt_int_t rc; + njt_keyval_t *proxy_headers; njt_hash_init_t hash; njt_http_core_loc_conf_t *clcf; njt_http_proxy_rewrite_t *pr; @@ -3959,6 +4125,14 @@ njt_http_proxy_merge_loc_conf(njt_conf_t *cf, void *parent, void *child) hash.bucket_size = conf->headers_hash_bucket_size; hash.name = "proxy_headers_hash"; +#if (NJT_HTTP_V2 ) + if (conf->http_version == NJT_HTTP_VERSION_20) { + conf->upstream.alpn.data = (unsigned char *) + NJT_HTTP_V2_ALPN_PROTO; + conf->upstream.alpn.len = sizeof(NJT_HTTP_V2_ALPN_PROTO) - 1; + + } +#endif if (njt_http_upstream_hide_headers_hash(cf, &conf->upstream, &prev->upstream, njt_http_proxy_hide_headers, &hash) != NJT_OK) @@ -3981,6 +4155,9 @@ njt_http_proxy_merge_loc_conf(njt_conf_t *cf, void *parent, void *child) conf->location = prev->location; conf->vars = prev->vars; +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + conf->host = prev->host; +#endif conf->proxy_lengths = prev->proxy_lengths; conf->proxy_values = prev->proxy_values; @@ -4021,15 +4198,33 @@ njt_http_proxy_merge_loc_conf(njt_conf_t *cf, void *parent, void *child) njt_conf_merge_ptr_value(conf->headers_source, prev->headers_source, NULL); - if (conf->headers_source == prev->headers_source) { + if (conf->headers_source == prev->headers_source + #if (NJT_HTTP_V3 || NJT_HTTP_V2) + /* H3 uses own set of headers, so do not inherit on version change */ + && !((conf->http_version >= NJT_HTTP_VERSION_20 + || prev->http_version >= NJT_HTTP_VERSION_20) + && conf->http_version != prev->http_version) + #endif + ) + { conf->headers = prev->headers; #if (NJT_HTTP_CACHE) conf->headers_cache = prev->headers_cache; +#endif +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + conf->host_set = prev->host_set; #endif } + proxy_headers = njt_http_proxy_headers; +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + if (conf->http_version == NJT_HTTP_VERSION_20 || + conf->http_version == NJT_HTTP_VERSION_30) { + proxy_headers = njt_http_v3_proxy_headers; + } +#endif rc = njt_http_proxy_init_headers(cf, conf, &conf->headers, - njt_http_proxy_headers); + proxy_headers); if (rc != NJT_OK) { return NJT_CONF_ERROR; } @@ -4037,8 +4232,17 @@ njt_http_proxy_merge_loc_conf(njt_conf_t *cf, void *parent, void *child) #if (NJT_HTTP_CACHE) if (conf->upstream.cache) { + + proxy_headers = njt_http_proxy_cache_headers; + +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + if (conf->http_version == NJT_HTTP_VERSION_20 || + conf->http_version == NJT_HTTP_VERSION_30) { + proxy_headers = njt_http_v3_proxy_cache_headers; + } +#endif rc = njt_http_proxy_init_headers(cf, conf, &conf->headers_cache, - njt_http_proxy_cache_headers); + proxy_headers); if (rc != NJT_OK) { return NJT_CONF_ERROR; } @@ -4057,9 +4261,19 @@ njt_http_proxy_merge_loc_conf(njt_conf_t *cf, void *parent, void *child) prev->headers = conf->headers; #if (NJT_HTTP_CACHE) prev->headers_cache = conf->headers_cache; +#endif +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + prev->host_set = conf->host_set; #endif } - +#if (NJT_HTTP_V2 ) + njt_conf_merge_size_value(conf->upstream.h2_conf.recv_window, + prev->upstream.h2_conf.recv_window, 65536); + njt_conf_merge_uint_value(conf->upstream.h2_conf.concurrent_streams, + prev->upstream.h2_conf.concurrent_streams, 128); + njt_conf_merge_uint_value(conf->upstream.h2_conf.streams_index_mask, + prev->upstream.h2_conf.streams_index_mask, 32 - 1); +#endif return NJT_CONF_OK; } @@ -4110,6 +4324,13 @@ njt_http_proxy_init_headers(njt_conf_t *cf, njt_http_proxy_loc_conf_t *conf, src = conf->headers_source->elts; for (i = 0; i < conf->headers_source->nelts; i++) { +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + if (src[i].key.len == 4 + && njt_strncasecmp(src[i].key.data, (u_char *) "Host", 4) == 0) + { + conf->host_set = 1; + } +#endif s = njt_array_push(&headers_merged); if (s == NULL) { return NJT_ERROR; @@ -4324,6 +4545,22 @@ njt_http_proxy_pass(njt_conf_t *cf, njt_command_t *cmd, void *conf) plcf->vars.schema.data = url->data; plcf->vars.key_start = plcf->vars.schema; +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + if (u.family != AF_UNIX) { + + if (u.no_port) { + plcf->host = u.host; + + } else { + plcf->host.len = u.host.len + 1 + u.port_text.len; + plcf->host.data = u.host.data; + } + + } else { + njt_str_set(&plcf->host, "localhost"); + } +#endif + njt_http_proxy_set_vars(&u, &plcf->vars); plcf->location = clcf->name; @@ -5415,5 +5652,891 @@ njt_http_proxy_ssl_alpn(njt_conf_t *cf, njt_command_t *cmd, void *conf) } #endif +#if (NJT_HTTP_V2) + +static njt_int_t +njt_http_v2_proxy_create_request(njt_http_request_t *r) +{ + njt_buf_t *b; + njt_chain_t *cl ,*out, *body; + njt_http_upstream_t *u; + njt_http_proxy_ctx_t *ctx; + njt_http_v2_proxy_ctx_t v2c; + njt_http_proxy_headers_t *headers; + njt_http_proxy_loc_conf_t *plcf; + + /* + * HTTP/3 Request: + * + * HEADERS FRAME + * :method: + * :scheme: + * :path: + * :authority: + * proxy headers[] + * client headers[] + * + * DATA FRAME + * body + * + * HEADERS FRAME + * trailers[] + */ + + njt_log_debug0(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 proxy create request"); + u = r->upstream; + + plcf = njt_http_get_module_loc_conf(r, njt_http_proxy_module); + +#if (NJT_HTTP_CACHE) + headers = u->cacheable ? &plcf->headers_cache : &plcf->headers; +#else + headers = &plcf->headers; +#endif + njt_memzero(&v2c,sizeof(njt_http_v2_proxy_ctx_t)); + + njt_http_script_flush_no_cacheable_variables(r, plcf->body_flushes); + njt_http_script_flush_no_cacheable_variables(r, headers->flushes); + + v2c.headers = headers; + /* calculate lengths */ + + njt_http_v2_proxy_encode_method(r, &v2c, NULL); + + //schme + v2c.n += 1; + + if (njt_http_v2_proxy_encode_path(r, &v2c, NULL) != NJT_OK) { + return NJT_ERROR; + } + + if (njt_http_v2_proxy_encode_authority(r, &v2c, NULL) != NJT_OK) { + return NJT_ERROR; + } + + if (njt_http_v2_proxy_body_length(r, &v2c) != NJT_OK) { + return NJT_ERROR; + } + + if (njt_http_v2_proxy_encode_headers(r, &v2c, NULL) != NJT_OK) { + return NJT_ERROR; + } + + /* generate HTTP/2 request of known size */ + + b = njt_create_temp_buf(r->pool, v2c.n); + if (b == NULL) { + return NJT_ERROR; + } + + if (v2c.max_tmp_len > 0) { + v2c.tmp = njt_pnalloc(r->pool,v2c.max_tmp_len); + if (v2c.tmp == NULL) { + return NJT_ERROR; + } + } + + if (njt_http_v2_proxy_encode_method(r, &v2c, b) != NJT_OK) { + return NJT_ERROR; + } + + *b->last++ = njt_http_v2_indexed(NJT_HTTP_V2_SCHEME_HTTPS_INDEX); + + + if (njt_http_v2_proxy_encode_path(r, &v2c, b) != NJT_OK) { + return NJT_ERROR; + } + + if (njt_http_v2_proxy_encode_authority(r, &v2c, b) != NJT_OK) { + return NJT_ERROR; + } + + if (njt_http_v2_proxy_encode_headers(r, &v2c, b) != NJT_OK) { + return NJT_ERROR; + } + + cl = njt_alloc_chain_link(r->pool); + if (cl == NULL) { + return NJT_ERROR; + } + + if (r->stream) { + b->last_buf = r->stream->in_closed || + (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked); + } else { + //当下游为http3时,r->headers_in.content_length_n == 0和 + //r->headers_in.chunked ==1 同时成立 + b->last_buf = r->headers_in.content_length_n == 0 || + (r->headers_in.content_length_n < 0 && !r->headers_in.chunked); + } + + cl->buf = b; + cl->next = NULL; + out = cl; + + ctx = njt_http_get_module_ctx(r, njt_http_proxy_module); + + if (r->request_body_no_buffering || ctx->internal_chunked) { + //http2 在fake client层封包,不需要特殊处理,缺省即可 + u->output.output_filter = njt_chain_writer; + u->output.filter_ctx = &u->writer; + + } else if (ctx->internal_body_length != -1) { + + body = njt_http_v2_proxy_encode_body(r, &v2c); + if (body == NJT_CHAIN_ERROR) { + return NJT_ERROR; + } + + for (cl = out; cl->next; cl = cl->next) { } + cl->next = body; + } + + /* TODO: trailers */ + u->request_bufs = out; + + return NJT_OK; +} + +static njt_int_t +njt_http_v2_proxy_encode_method(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c, njt_buf_t *b) +{ + size_t n; + njt_str_t method; + njt_uint_t v3method; + njt_http_upstream_t *u; + njt_http_proxy_ctx_t *ctx; + njt_http_proxy_loc_conf_t *plcf; + + static njt_str_t njt_http_v2_header_method = njt_string(":method"); + + if (b == NULL) { + /* calculate length */ + + plcf = njt_http_get_module_loc_conf(r, njt_http_proxy_module); + ctx = njt_http_get_module_ctx(r, njt_http_proxy_module); + + method.len = 0; + n = 0; + + u = r->upstream; + + if (u->method.len) { + /* HEAD was changed to GET to cache response */ + method = u->method; + + } else if (plcf->method) { + if (njt_http_complex_value(r, plcf->method, &method) != NJT_OK) { + return NJT_ERROR; + } + } else { + method = r->method_name; + } + + if (method.len == 4 + && njt_strncasecmp(method.data, (u_char *) "HEAD", 4) == 0) + { + ctx->head = 1; + } + + if (method.len) { + n = 1 + NJT_HTTP_V2_INT_OCTETS + njt_http_v2_header_method.len + + NJT_HTTP_V2_INT_OCTETS + method.len; + } else { + + v3method = njt_http_v2_map_method(r->method); + + if (v3method) { + n = 1; + + } else { + n = 1 + NJT_HTTP_V2_INT_OCTETS + njt_http_v2_header_method.len + + NJT_HTTP_V2_INT_OCTETS + r->method_name.len; + } + } + if (n > v2c->max_tmp_len) { + v2c->max_tmp_len = n; + } + v2c->n += n; + v2c->method = method; + + return NJT_OK; + } + + method = v2c->method; + + if (method.len) { + *b->last++ = 0; + b->last = njt_http_v2_write_name(b->last,njt_http_v2_header_method.data, + njt_http_v2_header_method.len,v2c->tmp); + b->last = njt_http_v2_write_value(b->last,method.data,method.len,v2c->tmp); + } else { + + v3method = njt_http_v2_map_method(r->method); + + if (v3method) { + *b->last++ = njt_http_v2_indexed(v3method); + } else { + *b->last++ = 0; + b->last = njt_http_v2_write_name(b->last,njt_http_v2_header_method.data, + njt_http_v2_header_method.len,v2c->tmp); + b->last = njt_http_v2_write_value(b->last,r->method_name.data,r->method_name.len,v2c->tmp); + } + } + + return NJT_OK; +} + +static njt_inline njt_uint_t +njt_http_v2_map_method(njt_uint_t method) +{ + switch (method) { + case NJT_HTTP_GET: + return NJT_HTTP_V2_METHOD_GET_INDEX; + case NJT_HTTP_POST: + return NJT_HTTP_V2_METHOD_POST_INDEX; + default: + return 0; + } +} + +static njt_int_t +njt_http_v2_proxy_encode_headers(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c, njt_buf_t *b) +{ + u_char *p, *start; + size_t key_len, val_len, hlen, max_head, n; + njt_str_t tmp, tmpv; + njt_uint_t i; + njt_list_part_t *part; + njt_table_elt_t *header; + njt_http_script_code_pt code; + njt_http_proxy_headers_t *headers; + njt_http_script_engine_t *le; + njt_http_script_engine_t *e; + njt_http_proxy_loc_conf_t *plcf; + njt_http_script_len_code_pt lcode; + + plcf = njt_http_get_module_loc_conf(r, njt_http_proxy_module); + + headers = v2c->headers; + le = &v2c->le; + e = &v2c->e; + + if (b == NULL) { + + le->ip = headers->lengths->elts; + le->request = r; + le->flushed = 1; + + n = 0; + max_head = 0; + + while (*(uintptr_t *) le->ip) { + + lcode = *(njt_http_script_len_code_pt *) le->ip; + key_len = lcode(le); + + for (val_len = 0; *(uintptr_t *) le->ip; val_len += lcode(le)) { + lcode = *(njt_http_script_len_code_pt *) le->ip; + } + le->ip += sizeof(uintptr_t); + + if (val_len == 0) { + continue; + } + + hlen = key_len + val_len; + if (hlen > max_head) { + max_head = hlen; + } + if (key_len > v2c->max_tmp_len) { + v2c->max_tmp_len = key_len; + } + + if (val_len > v2c->max_tmp_len) { + v2c->max_tmp_len = val_len; + } + + n += 1 + NJT_HTTP_V2_INT_OCTETS + key_len + + NJT_HTTP_V2_INT_OCTETS + val_len; + } + + if (plcf->upstream.pass_request_headers) { + part = &r->headers_in.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 (njt_hash_find(&headers->hash, header[i].hash, + header[i].lowcase_key, header[i].key.len)) + { + continue; + } + n += 1 + NJT_HTTP_V2_INT_OCTETS + header[i].key.len + + NJT_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > v2c->max_tmp_len) { + v2c->max_tmp_len = header[i].key.len; + } + if (header[i].value.len > v2c->max_tmp_len) { + v2c->max_tmp_len = header[i].value.len; + } + } + } + + v2c->n += n; + v2c->max_head = max_head; + + return NJT_OK; + } + + max_head = v2c->max_head; + + p = njt_pnalloc(r->pool, max_head); + if (p == NULL) { + return NJT_ERROR; + } + + start = p; + + njt_memzero(e, sizeof(njt_http_script_engine_t)); + + e->ip = headers->values->elts; + e->pos = p; + e->request = r; + e->flushed = 1; + + le->ip = headers->lengths->elts; + + tmp.data = p; + tmp.len = 0; + + tmpv.data = NULL; + tmpv.len = 0; + + while (*(uintptr_t *) le->ip) { + + lcode = *(njt_http_script_len_code_pt *) le->ip; + (void) lcode(le); + + for (val_len = 0; *(uintptr_t *) le->ip; val_len += lcode(le)) { + lcode = *(njt_http_script_len_code_pt *) le->ip; + } + le->ip += sizeof(uintptr_t); + + if (val_len == 0) { + e->skip = 1; + + while (*(uintptr_t *) e->ip) { + code = *(njt_http_script_code_pt *) e->ip; + code((njt_http_script_engine_t *) e); + } + e->ip += sizeof(uintptr_t); + + e->skip = 0; + + continue; + } + + code = *(njt_http_script_code_pt *) e->ip; + code((njt_http_script_engine_t *) e); + + tmp.len = e->pos - tmp.data; + tmpv.data = e->pos; + + while (*(uintptr_t *) e->ip) { + code = *(njt_http_script_code_pt *) e->ip; + code((njt_http_script_engine_t *) e); + } + e->ip += sizeof(uintptr_t); + + tmpv.len = e->pos - tmpv.data; + + *b->last++ = 0; + b->last = njt_http_v2_write_name(b->last,tmp.data,tmp.len,v2c->tmp); + b->last = njt_http_v2_write_value(b->last,tmpv.data,tmpv.len,v2c->tmp); + + tmp.data = p; + tmp.len = 0; + + tmpv.data = NULL; + tmpv.len = 0; + e->pos = start; + } + + if (plcf->upstream.pass_request_headers) { + part = &r->headers_in.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 (njt_hash_find(&headers->hash, header[i].hash, + header[i].lowcase_key, header[i].key.len)) + { + continue; + } + + *b->last++ = 0; + b->last = njt_http_v2_write_name(b->last,header[i].key.data,header[i].key.len,v2c->tmp); + b->last = njt_http_v2_write_value(b->last,header[i].value.data,header[i].value.len,v2c->tmp); + + njt_log_debug2(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \"%V: %V\"", + &header[i].key, &header[i].value); + } + } + + return NJT_OK; +} + +static njt_int_t +njt_http_v2_proxy_encode_path(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c, njt_buf_t *b) +{ + size_t n; + u_char *p; + size_t loc_len; + size_t uri_len; + uintptr_t escape; + njt_uint_t unparsed_uri; + njt_http_upstream_t *u; + njt_http_proxy_ctx_t *ctx; + njt_http_proxy_loc_conf_t *plcf; + + static njt_str_t njt_http_v2_path = njt_string(":path"); + + plcf = njt_http_get_module_loc_conf(r, njt_http_proxy_module); + ctx = njt_http_get_module_ctx(r, njt_http_proxy_module); + + if (b == NULL) { + + escape = 0; + uri_len = 0; + loc_len = 0; + unparsed_uri = 0; + + if (plcf->proxy_lengths && ctx->vars.uri.len) { + uri_len = ctx->vars.uri.len; + + } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri) { + unparsed_uri = 1; + uri_len = r->unparsed_uri.len; + + } else { + loc_len = (r->valid_location && ctx->vars.uri.len) ? + plcf->location.len : 0; + + if (r->quoted_uri || r->internal) { + escape = 2 * njt_escape_uri(NULL, r->uri.data + loc_len, + r->uri.len - loc_len, + NJT_ESCAPE_URI); + } + + uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape + + sizeof("?") - 1 + r->args.len; + } + + if (uri_len == 0) { + njt_log_error(NJT_LOG_ERR, r->connection->log, 0, + "zero length URI to proxy"); + return NJT_ERROR; + } + + n = 1 + NJT_HTTP_V2_INT_OCTETS + njt_http_v2_path.len + + NJT_HTTP_V2_INT_OCTETS + uri_len; + + v2c->n += n; + + if (uri_len > v2c->max_tmp_len) { + v2c->max_tmp_len = uri_len; + } + + v2c->escape = escape; + v2c->uri_len = uri_len; + v2c->loc_len = loc_len; + v2c->unparsed_uri = unparsed_uri; + + return NJT_OK; + } + + u = r->upstream; + + escape = v2c->escape; + uri_len = v2c->uri_len; + loc_len = v2c->loc_len; + unparsed_uri = v2c->unparsed_uri; + + p = njt_palloc(r->pool, uri_len); + if (p == NULL) { + return NJT_ERROR; + } + + u->uri.data = p; + + if (plcf->proxy_lengths && ctx->vars.uri.len) { + p = njt_copy(p, ctx->vars.uri.data, ctx->vars.uri.len); + + } else if (unparsed_uri) { + p = njt_copy(p, r->unparsed_uri.data, r->unparsed_uri.len); + + } else { + if (r->valid_location) { + p = njt_copy(p, ctx->vars.uri.data, ctx->vars.uri.len); + } + + if (escape) { + njt_escape_uri(p, r->uri.data + loc_len, + r->uri.len - loc_len, NJT_ESCAPE_URI); + p += r->uri.len - loc_len + escape; + + } else { + p = njt_copy(p, r->uri.data + loc_len, r->uri.len - loc_len); + } + + if (r->args.len > 0) { + *p++ = '?'; + p = njt_copy(p, r->args.data, r->args.len); + } + } + + u->uri.len = p - u->uri.data; + *b->last++ = 0; + b->last = njt_http_v2_write_name(b->last,njt_http_v2_path.data,njt_http_v2_path.len,v2c->tmp); + b->last = njt_http_v2_write_value(b->last,u->uri.data,u->uri.len,v2c->tmp); + return NJT_OK; +} + +static njt_int_t +njt_http_v2_proxy_encode_authority(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c, njt_buf_t *b) +{ + size_t n; + njt_http_proxy_ctx_t *ctx; + njt_http_proxy_loc_conf_t *plcf; + static njt_str_t njt_http_v2_auth = njt_string(":authority"); + + plcf = njt_http_get_module_loc_conf(r, njt_http_proxy_module); + + if (plcf->host_set) { + return NJT_OK; + } + + ctx = njt_http_get_module_ctx(r, njt_http_proxy_module); + + if (b == NULL) { + + n = 1 + NJT_HTTP_V2_INT_OCTETS + njt_http_v2_auth.len + + NJT_HTTP_V2_INT_OCTETS + ctx->host.len; + v2c->n += n; + if ( ctx->host.len > v2c->max_tmp_len) { + v2c->max_tmp_len = ctx->host.len; + } + + return NJT_OK; + } + *b->last++ = 0; + b->last = njt_http_v2_write_name(b->last,njt_http_v2_auth.data,njt_http_v2_auth.len,v2c->tmp); + b->last = njt_http_v2_write_value(b->last,ctx->host.data,ctx->host.len,v2c->tmp); + + njt_log_debug1(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 header: \":authority: %V\"", &ctx->host); + + return NJT_OK; +} + +static njt_int_t +njt_http_v2_proxy_body_length(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c) +{ + size_t body_len; + njt_http_proxy_ctx_t *ctx; + njt_http_script_engine_t *le; + njt_http_proxy_loc_conf_t *plcf; + njt_http_script_len_code_pt lcode; + + plcf = njt_http_get_module_loc_conf(r, njt_http_proxy_module); + ctx = njt_http_get_module_ctx(r, njt_http_proxy_module); + + le = &v2c->le; + + if (plcf->body_lengths) { + le->ip = plcf->body_lengths->elts; + le->request = r; + le->flushed = 1; + body_len = 0; + + while (*(uintptr_t *) le->ip) { + lcode = *(njt_http_script_len_code_pt *) le->ip; + body_len += lcode(le); + } + + ctx->internal_body_length = body_len; + + } else if (r->headers_in.chunked && r->reading_body) { + ctx->internal_body_length = -1; + ctx->internal_chunked = 1; + + } else { + ctx->internal_body_length = r->headers_in.content_length_n; + } + + return NJT_OK; +} + +static njt_chain_t * +njt_http_v2_proxy_encode_body(njt_http_request_t *r, + njt_http_v2_proxy_ctx_t *v2c) +{ + njt_buf_t *b; + njt_chain_t *cl, *body, *prev, *head; + njt_http_upstream_t *u; + njt_http_proxy_ctx_t *ctx; + njt_http_script_code_pt code; + njt_http_script_engine_t *e; + njt_http_proxy_loc_conf_t *plcf; + + plcf = njt_http_get_module_loc_conf(r, njt_http_proxy_module); + ctx = njt_http_get_module_ctx(r, njt_http_proxy_module); + /* body set in configuration */ + + u = r->upstream; + + if (plcf->body_values) { + + e = &v2c->e; + + cl = njt_alloc_chain_link(r->pool); + if (cl == NULL) { + return NJT_CHAIN_ERROR; + } + + b = njt_create_temp_buf(r->pool, ctx->internal_body_length); + if (b == NULL) { + return NJT_CHAIN_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + e->ip = plcf->body_values->elts; + e->pos = b->last; + e->skip = 0; + + while (*(uintptr_t *) e->ip) { + code = *(njt_http_script_code_pt *) e->ip; + code((njt_http_script_engine_t *) e); + } + + b->last = e->pos; + b->last_buf = 1; + + return cl; + } + + if (!plcf->upstream.pass_request_body) { + return NULL; + } + + /* body from client */ + + cl = NULL; + head = NULL; + prev = NULL; + + body = u->request_bufs; + + while (body) { + + b = njt_alloc_buf(r->pool); + if (b == NULL) { + return NJT_CHAIN_ERROR; + } + + njt_memcpy(b, body->buf, sizeof(njt_buf_t)); + + cl = njt_alloc_chain_link(r->pool); + if (cl == NULL) { + return NJT_CHAIN_ERROR; + } + + cl->buf = b; + + if (prev) { + prev->next = cl; + + } else { + head = cl; + } + + prev = cl; + body = body->next; + } + + if (cl) { + cl->next = NULL; + } + + return head; +} + +static njt_int_t +njt_http_v2_proxy_process_header(njt_http_request_t *r) +{ + u_char *p; + njt_buf_t *b; + njt_int_t rc; + njt_connection_t *c, c_stub; + njt_http_upstream_t *u; + njt_http_v2_connection_t *h2c, h2c_stub; + njt_http_v2_stream_t *stream,stream_stub; + njt_http_v2_state_t tmp_state; + njt_http_core_srv_conf_t *cscf; + + u = r->upstream; + c = u->peer.connection; + + njt_log_debug3(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 proxy header cache:%p, c:%p, buffer:%d", + r->cache, c, u->buffer.last - u->buffer.pos); + +#if (NJT_HTTP_CACHE) + /*from cache file*/ + if (r->cache && + ( !c + || u->cache_status == NJT_HTTP_CACHE_STALE + || u->cache_status == NJT_HTTP_CACHE_REVALIDATED )) + { + njt_memzero(&h2c_stub,sizeof(njt_http_v2_connection_t)); + njt_memzero(&stream_stub,sizeof(njt_http_v2_stream_t)); + njt_memzero(&c_stub,sizeof(njt_connection_t)); + njt_memzero(&tmp_state,sizeof(njt_http_v2_state_t)); + + cscf = njt_http_get_module_srv_conf(r, njt_http_core_module); + + h2c = &h2c_stub; + h2c->fake = 1; + h2c->client = 1; + h2c->pool = r->pool; + h2c->state.pool = r->pool; + h2c->state.flags |= NJT_HTTP_V2_END_HEADERS_FLAG; + h2c->state.handler = njt_http_v2_state_header_block; + h2c->state.length = r->cache->body_start - r->cache->header_start; + h2c->state.keep_pool = 1; + h2c->http_connection = r->http_connection; + h2c->state.header_limit = cscf->large_client_header_buffers.size + * cscf->large_client_header_buffers.num; + c = &c_stub; + c->log = r->connection->log; + c->pool = r->pool; + h2c->connection = c; + + stream = &stream_stub; + stream->state = &tmp_state; + stream->request = r; + h2c->state.stream = stream; + } else { +#endif + stream = c->stream; + h2c = njt_http_v2_get_connection(c); +#if (NJT_HTTP_CACHE) + } +#endif + /*save state*/ + njt_memcpy(&tmp_state, &h2c->state, sizeof(njt_http_v2_state_t)); + njt_memcpy(&h2c->state, stream->state, sizeof(njt_http_v2_state_t)); + + b = &u->buffer; + p = b->pos; + + h2c->state.parse = 1; + rc = njt_http_v2_parse_headers(h2c, b); + h2c->state.parse = 0; + + /*restore state*/ + njt_memcpy(stream->state, &h2c->state, sizeof(njt_http_v2_state_t)); + njt_memcpy(&h2c->state, &tmp_state, sizeof(njt_http_v2_state_t)); + + njt_log_debug3(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "njt_http_v2_parse_headers rc:%d pos:%p, last:%p", + rc, b->pos, b->last); + + if (rc == NJT_ERROR) { + return NJT_ERROR; + } + + if (h2c) { + h2c->total_bytes += b->pos - p; + } + + if (rc == NJT_AGAIN) { + return NJT_AGAIN; + } + + return NJT_OK; +} + +static njt_int_t +njt_http_v2_proxy_reinit_request(njt_http_request_t *r) +{ + njt_http_proxy_ctx_t *ctx; + + ctx = njt_http_get_module_ctx(r, njt_http_proxy_module); + + if (ctx == NULL) { + return NJT_OK; + } + + ctx->status.code = 0; + ctx->status.count = 0; + ctx->status.start = NULL; + ctx->status.end = NULL; + ctx->chunked.state = 0; + + r->upstream->process_header = njt_http_v2_proxy_process_header; + r->upstream->pipe->input_filter = njt_http_proxy_copy_filter; + r->upstream->input_filter = njt_http_proxy_non_buffered_copy_filter; + r->state = 0; + + return NJT_OK; +} + +static void +njt_http_v2_proxy_abort_request(njt_http_request_t *r) +{ + njt_log_debug0(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "abort http v2 proxy request"); +} + + +static void +njt_http_v2_proxy_finalize_request(njt_http_request_t *r, njt_int_t rc) +{ + njt_log_debug0(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "finalize http v2 proxy request"); +} + +#endif diff --git a/src/http/modules/njt_http_proxy_module.h b/src/http/modules/njt_http_proxy_module.h index febc663e..c18949aa 100644 --- a/src/http/modules/njt_http_proxy_module.h +++ b/src/http/modules/njt_http_proxy_module.h @@ -130,6 +130,10 @@ typedef struct { njt_str_t ori_url; #endif +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + njt_str_t host; + njt_uint_t host_set; +#endif } njt_http_proxy_loc_conf_t; @@ -142,6 +146,9 @@ typedef struct { njt_chain_t *free; njt_chain_t *busy; +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + njt_str_t host; +#endif unsigned head:1; unsigned internal_chunked:1; unsigned header_sent:1; diff --git a/src/http/modules/njt_http_upstream_keepalive_module.c b/src/http/modules/njt_http_upstream_keepalive_module.c index 1964baf9..bdbb8c49 100644 --- a/src/http/modules/njt_http_upstream_keepalive_module.c +++ b/src/http/modules/njt_http_upstream_keepalive_module.c @@ -283,6 +283,17 @@ found: njt_log_debug1(NJT_LOG_DEBUG_HTTP, pc->log, 0, "get keepalive peer: using connection %p", c); +#if (NJT_HTTP_V2) + if (c->stream) { + njt_http_v2_connection_t *h2c = ((njt_http_v2_stream_t*)c->stream)->connection; + pc->connection = h2c->connection; + pc->cached = 1; + h2c->processing++; + njt_http_v2_close_stream(c->stream,0); + h2c->processing--; + return NJT_DONE; + } +#endif c->idle = 0; c->sent = 0; c->data = NULL; @@ -309,6 +320,7 @@ njt_http_upstream_free_keepalive_peer(njt_peer_connection_t *pc, void *data, njt_http_upstream_keepalive_peer_data_t *kp = data; njt_http_upstream_keepalive_cache_t *item; + njt_uint_t requests; njt_queue_t *q; njt_connection_t *c; njt_http_upstream_t *u; @@ -321,18 +333,40 @@ njt_http_upstream_free_keepalive_peer(njt_peer_connection_t *pc, void *data, u = kp->upstream; c = pc->connection; + if (c == NULL) { + goto invalid; + } + if (state & NJT_PEER_FAILED || c == NULL +#if (NJT_HTTP_V3 || NJT_HTTP_V2) + /* quic stream is done when using: ok to have EOF/write err */ + || (c->quic == NULL && c->stream == NULL && c->read->eof) + || (c->quic == NULL && c->stream == NULL && c->write->error) + || c->type == SOCK_DGRAM +#else || c->read->eof + || c->write->error +#endif || c->read->error || c->read->timedout - || c->write->error || c->write->timedout) { goto invalid; } +#if (NJT_HTTP_V3) + requests = c->quic ? c->quic->parent->requests : c->requests; +#else + requests = c->requests; +#endif +#if (NJT_HTTP_V2) + if (c->stream) { + njt_http_v2_connection_t *h2c = ((njt_http_v2_stream_t*)c->stream)->connection; + requests = h2c->connection->requests; + } +#endif - if (c->requests >= kp->conf->requests) { + if (requests >= kp->conf->requests) { goto invalid; } @@ -466,6 +500,12 @@ static void njt_http_upstream_keepalive_close(njt_connection_t *c) { +#if (NJT_HTTP_V2) + if (c->stream) { + njt_http_v2_close_stream(c->stream,0); + return; + } +#endif #if (NJT_HTTP_SSL) if (c->ssl) { diff --git a/src/http/njt_http_core_module.c b/src/http/njt_http_core_module.c index 6203d1c7..4f2a211d 100644 --- a/src/http/njt_http_core_module.c +++ b/src/http/njt_http_core_module.c @@ -2595,6 +2595,7 @@ njt_http_subrequest(njt_http_request_t *r, #if (NJT_HTTP_V2) sr->stream = r->stream; + sr->http_connection = r->http_connection; #endif sr->method = NJT_HTTP_GET; diff --git a/src/http/njt_http_header_filter_module.c b/src/http/njt_http_header_filter_module.c index eec0d015..24ba45b6 100644 --- a/src/http/njt_http_header_filter_module.c +++ b/src/http/njt_http_header_filter_module.c @@ -625,6 +625,54 @@ njt_http_header_filter(njt_http_request_t *r) return njt_http_write_filter(r, &out); } +njt_str_t * +njt_http_status_line(njt_uint_t status) +{ + njt_str_t *status_line; + + if (status >= NJT_HTTP_OK + && status < NJT_HTTP_LAST_2XX) + { + /* 2XX */ + + status -= NJT_HTTP_OK; + status_line = &njt_http_status_lines[status]; + + } else if (status >= NJT_HTTP_MOVED_PERMANENTLY + && status < NJT_HTTP_LAST_3XX) + { + /* 3XX */ + + status = status - NJT_HTTP_MOVED_PERMANENTLY + + NJT_HTTP_OFF_3XX; + + status_line = &njt_http_status_lines[status]; + + } else if (status >= NJT_HTTP_BAD_REQUEST + && status < NJT_HTTP_LAST_4XX) + { + /* 4XX */ + status = status - NJT_HTTP_BAD_REQUEST + + NJT_HTTP_OFF_4XX; + + status_line = &njt_http_status_lines[status]; + + } else if (status >= NJT_HTTP_INTERNAL_SERVER_ERROR + && status < NJT_HTTP_LAST_5XX) + { + /* 5XX */ + status = status - NJT_HTTP_INTERNAL_SERVER_ERROR + + NJT_HTTP_OFF_5XX; + + status_line = &njt_http_status_lines[status]; + + } else { + return NULL; + } + + return status_line; +} + static njt_int_t njt_http_header_filter_init(njt_conf_t *cf) diff --git a/src/http/njt_http_request.h b/src/http/njt_http_request.h index c4e4e8ab..901129dc 100644 --- a/src/http/njt_http_request.h +++ b/src/http/njt_http_request.h @@ -636,6 +636,7 @@ typedef struct { #define njt_http_ephemeral(r) (void *) (&r->uri_start) +njt_str_t *njt_http_status_line(njt_uint_t status); extern njt_http_header_t njt_http_headers_in[]; extern njt_http_header_out_t njt_http_headers_out[]; diff --git a/src/http/njt_http_request_body.c b/src/http/njt_http_request_body.c index 54279fe4..7a02639c 100644 --- a/src/http/njt_http_request_body.c +++ b/src/http/njt_http_request_body.c @@ -641,6 +641,11 @@ njt_http_discard_request_body(njt_http_request_t *r) #if (NJT_HTTP_V2) if (r->stream) { r->stream->skip_data = 1; + //forbid RST_STEAM + r->stream->in_closed = 1; + /*这种情况下,接收包头,未接收完包体,即向后端转发请求, + 后端响应请求后,即关闭stream,导致RST_STEAM发送, + 进而导致request失败,需优化HTTP2废弃包处理*/ return NJT_OK; } #endif @@ -1222,6 +1227,13 @@ njt_http_request_body_chunked_filter(njt_http_request_t *r, njt_chain_t *in) rb->rest = 0; + if (out) { + for (cl = out; cl->next; cl = cl->next) { } + cl->buf->last_buf = 1; + break; + } + /*增加空buf,njt_chain_update_chains方法会回收, + 设置last_buf = 1 标志不生效 */ tl = njt_chain_get_free_buf(r->pool, &rb->free); if (tl == NULL) { return NJT_HTTP_INTERNAL_SERVER_ERROR; @@ -1298,14 +1310,15 @@ njt_http_request_body_save_filter(njt_http_request_t *r, njt_chain_t *in) for (cl = in; cl; cl = cl->next) { - njt_log_debug7(NJT_LOG_DEBUG_EVENT, r->connection->log, 0, + njt_log_debug8(NJT_LOG_DEBUG_EVENT, r->connection->log, 0, "http body new buf t:%d f:%d %p, pos %p, size: %z " - "file: %O, size: %O", + "file: %O, size: %O last:%d", 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); + cl->buf->file_last - cl->buf->file_pos, + cl->buf->last_buf); if (cl->buf->last_buf) { @@ -1377,6 +1390,7 @@ njt_http_request_body_save_filter(njt_http_request_t *r, njt_chain_t *in) b->in_file = 1; b->file_last = rb->temp_file->file.offset; b->file = &rb->temp_file->file; + b->last_buf = 1; rb->bufs = cl; } diff --git a/src/http/njt_http_upstream.c b/src/http/njt_http_upstream.c index 6309abd3..245c975e 100644 --- a/src/http/njt_http_upstream.c +++ b/src/http/njt_http_upstream.c @@ -3,6 +3,7 @@ * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. * Copyright (C) 2021-2023 TMLake(Beijing) Technology Co., Ltd. + * Copyright (C) 2024 JD Technology Information Technology Co., Ltd. */ @@ -45,6 +46,8 @@ static void njt_http_upstream_check_broken_connection(njt_http_request_t *r, // static void njt_http_upstream_connect(njt_http_request_t *r, // njt_http_upstream_t *u); //end update by clb +static njt_int_t njt_http_upstream_configure(njt_http_request_t *r, + njt_http_upstream_t *u, njt_connection_t *c); static njt_int_t njt_http_upstream_reinit(njt_http_request_t *r, njt_http_upstream_t *u); static void njt_http_upstream_send_request(njt_http_request_t *r, @@ -103,6 +106,8 @@ static void njt_http_upstream_dummy_handler(njt_http_request_t *r, njt_http_upstream_t *u); static void njt_http_upstream_next(njt_http_request_t *r, njt_http_upstream_t *u, njt_uint_t ft_type); +static void njt_http_upstream_close_peer_connection(njt_http_request_t *r, + njt_http_upstream_t *u, njt_uint_t no_send); static void njt_http_upstream_cleanup(void *data); static void njt_http_upstream_finalize_request(njt_http_request_t *r, njt_http_upstream_t *u, njt_int_t rc); @@ -185,7 +190,7 @@ static void *njt_http_upstream_create_main_conf(njt_conf_t *cf); static char *njt_http_upstream_init_main_conf(njt_conf_t *cf, void *conf); #if (NJT_HTTP_SSL) -static void njt_http_upstream_ssl_init_connection(njt_http_request_t *, +static void njt_http_upstream_ssl_init_connection(njt_http_request_t *r, njt_http_upstream_t *u, njt_connection_t *c); static void njt_http_upstream_ssl_handshake_handler(njt_connection_t *c); static void njt_http_upstream_ssl_handshake(njt_http_request_t *, @@ -201,7 +206,20 @@ static njt_int_t njt_http_upstream_ssl_certificates(njt_http_request_t *r, #endif #endif +#if (NJT_HTTP_V2) +static njt_int_t njt_http_v2_upstream_init_connection(njt_http_request_t *, + njt_http_upstream_t *u, njt_connection_t *c); +static njt_int_t njt_http_v2_upstream_init(njt_connection_t *c); +static njt_int_t njt_http_v2_upstream_reuse_connection(njt_http_request_t *r, + njt_http_upstream_t *u, njt_connection_t *c); +static njt_int_t njt_http_v2_upstream_send_header(njt_http_request_t *r); +njt_http_v2_out_frame_t *njt_http_v2_create_headers_frame(njt_http_v2_stream_t *stream, + u_char *pos, u_char *end, njt_uint_t fin); +njt_int_t njt_http_v2_filter_send(njt_connection_t *fc, + njt_http_v2_stream_t *stream); +void njt_http_v2_filter_cleanup(void *data); +#endif static njt_http_upstream_header_t njt_http_upstream_headers_in[] = { @@ -1142,7 +1160,8 @@ njt_http_upstream_cache_send(njt_http_request_t *r, njt_http_upstream_t *u) r->cached = 1; c = r->cache; - + njt_log_debug0(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream cache send reponse"); if (c->header_start == c->body_start) { r->http_version = NJT_HTTP_VERSION_9; return njt_http_cache_send(r); @@ -1619,8 +1638,6 @@ njt_http_upstream_connect(njt_http_request_t *r, njt_http_upstream_t *u) { njt_int_t rc; njt_connection_t *c; - njt_http_core_loc_conf_t *clcf; - r->connection->log->action = "connecting to upstream"; @@ -1677,22 +1694,24 @@ njt_http_upstream_connect(njt_http_request_t *r, njt_http_upstream_t *u) c = u->peer.connection; c->requests++; +#if (NJT_HTTP_V2) + if (u->h2) { + c->idle = 1; + if (u->peer.cached ) { + if (njt_http_v2_upstream_reuse_connection(r, u, c) != NJT_OK) { + njt_http_upstream_finalize_request(r, u, + NJT_HTTP_INTERNAL_SERVER_ERROR); + } + return; + } + } +#endif c->data = r; c->write->handler = njt_http_upstream_handler; c->read->handler = njt_http_upstream_handler; - u->write_event_handler = njt_http_upstream_send_request_handler; - u->read_event_handler = njt_http_upstream_process_header; - - c->sendfile &= r->connection->sendfile; - u->output.sendfile = c->sendfile; - - if (r->connection->tcp_nopush == NJT_TCP_NOPUSH_DISABLED) { - c->tcp_nopush = NJT_TCP_NOPUSH_DISABLED; - } - if (c->pool == NULL) { /* we need separate pool here to be able to cache SSL connections */ @@ -1710,6 +1729,67 @@ njt_http_upstream_connect(njt_http_request_t *r, njt_http_upstream_t *u) c->read->log = c->log; c->write->log = c->log; + c->sendfile &= r->connection->sendfile; + + if (r->connection->tcp_nopush == NJT_TCP_NOPUSH_DISABLED) { + c->tcp_nopush = NJT_TCP_NOPUSH_DISABLED; + } + + if (njt_http_upstream_configure(r, u, c) != NJT_OK) { + njt_http_upstream_finalize_request(r, u, + NJT_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (rc == NJT_AGAIN) { + // njt_add_timer(c->write, u->conf->connect_timeout); openresty patch + njt_add_timer(c->write, u->connect_timeout); // openresty patch + return; + } + +#if (NJT_HTTP_SSL) + + if (u->ssl && c->ssl == NULL) { + njt_http_upstream_ssl_init_connection(r, u, c); + return; + } + +#endif + +#if (NJT_HTTP_V2) + if (u->h2) { + rc = njt_http_v2_upstream_init_connection(r, u, c); + + if (rc == NJT_DECLINED) { + njt_http_upstream_next(r, u, NJT_HTTP_UPSTREAM_FT_ERROR); + return; + } + + if (rc != NJT_OK) { + njt_http_upstream_finalize_request(r, u, + NJT_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + njt_http_v2_upstream_init(c); + return; + } +#endif + + njt_http_upstream_send_request(r, u, 1); +} + +static njt_int_t +njt_http_upstream_configure(njt_http_request_t *r, njt_http_upstream_t *u, + njt_connection_t *c) +{ + njt_http_core_loc_conf_t *clcf; + + u->write_event_handler = njt_http_upstream_send_request_handler; + u->read_event_handler = njt_http_upstream_process_header; + + u->output.sendfile = c->sendfile; + /* init or reinit the njt_output_chain() and njt_chain_writer() contexts */ clcf = njt_http_get_module_loc_conf(r, njt_http_core_module); @@ -1721,9 +1801,7 @@ njt_http_upstream_connect(njt_http_request_t *r, njt_http_upstream_t *u) if (u->request_sent) { if (njt_http_upstream_reinit(r, u) != NJT_OK) { - njt_http_upstream_finalize_request(r, u, - NJT_HTTP_INTERNAL_SERVER_ERROR); - return; + return NJT_ERROR; } } @@ -1739,9 +1817,7 @@ njt_http_upstream_connect(njt_http_request_t *r, njt_http_upstream_t *u) u->output.free = njt_alloc_chain_link(r->pool); if (u->output.free == NULL) { - njt_http_upstream_finalize_request(r, u, - NJT_HTTP_INTERNAL_SERVER_ERROR); - return; + return NJT_ERROR; } u->output.free->buf = r->request_body->buf; @@ -1757,22 +1833,7 @@ njt_http_upstream_connect(njt_http_request_t *r, njt_http_upstream_t *u) u->request_body_sent = 0; u->request_body_blocked = 0; - if (rc == NJT_AGAIN) { - // njt_add_timer(c->write, u->conf->connect_timeout); openresty patch - njt_add_timer(c->write, u->connect_timeout); // openresty patch - return; - } - -#if (NJT_HTTP_SSL) - - if (u->ssl && c->ssl == NULL) { - njt_http_upstream_ssl_init_connection(r, u, c); - return; - } - -#endif - - njt_http_upstream_send_request(r, u, 1); + return NJT_OK; } @@ -1809,6 +1870,24 @@ njt_http_upstream_ssl_init_connection(njt_http_request_t *r, return; } +#if (NJT_HTTP_V2) + if (u->h2) { + njt_str_t * alpn = &u->conf->alpn; + + if (SSL_set_alpn_protos(c->ssl->connection, (uint8_t *) alpn->data, + alpn->len) + != 0) + { + njt_log_error(NJT_LOG_INFO, c->log, 0, + "http2 SSL_set_alpn_protos() failed"); + njt_http_upstream_finalize_request(r, u, + NJT_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + } +#endif + if (u->conf->ssl_server_name || u->conf->ssl_verify) { if (njt_http_upstream_ssl_name(r, u, c) != NJT_OK) { njt_http_upstream_finalize_request(r, u, @@ -1936,6 +2015,21 @@ njt_http_upstream_ssl_handshake(njt_http_request_t *r, njt_http_upstream_t *u, c->write->handler = njt_http_upstream_handler; c->read->handler = njt_http_upstream_handler; +#if (NJT_HTTP_V2) + if (u->h2 ) { + c->write->handler = njt_http_v2_write_handler; + c->read->handler = njt_http_v2_read_handler; + + rc = njt_http_v2_upstream_init_connection(r, u, c); + + if (rc != NJT_OK) { + goto failed; + } + njt_http_v2_upstream_init(c); + return; + } + +#endif njt_http_upstream_send_request(r, u, 1); return; @@ -1962,6 +2056,16 @@ njt_http_upstream_ssl_save_session(njt_connection_t *c) return; } +#if (NJT_HTTP_V2) + if (c->stream) { + njt_http_v2_connection_t *h2c = c->data; + c = h2c->init_ssl_data; + } +#endif + + if (c->idle) { + return; + } r = c->data; u = r->upstream; @@ -2394,8 +2498,18 @@ njt_http_upstream_send_request_body(njt_http_request_t *r, /* buffered request body */ if (!u->request_sent) { + +#if (NJT_HTTP_V2) + if (u->h2) { + if (njt_http_v2_upstream_send_header(r) != NJT_OK) { + return NJT_ERROR; + } + out = u->request_bufs->next; + } else { + out = u->request_bufs; + } +#endif u->request_sent = 1; - out = u->request_bufs; } else { out = NULL; @@ -2414,10 +2528,23 @@ njt_http_upstream_send_request_body(njt_http_request_t *r, } if (!u->request_sent) { + +#if (NJT_HTTP_V2) + if (u->h2) { + if (njt_http_v2_upstream_send_header(r) != NJT_OK) { + return NJT_ERROR; + } + out = u->request_bufs->next; + } else { + out = u->request_bufs; + } +#endif u->request_sent = 1; - out = u->request_bufs; - - if (r->request_body->bufs) { + if (r->request_body->bufs + #if (NJT_HTTP_V2) + && out + #endif + ) { for (cl = out; cl->next; cl = cl->next) { /* void */ } cl->next = r->request_body->bufs; r->request_body->bufs = NULL; @@ -2501,6 +2628,9 @@ njt_http_upstream_send_request_body(njt_http_request_t *r, njt_http_upstream_send_request_handler(njt_http_request_t *r, njt_http_upstream_t *u) { +#if (NJT_HTTP_V2 || NJT_HTTP_V3) + njt_int_t rc; +#endif njt_connection_t *c; c = u->peer.connection; @@ -2522,6 +2652,17 @@ njt_http_upstream_send_request_handler(njt_http_request_t *r, #endif +#if (NJT_HTTP_V2) + if (u->h2 && !u->h2_init) { + rc = njt_http_v2_upstream_init_connection(r, u, c); + + if (rc != NJT_OK) { + njt_http_upstream_finalize_request(r, u, + NJT_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } +#endif // if (u->header_sent && !u->conf->preserve_output) { // openresty patch if (u->request_body_sent && !u->conf->preserve_output) { // openresty patch u->write_event_handler = njt_http_upstream_dummy_handler; @@ -4641,28 +4782,54 @@ njt_http_upstream_next(njt_http_request_t *r, njt_http_upstream_t *u, } if (u->peer.connection) { - njt_log_debug1(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, - "close http upstream connection: %d", - u->peer.connection->fd); -#if (NJT_HTTP_SSL) + njt_http_upstream_close_peer_connection(r, u, 1); + + } - if (u->peer.connection->ssl) { - u->peer.connection->ssl->no_wait_shutdown = 1; - u->peer.connection->ssl->no_send_shutdown = 1; + njt_http_upstream_connect(r, u); +} - (void) njt_ssl_shutdown(u->peer.connection); +static void +njt_http_upstream_close_peer_connection(njt_http_request_t *r, + njt_http_upstream_t *u, njt_uint_t no_send) +{ + njt_pool_t *pool; + njt_connection_t *c; + + c = u->peer.connection; + + njt_log_debug1(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "close http upstream connection: %d", c->fd); + +#if (NJT_HTTP_V2) + if (u->h2 && u->h2_init) { + if (c->stream) { + njt_http_v2_close_stream(c->stream,0); + } else { + njt_http_v2_finalize_connection(c->data,0); } + return; + } #endif - if (u->peer.connection->pool) { - njt_destroy_pool(u->peer.connection->pool); +#if (NJT_HTTP_SSL) + if (c->ssl) { + c->ssl->no_wait_shutdown = 1; + c->ssl->no_send_shutdown = no_send; + + (void) njt_ssl_shutdown(c); } +#endif + + pool = c->pool; + + njt_close_connection(c); - njt_close_connection(u->peer.connection); - u->peer.connection = NULL; + if (pool) { + njt_destroy_pool(pool); } - njt_http_upstream_connect(r, u); + u->peer.connection = NULL; } @@ -4723,37 +4890,8 @@ njt_http_upstream_finalize_request(njt_http_request_t *r, } if (u->peer.connection) { - -#if (NJT_HTTP_SSL) - - /* TODO: do not shutdown persistent connection */ - - if (u->peer.connection->ssl) { - - /* - * We send the "close notify" shutdown alert to the upstream only - * and do not wait its "close notify" shutdown alert. - * It is acceptable according to the TLS standard. - */ - - u->peer.connection->ssl->no_wait_shutdown = 1; - - (void) njt_ssl_shutdown(u->peer.connection); - } -#endif - - njt_log_debug1(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, - "close http upstream connection: %d", - u->peer.connection->fd); - - if (u->peer.connection->pool) { - njt_destroy_pool(u->peer.connection->pool); - } - - njt_close_connection(u->peer.connection); - } - - u->peer.connection = NULL; + njt_http_upstream_close_peer_connection(r, u, 1); + } if (u->pipe && u->pipe->temp_file) { njt_log_debug1(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -7077,5 +7215,262 @@ njt_http_upstream_init_main_conf(njt_conf_t *cf, void *conf) return NJT_CONF_OK; } +#if (NJT_HTTP_V2) + +static njt_int_t njt_http_v2_upstream_init_connection(njt_http_request_t *r, + njt_http_upstream_t *u, njt_connection_t *c) { + njt_http_connection_t *hc; + njt_uint_t id; + njt_http_v2_node_t *node; + njt_connection_t *fc; + njt_http_v2_stream_t *stream; + njt_http_v2_connection_t *h2c; + njt_http_core_srv_conf_t *cscf; + + njt_log_debug0(NJT_LOG_DEBUG_HTTP, c->log, 0, "upstream init http2 connection"); + + c->log->action = "upstream HTTP/2 connection"; + + cscf = njt_http_get_module_srv_conf(r, njt_http_core_module); + + hc = njt_pcalloc(c->pool, sizeof(njt_http_connection_t)); + if (hc == NULL) { + return NJT_ERROR; + } + + hc->ssl = 1; + c->data = hc; + + /* hc->addr_conf is unused */ + hc->conf_ctx = cscf->ctx; /* needed for streams to get config */ + c->log_error = NJT_ERROR_INFO; + + if (njt_http_v2_create_client(&u->conf->h2_conf, c) != NJT_OK) { + return NJT_ERROR; + } + + h2c = c->data; + ++h2c->last_sid; + id = (h2c->last_sid << 1) -1; + node = njt_http_v2_get_node_by_id(h2c,id,1); + if (node == NULL) { + return NJT_ERROR; + } + node->weight = 100; + + stream = njt_http_v2_create_stream(h2c); + if (stream == NULL) { + return NJT_ERROR; + } + + njt_log_debug2(NJT_LOG_DEBUG_EVENT, c->log, 0, + "http2 upstream stream id:0x%xL create c:%p", id, c); + + fc = stream->fc; + fc->data = r; + fc->read->handler = njt_http_upstream_handler; + fc->write->handler = njt_http_upstream_handler; + + stream->request = r; + stream->connection = h2c; + stream->send_window = h2c->init_window; + stream->recv_window = u->conf->h2_conf.recv_window; + h2c->priority_limit += u->conf->h2_conf.concurrent_streams; + + h2c->state.pool = njt_create_pool(1024, h2c->connection->log); + if (h2c->state.pool == NULL) { + return NJT_ERROR; + } + stream->pool = h2c->state.pool; + stream->node = node; + + u->peer.connection = fc; + u->writer.connection = fc; + //u->stream = stream; + + + h2c->state.stream = stream; + h2c->state.keep_pool = 1; + h2c->init_ssl_data = fc; + + node->stream = stream; + + njt_http_v2_set_dependency(h2c, node, 1, 0); + u->h2_init = 1; + + return NJT_OK; +} +static njt_int_t +njt_http_v2_upstream_init(njt_connection_t *c) { + njt_uint_t i, size; + njt_http_v2_node_t *node; + njt_event_t *ev; + njt_http_v2_stream_t *stream; + njt_http_v2_connection_t *h2c; + njt_connection_t *fc; + njt_http_v2_srv_conf_t *h2scf; + + h2c = c->data; + + if (njt_http_v2_send_preface(h2c) != NJT_OK) { + return NJT_ERROR; + } + + if (njt_http_v2_send_settings(h2c) == NJT_ERROR) { + return NJT_ERROR; + } + + if (njt_http_v2_send_window_update(h2c, 0, NJT_HTTP_V2_MAX_WINDOW + - NJT_HTTP_V2_DEFAULT_WINDOW) + == NJT_ERROR) + { + return NJT_ERROR; + } + + h2scf = njt_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + njt_http_v2_module); + + size = njt_http_v2_index_size(h2scf); + + for (i = 0; i < size; i++) { + + for (node = h2c->streams_index[i]; node; node = node->index) { + stream = node->stream; + + if (stream == NULL) { + continue; + } + + fc = stream->fc; + ev = fc->write; + ev->handler(ev); + } + } + + if (njt_http_v2_send_output_queue(h2c) != NJT_OK) { + return NJT_ERROR; + } + return NJT_OK; +} + + +static njt_int_t +njt_http_v2_upstream_send_header(njt_http_request_t *r) { + njt_http_upstream_t *u; + njt_uint_t fin; + njt_http_v2_stream_t *stream; + njt_http_v2_out_frame_t *frame; + njt_http_v2_connection_t *h2c; + njt_connection_t *fc; + njt_http_cleanup_t *cln; + + u = r->upstream; + fc = u->peer.connection; + stream = fc->stream; + h2c = stream->connection; + + if (u->request_sent) { + return NJT_OK; + } + + fin = stream->in_closed || r->headers_in.content_length_n == 0 || + (r->headers_in.content_length_n < 0 && !r->headers_in.chunked); + njt_buf_t *out = u->request_bufs->buf; + + frame = njt_http_v2_create_headers_frame(stream,out->pos,out->last,fin); + if (frame == NULL) { + return NJT_ERROR; + } + + njt_http_v2_queue_blocked_frame(h2c, frame); + + stream->queued = 1; + + cln = njt_http_cleanup_add(r, 0); + if (cln == NULL) { + return NJT_ERROR; + } + + //cln->handler = njt_http_v2_filter_cleanup; + //cln->data = stream; + fc->need_last_buf = 1; + fc->need_flush_buf = 1; + //u->request_bufs = u->request_bufs->next; + return njt_http_v2_filter_send(fc,stream);; + +} + +static njt_int_t +njt_http_v2_upstream_reuse_connection(njt_http_request_t *r, + njt_http_upstream_t *u, njt_connection_t *c) { + njt_http_v2_node_t *node; + njt_connection_t *fc; + njt_http_v2_stream_t *stream; + njt_http_v2_connection_t *h2c; + njt_uint_t id; + + njt_log_debug1(NJT_LOG_DEBUG_HTTP, c->log, 0, + "http2 upstream reuse connection c:%p", c); + + if (njt_http_upstream_configure(r, u, c) != NJT_OK) { + return NJT_ERROR; + } + + h2c = c->data; + + stream = njt_http_v2_create_stream(h2c); + if (stream == NULL) { + return NJT_ERROR; + } + + fc = stream->fc; + fc->data = r; + fc->read->handler = njt_http_upstream_handler; + fc->write->handler = njt_http_upstream_handler; + + stream->request = r; + stream->connection = h2c; + stream->send_window = h2c->init_window; + stream->recv_window = u->conf->h2_conf.recv_window; + h2c->priority_limit += u->conf->h2_conf.concurrent_streams; + + h2c->state.pool = njt_create_pool(1024, h2c->connection->log); + if (h2c->state.pool == NULL) { + return NJT_ERROR; + } + stream->pool = h2c->state.pool; + + ++h2c->last_sid; + id = (h2c->last_sid << 1) -1; + node = njt_http_v2_get_node_by_id(h2c,id,1); + if (node == NULL) { + return NJT_ERROR; + } + node->weight = 100; + stream->node = node; + + njt_log_debug2(NJT_LOG_DEBUG_EVENT, c->log, 0, + "http2 upstream stream id:0x%xL create c:%p", id, c); + h2c->state.stream = stream; + h2c->state.keep_pool = 1; + h2c->init_ssl_data = fc; + + node->stream = stream; + + njt_http_v2_set_dependency(h2c, node, 1, 0); + + u->peer.connection = fc; + u->writer.connection = fc; + //u->stream = stream; + u->h2_init = 1; + + njt_log_debug1(NJT_LOG_DEBUG_HTTP, fc->log, 0, + "http2 client stream created fc:%p", fc); + + njt_http_upstream_send_request(r, u, 1); + return NJT_OK; +} + +#endif diff --git a/src/http/njt_http_upstream.h b/src/http/njt_http_upstream.h index 82996b54..73f8e139 100644 --- a/src/http/njt_http_upstream.h +++ b/src/http/njt_http_upstream.h @@ -172,6 +172,13 @@ typedef struct { #endif } njt_http_upstream_local_t; +#if (NJT_HTTP_V2) +typedef struct { + njt_uint_t concurrent_streams; + size_t recv_window; + njt_uint_t streams_index_mask; +} njt_http_v2_conf_t; +#endif typedef struct { njt_http_upstream_srv_conf_t *upstream; @@ -283,6 +290,18 @@ typedef struct { njt_str_t module; +#if (NJT_HTTP_V2 || NGX_HTTP_V3) + njt_str_t alpn; +#endif + +#if (NJT_HTTP_V2) + njt_http_v2_conf_t h2_conf; +#endif + +#if (NJT_HTTP_V3) + njt_quic_conf_t quic; +#endif + NJT_COMPAT_BEGIN(2) NJT_COMPAT_END } njt_http_upstream_conf_t; @@ -448,6 +467,15 @@ struct njt_http_upstream_s { unsigned request_body_sent:1; unsigned request_body_blocked:1; unsigned header_sent:1; +#if (NJT_HTTP_V2) + unsigned h2:1; + unsigned h2_init:1; +#endif +#if (NJT_HTTP_V3) + unsigned h3:1; + unsigned h3_started:1; + unsigned hq:1; +#endif }; diff --git a/src/http/v2/njt_http_v2.c b/src/http/v2/njt_http_v2.c index 7ce890eb..9a2f671c 100755 --- a/src/http/v2/njt_http_v2.c +++ b/src/http/v2/njt_http_v2.c @@ -3,6 +3,7 @@ * Copyright (C) Nginx, Inc. * Copyright (C) 2021-2023 TMLake(Beijing) Technology Co., Ltd. * Copyright (C) Valentin V. Bartenev + * Copyright (C) 2024 JD Technology Information Technology Co., Ltd. */ @@ -49,9 +50,6 @@ #define NJT_HTTP_V2_ROOT (void *) -1 - -static void njt_http_v2_read_handler(njt_event_t *rev); -static void njt_http_v2_write_handler(njt_event_t *wev); static void njt_http_v2_handle_connection(njt_http_v2_connection_t *h2c); static void njt_http_v2_lingering_close(njt_connection_t *c); static void njt_http_v2_lingering_close_handler(njt_event_t *rev); @@ -68,8 +66,6 @@ static u_char *njt_http_v2_state_read_data(njt_http_v2_connection_t *h2c, u_char *pos, u_char *end); static u_char *njt_http_v2_state_headers(njt_http_v2_connection_t *h2c, u_char *pos, u_char *end); -static u_char *njt_http_v2_state_header_block(njt_http_v2_connection_t *h2c, - u_char *pos, u_char *end); static u_char *njt_http_v2_state_field_len(njt_http_v2_connection_t *h2c, u_char *pos, u_char *end); static u_char *njt_http_v2_state_field_huff(njt_http_v2_connection_t *h2c, @@ -118,20 +114,10 @@ static u_char *njt_http_v2_connection_error(njt_http_v2_connection_t *h2c, static njt_int_t njt_http_v2_parse_int(njt_http_v2_connection_t *h2c, u_char **pos, u_char *end, njt_uint_t prefix); -static njt_http_v2_stream_t *njt_http_v2_create_stream( - njt_http_v2_connection_t *h2c); -static njt_http_v2_node_t *njt_http_v2_get_node_by_id( - njt_http_v2_connection_t *h2c, njt_uint_t sid, njt_uint_t alloc); static njt_http_v2_node_t *njt_http_v2_get_closed_node( njt_http_v2_connection_t *h2c); -#define njt_http_v2_index_size(h2scf) (h2scf->streams_index_mask + 1) -#define njt_http_v2_index(h2scf, sid) ((sid >> 1) & h2scf->streams_index_mask) - -static njt_int_t njt_http_v2_send_settings(njt_http_v2_connection_t *h2c); static njt_int_t njt_http_v2_settings_frame_handler( njt_http_v2_connection_t *h2c, njt_http_v2_out_frame_t *frame); -static njt_int_t njt_http_v2_send_window_update(njt_http_v2_connection_t *h2c, - njt_uint_t sid, size_t window); static njt_int_t njt_http_v2_send_rst_stream(njt_http_v2_connection_t *h2c, njt_uint_t sid, njt_uint_t status); static njt_int_t njt_http_v2_send_goaway(njt_http_v2_connection_t *h2c, @@ -171,17 +157,18 @@ static void njt_http_v2_close_stream_handler(njt_event_t *ev); static void njt_http_v2_retry_close_stream_handler(njt_event_t *ev); static void njt_http_v2_handle_connection_handler(njt_event_t *rev); static void njt_http_v2_idle_handler(njt_event_t *rev); -static void njt_http_v2_finalize_connection(njt_http_v2_connection_t *h2c, - njt_uint_t status); static njt_int_t njt_http_v2_adjust_windows(njt_http_v2_connection_t *h2c, ssize_t delta); -static void njt_http_v2_set_dependency(njt_http_v2_connection_t *h2c, - njt_http_v2_node_t *node, njt_uint_t depend, njt_uint_t exclusive); static void njt_http_v2_node_children_update(njt_http_v2_node_t *node); static void njt_http_v2_pool_cleanup(void *data); - +njt_chain_t *njt_http_v2_send_chain(njt_connection_t *fc, + njt_chain_t *in, off_t limit); +njt_int_t njt_http_v2_filter_send(njt_connection_t *fc, + njt_http_v2_stream_t *stream); +static njt_int_t njt_http_v2_upstream_process_header(njt_http_request_t *r, + njt_str_t *name, njt_str_t *value); static njt_http_v2_handler_pt njt_http_v2_frame_states[] = { njt_http_v2_state_data, /* NJT_HTTP_V2_DATA_FRAME */ @@ -326,7 +313,7 @@ njt_http_v2_init(njt_event_t *rev) } -static void +void njt_http_v2_read_handler(njt_event_t *rev) { u_char *p, *end; @@ -450,7 +437,7 @@ njt_http_v2_read_handler(njt_event_t *rev) } -static void +void njt_http_v2_write_handler(njt_event_t *wev) { njt_int_t rc; @@ -659,8 +646,8 @@ njt_http_v2_handle_connection(njt_http_v2_connection_t *h2c) clcf = njt_http_get_module_loc_conf(h2c->http_connection->conf_ctx, njt_http_core_module); - - if (!c->read->timer_set) { + //upstream connection disable keepalive + if (!c->read->timer_set && !h2c->client) { njt_add_timer(c->read, clcf->keepalive_timeout); } @@ -671,6 +658,9 @@ njt_http_v2_handle_connection(njt_http_v2_connection_t *h2c) } njt_destroy_pool(h2c->pool); + c->log = njt_cycle->log; + c->write->log = njt_cycle->log; + c->read->log = njt_cycle->log; h2c->pool = NULL; h2c->free_frames = NULL; @@ -691,6 +681,10 @@ njt_http_v2_handle_connection(njt_http_v2_connection_t *h2c) if (c->write->timer_set) { njt_del_timer(c->write); } + //upstream connection 用完即关 + if (h2c->client) { + njt_http_v2_finalize_connection(h2c, NJT_HTTP_V2_NO_ERROR); + } } @@ -1077,20 +1071,22 @@ njt_http_v2_state_read_data(njt_http_v2_connection_t *h2c, u_char *pos, } r = stream->request; - fc = r->connection; + fc = stream->fc; - if (r->reading_body && !r->request_body_no_buffering) { - njt_log_debug0(NJT_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "skipping http2 DATA frame"); + if (!h2c->client) { + if (r->reading_body && !r->request_body_no_buffering) { + njt_log_debug0(NJT_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "skipping http2 DATA frame"); - return njt_http_v2_state_skip_padded(h2c, pos, end); - } + return njt_http_v2_state_skip_padded(h2c, pos, end); + } - if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { - njt_log_debug0(NJT_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "skipping http2 DATA frame"); + if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { + njt_log_debug0(NJT_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "skipping http2 DATA frame"); - return njt_http_v2_state_skip_padded(h2c, pos, end); + return njt_http_v2_state_skip_padded(h2c, pos, end); + } } size = end - pos; @@ -1102,42 +1098,54 @@ njt_http_v2_state_read_data(njt_http_v2_connection_t *h2c, u_char *pos, h2c->payload_bytes += size; - if (r->request_body) { - rc = njt_http_v2_process_request_body(r, pos, size, - stream->in_closed, 0); - - if (rc != NJT_OK && rc != NJT_AGAIN) { - stream->skip_data = 1; - njt_http_finalize_request(r, rc); + if (h2c->client) { + njt_log_debug2(NJT_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 write buffer sid:%ui size:%ui",h2c->state.sid, size); + if (size > 0) { + njt_http_v2_write_buffer(fc, pos, size); } + /* end stream*/ + fc->read->ready = 1; + njt_post_event(fc->read,&njt_posted_events); - njt_http_run_posted_requests(fc); + } else { + if (r->request_body) { + rc = njt_http_v2_process_request_body(r, pos, size, + stream->in_closed, 0); - } else if (size) { - buf = stream->preread; + if (rc != NJT_OK && rc != NJT_AGAIN) { + stream->skip_data = 1; + njt_http_finalize_request(r, rc); + } - if (buf == NULL) { - h2scf = njt_http_get_module_srv_conf(r, njt_http_v2_module); + njt_http_run_posted_requests(fc); + + } else if (size) { + buf = stream->preread; - buf = njt_create_temp_buf(r->pool, h2scf->preread_size); if (buf == NULL) { + h2scf = njt_http_get_module_srv_conf(r, njt_http_v2_module); + + buf = njt_create_temp_buf(r->pool, h2scf->preread_size); + if (buf == NULL) { + return njt_http_v2_connection_error(h2c, + NJT_HTTP_V2_INTERNAL_ERROR); + } + + stream->preread = buf; + } + + if (size > (size_t) (buf->end - buf->last)) { + njt_log_error(NJT_LOG_ALERT, h2c->connection->log, 0, + "http2 preread buffer overflow"); return njt_http_v2_connection_error(h2c, NJT_HTTP_V2_INTERNAL_ERROR); } - stream->preread = buf; + buf->last = njt_cpymem(buf->last, pos, size); } - - if (size > (size_t) (buf->end - buf->last)) { - njt_log_error(NJT_LOG_ALERT, h2c->connection->log, 0, - "http2 preread buffer overflow"); - return njt_http_v2_connection_error(h2c, - NJT_HTTP_V2_INTERNAL_ERROR); - } - - buf->last = njt_cpymem(buf->last, pos, size); } - + pos += size; h2c->state.length -= size; @@ -1259,23 +1267,37 @@ njt_http_v2_state_headers(njt_http_v2_connection_t *h2c, u_char *pos, return njt_http_v2_connection_error(h2c, NJT_HTTP_V2_PROTOCOL_ERROR); } - - h2c->last_sid = h2c->state.sid; - + + if (!h2c->client) { h2c->state.pool = njt_create_pool(1024, h2c->connection->log); if (h2c->state.pool == NULL) { return njt_http_v2_connection_error(h2c, NJT_HTTP_V2_INTERNAL_ERROR); } + } + + h2scf = njt_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + njt_http_v2_module); + + if (!h2c->settings_ack + && !(h2c->state.flags & NJT_HTTP_V2_END_STREAM_FLAG) + && h2scf->preread_size < NJT_HTTP_V2_DEFAULT_WINDOW) + { + njt_log_error(NJT_LOG_INFO, h2c->connection->log, 0, + "client sent stream with data " + "before settings were acknowledged"); + + status = NJT_HTTP_V2_REFUSED_STREAM; + goto rst_stream; + } cscf = njt_http_get_module_srv_conf(h2c->http_connection->conf_ctx, njt_http_core_module); h2c->state.header_limit = cscf->large_client_header_buffers.size * cscf->large_client_header_buffers.num; - - h2scf = njt_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - njt_http_v2_module); - + if (!h2c->client) { + + h2c->last_sid = h2c->state.sid; if (h2c->processing >= h2scf->concurrent_streams) { njt_log_error(NJT_LOG_INFO, h2c->connection->log, 0, "concurrent streams exceeded %ui", h2c->processing); @@ -1292,18 +1314,6 @@ njt_http_v2_state_headers(njt_http_v2_connection_t *h2c, u_char *pos, goto rst_stream; } - if (!h2c->settings_ack - && !(h2c->state.flags & NJT_HTTP_V2_END_STREAM_FLAG) - && h2scf->preread_size < NJT_HTTP_V2_DEFAULT_WINDOW) - { - njt_log_error(NJT_LOG_INFO, h2c->connection->log, 0, - "client sent stream with data " - "before settings were acknowledged"); - - status = NJT_HTTP_V2_REFUSED_STREAM; - goto rst_stream; - } - node = njt_http_v2_get_node_by_id(h2c, h2c->state.sid, 1); if (node == NULL) { @@ -1326,6 +1336,9 @@ njt_http_v2_state_headers(njt_http_v2_connection_t *h2c, u_char *pos, h2c->state.keep_pool = 1; stream->request->request_length = h2c->state.length; + stream->send_window = h2c->init_window; + stream->recv_window = h2scf->preread_size; + h2c->priority_limit += h2scf->concurrent_streams; stream->in_closed = h2c->state.flags & NJT_HTTP_V2_END_STREAM_FLAG; stream->node = node; @@ -1336,6 +1349,25 @@ njt_http_v2_state_headers(njt_http_v2_connection_t *h2c, u_char *pos, node->weight = weight; njt_http_v2_set_dependency(h2c, node, depend, excl); } + } else { + node = njt_http_v2_get_node_by_id(h2c, h2c->state.sid, 0); + + if (node == NULL) { + return njt_http_v2_connection_error(h2c, NJT_HTTP_V2_INTERNAL_ERROR); + } + stream = node->stream; + h2c->state.stream = stream; + h2c->state.keep_pool = 1; + + stream->state->flags = h2c->state.flags & NJT_HTTP_V2_END_HEADERS_FLAG; + stream->state->length = h2c->state.length; + stream->state->handler = njt_http_v2_state_header_block; + + stream->state->stream = stream; + stream->state->pool = stream->pool; + stream->state->keep_pool = 1; + stream->state->header_limit = h2c->state.header_limit; + } clcf = njt_http_get_module_loc_conf(h2c->http_connection->conf_ctx, njt_http_core_module); @@ -1371,13 +1403,14 @@ rst_stream: } -static u_char * +u_char * njt_http_v2_state_header_block(njt_http_v2_connection_t *h2c, u_char *pos, u_char *end) { u_char ch; njt_int_t value; njt_uint_t indexed, size_update, prefix; + njt_connection_t *fc; if (end - pos < 1) { return njt_http_v2_state_headers_save(h2c, pos, end, @@ -1391,6 +1424,19 @@ njt_http_v2_state_header_block(njt_http_v2_connection_t *h2c, u_char *pos, njt_http_v2_state_header_block); } + /*capture http2 header*/ + if (h2c->client && !h2c->state.parse) { + fc = h2c->state.stream->fc; + + size_t len = njt_min(h2c->state.length, (size_t) (end - pos)); + fc->read->ready = 1; + njt_http_v2_write_buffer(fc, pos, len); + njt_post_event(fc->read,&njt_posted_events); + h2c->state.length -= len; + pos += len; + + return njt_http_v2_state_header_complete(h2c, pos, end); + } size_update = 0; indexed = 0; @@ -1707,7 +1753,7 @@ njt_http_v2_state_process_header(njt_http_v2_connection_t *h2c, u_char *pos, size_t len; njt_int_t rc; njt_table_elt_t *h; - njt_connection_t *fc; + njt_connection_t *c; njt_http_header_t *hh; njt_http_request_t *r; njt_http_v2_header_t *header; @@ -1766,9 +1812,21 @@ njt_http_v2_state_process_header(njt_http_v2_connection_t *h2c, u_char *pos, return njt_http_v2_state_header_complete(h2c, pos, end); } - r = h2c->state.stream->request; - fc = r->connection; + r = h2c->state.stream->request; + + if (h2c->client) { + if (njt_http_v2_upstream_process_header(r, + &header->name, &header->value) != NJT_OK) { + + njt_http_v2_connection_error(h2c, NJT_HTTP_V2_INTERNAL_ERROR); + njt_http_finalize_request(r, NJT_HTTP_BAD_REQUEST); + h2c->state.stream = NULL; + return njt_http_v2_state_header_complete(h2c, pos, end); + } + return njt_http_v2_state_header_complete(h2c, pos, end); + } + c = r->connection; /* TODO Optimization: validate headers while parsing. */ if (njt_http_v2_validate_header(r, header) != NJT_OK) { njt_http_finalize_request(r, NJT_HTTP_BAD_REQUEST); @@ -1858,8 +1916,7 @@ error: h2c->state.stream = NULL; - njt_http_run_posted_requests(fc); - + njt_http_run_posted_requests(c); return njt_http_v2_state_header_complete(h2c, pos, end); } @@ -1887,7 +1944,7 @@ njt_http_v2_state_header_complete(njt_http_v2_connection_t *h2c, u_char *pos, stream = h2c->state.stream; - if (stream) { + if (stream && !h2c->client) { njt_http_v2_run_request(stream->request); } @@ -1913,6 +1970,7 @@ njt_http_v2_handle_continuation(njt_http_v2_connection_t *h2c, u_char *pos, u_char *p; size_t len, skip; uint32_t head; + njt_http_v2_stream_t *stream; len = h2c->state.length; @@ -1959,11 +2017,18 @@ njt_http_v2_handle_continuation(njt_http_v2_connection_t *h2c, u_char *pos, h2c->state.length += len; - if (h2c->state.stream) { + if (h2c->state.stream && !h2c->client && !h2c->fake ) { h2c->state.stream->request->request_length += len; } h2c->state.handler = handler; + + /*capture http2 continuation header*/ + if (h2c->client && !h2c->state.parse) { + stream = h2c->state.stream; + stream->state->flags = h2c->state.flags & NJT_HTTP_V2_END_HEADERS_FLAG; + stream->state->length =+ len; + } return pos; } @@ -2100,7 +2165,7 @@ njt_http_v2_state_rst_stream(njt_http_v2_connection_t *h2c, u_char *pos, stream->in_closed = 1; stream->out_closed = 1; - fc = stream->request->connection; + fc = stream->fc; fc->error = 1; switch (status) { @@ -2124,7 +2189,8 @@ njt_http_v2_state_rst_stream(njt_http_v2_connection_t *h2c, u_char *pos, } ev = fc->read; - ev->handler(ev); + //ev->handler(ev); + njt_post_event(ev, &njt_posted_next_events); return njt_http_v2_state_complete(h2c, pos, end); } @@ -2454,7 +2520,7 @@ njt_http_v2_state_window_update(njt_http_v2_connection_t *h2c, u_char *pos, if (stream->exhausted) { stream->exhausted = 0; - wev = stream->request->connection->write; + wev = stream->fc->write; wev->active = 0; wev->ready = 1; @@ -2489,7 +2555,7 @@ njt_http_v2_state_window_update(njt_http_v2_connection_t *h2c, u_char *pos, stream->waiting = 0; - wev = stream->request->connection->write; + wev = stream->fc->write; wev->active = 0; wev->ready = 1; @@ -2609,9 +2675,9 @@ njt_http_v2_state_headers_save(njt_http_v2_connection_t *h2c, u_char *pos, njt_http_request_t *r; njt_http_core_srv_conf_t *cscf; - if (h2c->state.stream) { + if (h2c->state.stream && !h2c->fake) { r = h2c->state.stream->request; - rev = r->connection->read; + rev = h2c->state.stream->fc->read; if (!rev->timer_set) { cscf = njt_http_get_module_srv_conf(r, njt_http_core_module); @@ -2630,6 +2696,9 @@ njt_http_v2_connection_error(njt_http_v2_connection_t *h2c, njt_log_debug0(NJT_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 state connection error"); + if (h2c->fake) { + return NULL; + } njt_http_v2_finalize_connection(h2c, err); return NULL; @@ -2692,7 +2761,7 @@ njt_http_v2_parse_int(njt_http_v2_connection_t *h2c, u_char **pos, u_char *end, } -static njt_int_t +njt_int_t njt_http_v2_send_settings(njt_http_v2_connection_t *h2c) { size_t len; @@ -2783,7 +2852,7 @@ njt_http_v2_settings_frame_handler(njt_http_v2_connection_t *h2c, } -static njt_int_t +njt_int_t njt_http_v2_send_window_update(njt_http_v2_connection_t *h2c, njt_uint_t sid, size_t window) { @@ -2962,7 +3031,7 @@ njt_http_v2_frame_handler(njt_http_v2_connection_t *h2c, } -static njt_http_v2_stream_t * +njt_http_v2_stream_t * njt_http_v2_create_stream(njt_http_v2_connection_t *h2c) { njt_log_t *log; @@ -2971,10 +3040,9 @@ njt_http_v2_create_stream(njt_http_v2_connection_t *h2c) njt_http_log_ctx_t *ctx; njt_http_request_t *r; njt_http_v2_stream_t *stream; - njt_http_v2_srv_conf_t *h2scf; - njt_http_core_srv_conf_t *cscf; + njt_http_core_srv_conf_t *cscf; - fc = h2c->free_fake_connections; + fc = h2c->free_fake_connections; if (fc) { h2c->free_fake_connections = fc->data; @@ -3018,7 +3086,10 @@ njt_http_v2_create_stream(njt_http_v2_connection_t *h2c) njt_memcpy(log, h2c->connection->log, sizeof(njt_log_t)); log->data = ctx; - log->action = "reading client request headers"; + log->action = "http2 stream request headers"; + + /*distinguish physical connections in logs*/ + log->connection = log->connection + 1000; njt_memzero(rev, sizeof(njt_event_t)); @@ -3026,10 +3097,14 @@ njt_http_v2_create_stream(njt_http_v2_connection_t *h2c) rev->ready = 1; rev->handler = njt_http_v2_close_stream_handler; rev->log = log; + rev->active = 1; njt_memcpy(wev, rev, sizeof(njt_event_t)); + wev->data = fc; + wev->log = log; wev->write = 1; + wev->active = 1; njt_memcpy(fc, h2c->connection, sizeof(njt_connection_t)); @@ -3042,6 +3117,11 @@ njt_http_v2_create_stream(njt_http_v2_connection_t *h2c) fc->sndlowat = 1; fc->tcp_nodelay = NJT_TCP_NODELAY_DISABLED; + stream = njt_pcalloc(h2c->pool, sizeof(njt_http_v2_stream_t)); + if (stream == NULL) { + return NULL; + } + if (!h2c->client) { r = njt_http_create_request(fc); if (r == NULL) { return NULL; @@ -3073,26 +3153,32 @@ njt_http_v2_create_stream(njt_http_v2_connection_t *h2c) } r->headers_in.connection_type = NJT_HTTP_CONNECTION_CLOSE; - - stream = njt_pcalloc(r->pool, sizeof(njt_http_v2_stream_t)); - if (stream == NULL) { - njt_http_free_request(r, NJT_HTTP_INTERNAL_SERVER_ERROR); + r->stream = stream; + stream->request = r; + } else { + stream->state = njt_pcalloc(h2c->pool, sizeof(njt_http_v2_state_t)); + if (stream->state == NULL) { return NULL; } + fc->log = njt_cycle->log; + fc->write->log = njt_cycle->log; + fc->read->log = njt_cycle->log; - r->stream = stream; - - stream->request = r; - stream->connection = h2c; - - h2scf = njt_http_get_module_srv_conf(r, njt_http_v2_module); - - stream->send_window = h2c->init_window; - stream->recv_window = h2scf->preread_size; - - h2c->processing++; + rev->ready = 0; + rev->active = 1; + wev->active = 1; + + fc->send = njt_http_v2_stream_send; + fc->recv = njt_http_v2_stream_recv; + fc->send_chain = njt_http_v2_send_chain; + fc->recv_chain = njt_http_v2_recv_chain; + } + stream->recv.last_chain = &stream->recv.chain; + fc->stream = stream; + stream->fc = fc; + stream->connection = h2c; - h2c->priority_limit += h2scf->concurrent_streams; + h2c->processing++; if (h2c->connection->read->timer_set) { njt_del_timer(h2c->connection->read); @@ -3102,7 +3188,7 @@ njt_http_v2_create_stream(njt_http_v2_connection_t *h2c) } -static njt_http_v2_node_t * +njt_http_v2_node_t * njt_http_v2_get_node_by_id(njt_http_v2_connection_t *h2c, njt_uint_t sid, njt_uint_t alloc) { @@ -4379,7 +4465,7 @@ njt_http_v2_terminate_stream(njt_http_v2_connection_t *h2c, stream->rst_sent = 1; stream->skip_data = 1; - fc = stream->request->connection; + fc = stream->fc; fc->error = 1; rev = fc->read; @@ -4405,7 +4491,7 @@ njt_http_v2_close_stream(njt_http_v2_stream_t *stream, njt_int_t rc) "http2 close stream %ui, queued %ui, processing %ui", node->id, stream->queued, h2c->processing); - fc = stream->request->connection; + fc = stream->fc; if (stream->queued) { fc->error = 1; @@ -4454,14 +4540,20 @@ njt_http_v2_close_stream(njt_http_v2_stream_t *stream, njt_int_t rc) h2c->frames -= stream->frames; - njt_http_free_request(stream->request, rc); - - if (pool != h2c->state.pool) { - njt_destroy_pool(pool); + if (!h2c->client) { + njt_http_free_request(stream->request, rc); + if (pool != h2c->state.pool) { + njt_destroy_pool(pool); + } else { + /* pool will be destroyed when the complete header is parsed */ + h2c->state.keep_pool = 0; + } } else { - /* pool will be destroyed when the complete header is parsed */ - h2c->state.keep_pool = 0; + if (pool == h2c->state.pool) { + h2c->state.pool = NULL; + } + njt_destroy_pool(pool); } ev = fc->read; @@ -4633,7 +4725,7 @@ njt_http_v2_idle_handler(njt_event_t *rev) njt_http_v2_finalize_connection(h2c, NJT_HTTP_V2_INTERNAL_ERROR); return; } - + njt_log_debug1(NJT_LOG_DEBUG_HTTP, c->log, 0, "http2 flood fd:%d",c->fd); c->write->handler = njt_http_v2_write_handler; rev->handler = njt_http_v2_read_handler; @@ -4641,14 +4733,13 @@ njt_http_v2_idle_handler(njt_event_t *rev) } -static void +void njt_http_v2_finalize_connection(njt_http_v2_connection_t *h2c, njt_uint_t status) { njt_uint_t i, size; njt_event_t *ev; - njt_connection_t *c, *fc; - njt_http_request_t *r; + njt_connection_t *c, *fc; njt_http_v2_node_t *node; njt_http_v2_stream_t *stream; njt_http_v2_srv_conf_t *h2scf; @@ -4665,6 +4756,8 @@ njt_http_v2_finalize_connection(njt_http_v2_connection_t *h2c, } } + njt_log_debug2(NJT_LOG_DEBUG_HTTP, c->log, 0, "http2 finalize connection fd:%d, processing:%d",c->fd, h2c->processing); + if (!h2c->processing) { goto done; } @@ -4690,9 +4783,7 @@ njt_http_v2_finalize_connection(njt_http_v2_connection_t *h2c, stream->waiting = 0; - r = stream->request; - fc = r->connection; - + fc = stream->fc; fc->error = 1; if (stream->queued) { @@ -4724,8 +4815,12 @@ done: njt_http_close_connection(c); return; } - - njt_http_v2_lingering_close(c); + if (!h2c->client) { + njt_http_v2_lingering_close(c); + } else { + njt_http_close_connection(c); + } + } @@ -4775,7 +4870,7 @@ njt_http_v2_adjust_windows(njt_http_v2_connection_t *h2c, ssize_t delta) if (stream->send_window > 0 && stream->exhausted) { stream->exhausted = 0; - wev = stream->request->connection->write; + wev = stream->fc->write; wev->active = 0; wev->ready = 1; @@ -4791,7 +4886,7 @@ njt_http_v2_adjust_windows(njt_http_v2_connection_t *h2c, ssize_t delta) } -static void +void njt_http_v2_set_dependency(njt_http_v2_connection_t *h2c, njt_http_v2_node_t *node, njt_uint_t depend, njt_uint_t exclusive) { @@ -4915,3 +5010,339 @@ njt_http_v2_pool_cleanup(void *data) njt_destroy_pool(h2c->pool); } } + +njt_int_t +njt_http_v2_create_client(njt_http_v2_conf_t *conf, njt_connection_t *c) +{ + njt_http_v2_main_conf_t *h2mcf; + njt_http_v2_srv_conf_t *h2scf; + njt_http_connection_t *hc; + njt_http_v2_connection_t *h2c; + njt_pool_cleanup_t *cln; + njt_uint_t id; + njt_http_v2_node_t *node; + njt_log_t *log; + + hc = c->data; + + h2mcf = njt_http_get_module_main_conf(hc->conf_ctx, njt_http_v2_module); + + if (h2mcf->recv_buffer == NULL) { + h2mcf->recv_buffer = njt_palloc(njt_cycle->pool, + h2mcf->recv_buffer_size); + if (h2mcf->recv_buffer == NULL) { + return NJT_ERROR; + } + } + + h2c = njt_pcalloc(c->pool, sizeof(njt_http_v2_connection_t)); + if (h2c == NULL) { + return NJT_ERROR; + } + + h2c->connection = c; + h2c->http_connection = hc; + c->data = h2c; + + h2c->send_window = NJT_HTTP_V2_DEFAULT_WINDOW; + h2c->recv_window = NJT_HTTP_V2_MAX_WINDOW; + + h2c->init_window = NJT_HTTP_V2_DEFAULT_WINDOW; + + h2c->frame_size = NJT_HTTP_V2_DEFAULT_FRAME_SIZE; + + h2scf = njt_http_get_module_srv_conf(hc->conf_ctx, njt_http_v2_module); + + h2c->priority_limit = njt_max(conf->concurrent_streams, 100); + + //h2c->state.handler = njt_http_v2_upstream_state_handler; + h2c->state.handler = njt_http_v2_state_head; + njt_queue_init(&h2c->waiting); + njt_queue_init(&h2c->dependencies); + njt_queue_init(&h2c->closed); + + h2c->client = 1; + h2c->pool = njt_create_pool(h2scf->pool_size, h2c->connection->log); + if (h2c->pool == NULL) { + return NJT_ERROR; + } + + cln = njt_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + return NJT_ERROR; + } + + cln->handler = njt_http_v2_pool_cleanup; + cln->data = h2c; + + log = njt_palloc(h2c->pool, sizeof(njt_log_t)); + if (log == NULL) { + return NJT_ERROR; + } + + *log = *njt_cycle->log; + log->connection = c->number; + + c->log = log; + c->read->log = log; + c->write->log = log; + c->pool->log = log; + h2c->pool->log = log; + + h2c->streams_index = njt_pcalloc(c->pool, njt_http_v2_index_size(h2scf) + * sizeof(njt_http_v2_node_t *)); + if (h2c->streams_index == NULL) { + return NJT_ERROR; + } + --h2c->priority_limit; + + h2c->last_sid++; + id = (h2c->last_sid << 1) -1; + node = njt_http_v2_get_node_by_id(h2c,id,1); + if (node == NULL) { + return NJT_ERROR; + } + node->weight = 200; + h2c->closed_nodes++; + njt_queue_insert_tail(&h2c->closed,&node->reuse); + njt_http_v2_set_dependency(h2c, node, 0, 0); + + return NJT_OK; +} + +njt_int_t +njt_http_v2_send_preface(njt_http_v2_connection_t *h2c) { + size_t len; + njt_buf_t *buf; + njt_chain_t *cl; + njt_http_v2_out_frame_t *frame; + + njt_log_debug0(NJT_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send PREFACE frame"); + + frame = njt_palloc(h2c->pool, sizeof(njt_http_v2_out_frame_t)); + if (frame == NULL) { + return NJT_ERROR; + } + + cl = njt_alloc_chain_link(h2c->pool); + if (cl == NULL) { + return NJT_ERROR; + } + + len = sizeof(NJT_HTTP_V2_PREFACE) - 1; + + buf = njt_create_temp_buf(h2c->pool, len); + if (buf == NULL) { + return NJT_ERROR; + } + + //buf->last_buf = 1; + + cl->buf = buf; + cl->next = NULL; + + frame->first = cl; + frame->last = cl; + frame->handler = njt_http_v2_settings_frame_handler; + frame->stream = NULL; +#if (NJT_DEBUG) + frame->length = len; +#endif + frame->blocked = 1; + + buf->last = njt_cpymem(buf->last, NJT_HTTP_V2_PREFACE, len); + + njt_http_v2_queue_blocked_frame(h2c, frame); + + return NJT_OK; +} + + +/*static u_char * +njt_http_v2_upstream_state_handler(njt_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + njt_log_debug1(NJT_LOG_DEBUG_CORE, h2c->connection->log, 0, + "njt_http_v2_upstream_state_handler size: %d", end-pos); + return end; +}*/ + +static njt_int_t +njt_http_v2_upstream_process_pseudo_header(njt_http_request_t *r, njt_str_t *name, + njt_str_t *value) +{ + njt_int_t status; + njt_str_t *status_line; + njt_http_upstream_t *u; + + /* based on njt_http_v2_process_pseudo_header() */ + + /* + * RFC 9114, 4.3.2 + * + * For responses, a single ":status" pseudo-header field + * is defined that carries the HTTP status code; + */ + + u = r->upstream; + + if (name->len == 7 && njt_strncmp(name->data, ":status", 7) == 0) { + + if (u->state && u->state->status +#if (NJT_HTTP_CACHE) + && !r->cached +#endif + ) { + njt_log_error(NJT_LOG_INFO, r->connection->log, 0, + "upstream sent duplicate \":status\" header"); + return NJT_ERROR; + } + + if (value->len == 0) { + njt_log_error(NJT_LOG_INFO, r->connection->log, 0, + "upstream sent empty \":status\" header"); + return NJT_ERROR; + } + + if (value->len < 3) { + njt_log_error(NJT_LOG_INFO, r->connection->log, 0, + "upstream sent too short \":status\" header"); + return NJT_ERROR; + } + + status = njt_atoi(value->data, 3); + + if (status == NJT_ERROR) { + njt_log_error(NJT_LOG_ERR, r->connection->log, 0, + "upstream sent invalid status \"%V\"", value); + return NJT_ERROR; + } + + if (u->state && u->state->status == 0) { + u->state->status = status; + } + + u->headers_in.status_n = status; + + status_line = njt_http_status_line(status); + if (status_line) { + u->headers_in.status_line = *status_line; + } + + njt_log_debug2(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "http v2 proxy status %ui \"%V\"", + u->headers_in.status_n, &u->headers_in.status_line); + + return NJT_OK; + } + + njt_log_error(NJT_LOG_INFO, r->connection->log, 0, + "upstream sent unexpected pseudo-header \"%V\"", name); + + return NJT_ERROR; +} + +static njt_int_t +njt_http_v2_upstream_process_header(njt_http_request_t *r,njt_str_t *name, + njt_str_t *value) +{ + njt_table_elt_t *h; + njt_http_upstream_t *u; + njt_http_upstream_header_t *hh; + njt_http_upstream_main_conf_t *umcf; + + /* based on njt_http_v2_process_header() */ + + umcf = njt_http_get_module_main_conf(r, njt_http_upstream_module); + u = r->upstream; + + if (name->len && name->data[0] == ':') { + return njt_http_v2_upstream_process_pseudo_header(r, name, value); + } + + h = njt_list_push(&u->headers_in.headers); + if (h == NULL) { + return NJT_ERROR; + } + + /* + * HTTP/2 parsing used peer->connection.pool, which might be destroyed, + * at the moment when r->headers_out are used; + * thus allocate from r->pool and copy header name/value + */ + h->key.len = name->len; + h->key.data = njt_pnalloc(r->pool, name->len + 1); + if (h->key.data == NULL) { + return NJT_ERROR; + } + njt_memcpy(h->key.data, name->data, name->len); + h->key.data[h->key.len] = 0; + + h->value.len = value->len; + h->value.data = njt_pnalloc(r->pool, value->len + 1); + if (h->value.data == NULL) { + return NJT_ERROR; + } + njt_memcpy(h->value.data, value->data, value->len); + h->value.data[h->value.len] = 0; + + h->lowcase_key = h->key.data; + h->hash = njt_hash_key(h->key.data, h->key.len); + + hh = njt_hash_find(&umcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh && hh->handler(r, h, hh->offset) != NJT_OK) { + return NJT_ERROR; + } + + njt_log_debug2(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 header: \"%V: %V\"", name, value); + return NJT_OK; +} + +njt_int_t +njt_http_v2_parse_headers(njt_http_v2_connection_t *ph2, njt_buf_t *b) +{ + u_char *p, *end; + + ph2->state.buffer_used = 0; + ph2->state.incomplete = 0; + + p = b->pos; + end = b->last; + + do { + p = ph2->state.handler(ph2, p, end); + + if (p == NULL) { + njt_log_debug3(NJT_LOG_INFO, ph2->connection->log, 0, + "njt_http_v2_parse_headers pos:%p, end:%p, current:%p", + b->pos, end, p); + return NJT_ERROR; + } + if (ph2->state.length == 0 && + ph2->state.flags & NJT_HTTP_V2_END_HEADERS_FLAG) { + ph2->state.handler = njt_http_v2_state_header_block; + //h2c->state.stream = stream; + break; + } + + } while (p != end); + + if (ph2->state.incomplete) { + b->pos = p - ph2->state.buffer_used; + ph2->state.incomplete = 0; + ph2->state.buffer_used = 0; + } else { + b->pos = p; + } + + if (ph2->state.length == 0 && + ph2->state.flags & NJT_HTTP_V2_END_HEADERS_FLAG) { + return NJT_OK; + } + + return NJT_AGAIN; +} \ No newline at end of file diff --git a/src/http/v2/njt_http_v2.h b/src/http/v2/njt_http_v2.h index afe2c3e1..8b671146 100755 --- a/src/http/v2/njt_http_v2.h +++ b/src/http/v2/njt_http_v2.h @@ -13,6 +13,7 @@ #include #include +#include #define NJT_HTTP_V2_ALPN_PROTO "\x02h2" @@ -89,6 +90,7 @@ typedef struct { unsigned parse_name:1; unsigned parse_value:1; unsigned index:1; + unsigned parse:1; njt_http_v2_header_t header; size_t header_limit; u_char field_state; @@ -124,6 +126,7 @@ typedef struct { struct njt_http_v2_connection_s { njt_connection_t *connection; njt_http_connection_t *http_connection; + void *init_ssl_data; off_t total_bytes; off_t payload_bytes; @@ -164,10 +167,20 @@ struct njt_http_v2_connection_s { time_t lingering_time; + njt_buf_t *free_bufs; + njt_buf_t *free_shadow_bufs; + +#ifdef NJT_HTTP_V2_DEBUG_ALLOC + njt_uint_t nbufs; + njt_uint_t nshadowbufs; +#endif + unsigned settings_ack:1; unsigned table_update:1; unsigned blocked:1; unsigned goaway:1; + unsigned client:1; + unsigned fake:1; }; @@ -189,6 +202,7 @@ struct njt_http_v2_stream_s { njt_http_request_t *request; njt_http_v2_connection_t *connection; njt_http_v2_node_t *node; + njt_connection_t *fc; njt_uint_t queued; @@ -212,6 +226,8 @@ struct njt_http_v2_stream_s { njt_array_t *cookies; njt_pool_t *pool; + njt_http_v2_stream_buffer_t recv; + njt_http_v2_state_t *state; unsigned waiting:1; unsigned blocked:1; @@ -311,7 +327,27 @@ njt_int_t njt_http_v2_add_header(njt_http_v2_connection_t *h2c, njt_http_v2_header_t *header); njt_int_t njt_http_v2_table_size(njt_http_v2_connection_t *h2c, size_t size); +njt_int_t njt_http_v2_send_preface(njt_http_v2_connection_t *h2c); +njt_int_t njt_http_v2_send_settings(njt_http_v2_connection_t *h2c); +void njt_http_v2_read_handler(njt_event_t *rev); +void njt_http_v2_write_handler(njt_event_t *wev); +njt_int_t njt_http_v2_create_client(njt_http_v2_conf_t *conf, njt_connection_t *c); +njt_http_v2_stream_t *njt_http_v2_create_stream( + njt_http_v2_connection_t *h2c); +njt_http_v2_node_t *njt_http_v2_get_node_by_id( + njt_http_v2_connection_t *h2c, njt_uint_t sid, njt_uint_t alloc); +void njt_http_v2_set_dependency(njt_http_v2_connection_t *h2c, + njt_http_v2_node_t *node, njt_uint_t depend, njt_uint_t exclusive); +void njt_http_v2_finalize_connection(njt_http_v2_connection_t *h2c, + njt_uint_t status); +njt_int_t njt_http_v2_send_window_update(njt_http_v2_connection_t *h2c, + njt_uint_t sid, size_t window); +njt_int_t njt_http_v2_parse_headers(njt_http_v2_connection_t *ph2, njt_buf_t *b); +u_char *njt_http_v2_state_header_block(njt_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +#define njt_http_v2_index_size(h2scf) (h2scf->streams_index_mask + 1) +#define njt_http_v2_index(h2scf, sid) ((sid >> 1) & h2scf->streams_index_mask) #define njt_http_v2_prefix(bits) ((1 << (bits)) - 1) diff --git a/src/http/v2/njt_http_v2_filter_module.c b/src/http/v2/njt_http_v2_filter_module.c index 91fc8c95..31b8c58a 100755 --- a/src/http/v2/njt_http_v2_filter_module.c +++ b/src/http/v2/njt_http_v2_filter_module.c @@ -4,6 +4,7 @@ * Copyright (C) 2021-2023 TMLake(Beijing) Technology Co., Ltd. * Copyright (C) Valentin V. Bartenev * Copyright (C) Ruslan Ermilov + * Copyright (C) 2024 JD Technology Information Technology Co., Ltd. */ @@ -28,12 +29,14 @@ #define NJT_HTTP_V2_NO_TRAILERS (njt_http_v2_out_frame_t *) -1 -static njt_http_v2_out_frame_t *njt_http_v2_create_headers_frame( - njt_http_request_t *r, u_char *pos, u_char *end, njt_uint_t fin); +njt_http_v2_out_frame_t *njt_http_v2_create_headers_frame( + njt_http_v2_stream_t *stream, u_char *pos, u_char *end, njt_uint_t fin); static njt_http_v2_out_frame_t *njt_http_v2_create_trailers_frame( njt_http_request_t *r); -static njt_chain_t *njt_http_v2_send_chain(njt_connection_t *fc, +njt_chain_t *njt_http_v2_send_chain(njt_connection_t *fc, + njt_chain_t *in, off_t limit); +ssize_t njt_http_v2_recv_chain(njt_connection_t *fc, njt_chain_t *in, off_t limit); static njt_chain_t *njt_http_v2_filter_get_shadow( @@ -47,7 +50,7 @@ static njt_inline njt_int_t njt_http_v2_flow_control( static void njt_http_v2_waiting_queue(njt_http_v2_connection_t *h2c, njt_http_v2_stream_t *stream); -static njt_inline njt_int_t njt_http_v2_filter_send( +njt_int_t njt_http_v2_filter_send( njt_connection_t *fc, njt_http_v2_stream_t *stream); static njt_int_t njt_http_v2_headers_frame_handler( @@ -59,7 +62,7 @@ static njt_inline void njt_http_v2_handle_frame( static njt_inline void njt_http_v2_handle_stream( njt_http_v2_connection_t *h2c, njt_http_v2_stream_t *stream); -static void njt_http_v2_filter_cleanup(void *data); +void njt_http_v2_filter_cleanup(void *data); static njt_int_t njt_http_v2_filter_init(njt_conf_t *cf); @@ -605,7 +608,7 @@ njt_http_v2_header_filter(njt_http_request_t *r) fin = r->header_only || (r->headers_out.content_length_n == 0 && !r->expect_trailers); - frame = njt_http_v2_create_headers_frame(r, start, pos, fin); + frame = njt_http_v2_create_headers_frame(r->stream, start, pos, fin); if (frame == NULL) { return NJT_ERROR; } @@ -630,21 +633,21 @@ njt_http_v2_header_filter(njt_http_request_t *r) } -static njt_http_v2_out_frame_t * -njt_http_v2_create_headers_frame(njt_http_request_t *r, u_char *pos, +njt_http_v2_out_frame_t * +njt_http_v2_create_headers_frame(njt_http_v2_stream_t *stream, u_char *pos, u_char *end, njt_uint_t fin) { u_char type, flags; size_t rest, frame_size; njt_buf_t *b; njt_chain_t *cl, **ll; - njt_http_v2_stream_t *stream; + //njt_http_v2_connection_t *h2c; njt_http_v2_out_frame_t *frame; - stream = r->stream; + //h2c = stream->connection; rest = end - pos; - frame = njt_palloc(r->pool, sizeof(njt_http_v2_out_frame_t)); + frame = njt_palloc(stream->request->pool, sizeof(njt_http_v2_out_frame_t)); if (frame == NULL) { return NULL; } @@ -667,7 +670,7 @@ njt_http_v2_create_headers_frame(njt_http_request_t *r, u_char *pos, flags |= NJT_HTTP_V2_END_HEADERS_FLAG; } - b = njt_create_temp_buf(r->pool, NJT_HTTP_V2_FRAME_HEADER_SIZE); + b = njt_create_temp_buf(stream->request->pool, NJT_HTTP_V2_FRAME_HEADER_SIZE); if (b == NULL) { return NULL; } @@ -678,7 +681,7 @@ njt_http_v2_create_headers_frame(njt_http_request_t *r, u_char *pos, b->tag = (njt_buf_tag_t) &njt_http_v2_module; - cl = njt_alloc_chain_link(r->pool); + cl = njt_alloc_chain_link(stream->request->pool); if (cl == NULL) { return NULL; } @@ -688,7 +691,7 @@ njt_http_v2_create_headers_frame(njt_http_request_t *r, u_char *pos, *ll = cl; ll = &cl->next; - b = njt_calloc_buf(r->pool); + b = njt_calloc_buf(stream->request->pool); if (b == NULL) { return NULL; } @@ -702,7 +705,7 @@ njt_http_v2_create_headers_frame(njt_http_request_t *r, u_char *pos, b->end = b->last; b->temporary = 1; - cl = njt_alloc_chain_link(r->pool); + cl = njt_alloc_chain_link(stream->request->pool); if (cl == NULL) { return NULL; } @@ -726,7 +729,7 @@ njt_http_v2_create_headers_frame(njt_http_request_t *r, u_char *pos, cl->next = NULL; frame->last = cl; - njt_log_debug4(NJT_LOG_DEBUG_HTTP, r->connection->log, 0, + njt_log_debug4(NJT_LOG_DEBUG_HTTP, stream->fc->log, 0, "http2:%ui create HEADERS frame %p: len:%uz fin:%ui", stream->node->id, frame, frame->length, fin); @@ -845,11 +848,11 @@ njt_http_v2_create_trailers_frame(njt_http_request_t *r) header[i].value.len, tmp); } - return njt_http_v2_create_headers_frame(r, start, pos, 1); + return njt_http_v2_create_headers_frame(r->stream, start, pos, 1); } -static njt_chain_t * +njt_chain_t * njt_http_v2_send_chain(njt_connection_t *fc, njt_chain_t *in, off_t limit) { off_t size, offset; @@ -862,7 +865,7 @@ njt_http_v2_send_chain(njt_connection_t *fc, njt_chain_t *in, off_t limit) njt_http_v2_connection_t *h2c; r = fc->data; - stream = r->stream; + stream = fc->stream; #if (NJT_SUPPRESS_WARN) size = 0; @@ -1248,7 +1251,7 @@ njt_http_v2_waiting_queue(njt_http_v2_connection_t *h2c, } -static njt_inline njt_int_t +njt_int_t njt_http_v2_filter_send(njt_connection_t *fc, njt_http_v2_stream_t *stream) { njt_connection_t *c; @@ -1324,9 +1327,10 @@ njt_http_v2_headers_frame_handler(njt_http_v2_connection_t *h2c, njt_log_debug2(NJT_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2:%ui HEADERS frame %p was sent", stream->node->id, frame); - - stream->request->header_size += NJT_HTTP_V2_FRAME_HEADER_SIZE + if (!h2c->client) { + stream->request->header_size += NJT_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + } h2c->payload_bytes += frame->length; @@ -1420,8 +1424,9 @@ done: njt_log_debug2(NJT_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2:%ui DATA frame %p was sent", stream->node->id, frame); - - stream->request->header_size += NJT_HTTP_V2_FRAME_HEADER_SIZE; + if (!h2c->client) { + stream->request->header_size += NJT_HTTP_V2_FRAME_HEADER_SIZE; + } h2c->payload_bytes += frame->length; @@ -1437,12 +1442,12 @@ static njt_inline void njt_http_v2_handle_frame(njt_http_v2_stream_t *stream, njt_http_v2_out_frame_t *frame) { - njt_http_request_t *r; njt_http_v2_connection_t *h2c; + njt_connection_t *fc; - r = stream->request; + fc = stream->fc; - r->connection->sent += NJT_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + fc->sent += NJT_HTTP_V2_FRAME_HEADER_SIZE + frame->length; h2c = stream->connection; @@ -1470,7 +1475,7 @@ njt_http_v2_handle_stream(njt_http_v2_connection_t *h2c, return; } - fc = stream->request->connection; + fc = stream->fc; if (!fc->error && stream->exhausted) { return; @@ -1489,7 +1494,7 @@ njt_http_v2_handle_stream(njt_http_v2_connection_t *h2c, } -static void +void njt_http_v2_filter_cleanup(void *data) { njt_http_v2_stream_t *stream = data; @@ -1546,7 +1551,7 @@ njt_http_v2_filter_cleanup(void *data) stream->waiting = 0; - wev = stream->request->connection->write; + wev = stream->fc->write; wev->active = 0; wev->ready = 1; diff --git a/src/http/v2/njt_http_v2_stream.c b/src/http/v2/njt_http_v2_stream.c new file mode 100644 index 00000000..d78bf0b7 --- /dev/null +++ b/src/http/v2/njt_http_v2_stream.c @@ -0,0 +1,885 @@ +/* + * Copyright (C) 2024 Kern + */ + +#include +#include +#include + + +#define NJT_HTTP_V2_BUFFER_SIZE 4096 + +#define njt_http_v2_buf_refs(b) (b)->shadow->num +#define njt_http_v2_buf_inc_refs(b) njt_http_v2_buf_refs(b)++ +#define njt_http_v2_buf_dec_refs(b) njt_http_v2_buf_refs(b)-- +#define njt_http_v2_buf_set_refs(b, v) njt_http_v2_buf_refs(b) = v + + +static njt_buf_t *njt_http_v2_alloc_buf(njt_connection_t *c); +static void njt_http_v2_free_buf(njt_connection_t *c, njt_buf_t *b); +static njt_buf_t *njt_http_v2_clone_buf(njt_connection_t *c, njt_buf_t *b); +static njt_int_t njt_http_v2_split_chain(njt_connection_t *c, njt_chain_t *cl, + off_t offset); +njt_chain_t *njt_http_v2_write_buffer_chain(njt_connection_t *c, njt_http_v2_stream_buffer_t *qb, + njt_chain_t *in, uint64_t limit, uint64_t offset); + +static njt_buf_t * +njt_http_v2_alloc_buf(njt_connection_t *c) +{ + u_char *p; + njt_buf_t *b; + njt_http_v2_connection_t *h2c; + + h2c = njt_http_v2_get_connection(c); + + b = h2c->free_bufs; + + if (b) { + h2c->free_bufs = b->shadow; + p = b->start; + + } else { + b = h2c->free_shadow_bufs; + + if (b) { + h2c->free_shadow_bufs = b->shadow; + +#ifdef NJT_HTTP_V2_DEBUG_ALLOC + njt_log_debug2(NJT_LOG_DEBUG_EVENT, c->log, 0, + "quic use shadow buffer n:%ui %ui", + ++h2c->nbufs, --h2c->nshadowbufs); +#endif + + } else { + b = njt_palloc(h2c->pool, sizeof(njt_buf_t)); + if (b == NULL) { + return NULL; + } + +#ifdef NJT_HTTP_V2_DEBUG_ALLOC + njt_log_debug1(NJT_LOG_DEBUG_EVENT, c->log, 0, + "quic new buffer n:%ui", ++h2c->nbufs); +#endif + } + + p = njt_pnalloc(h2c->pool, NJT_HTTP_V2_BUFFER_SIZE); + if (p == NULL) { + return NULL; + } + } + +#ifdef NJT_HTTP_V2_DEBUG_ALLOC + njt_log_debug1(NJT_LOG_DEBUG_EVENT, c->log, 0, "quic alloc buffer %p", b); +#endif + + njt_memzero(b, sizeof(njt_buf_t)); + + b->tag = (njt_buf_tag_t) &njt_http_v2_alloc_buf; + b->temporary = 1; + b->shadow = b; + + b->start = p; + b->pos = p; + b->last = p; + b->end = p + NJT_HTTP_V2_BUFFER_SIZE; + + njt_http_v2_buf_set_refs(b, 1); + + return b; +} + + +static void +njt_http_v2_free_buf(njt_connection_t *c, njt_buf_t *b) +{ + njt_buf_t *shadow; + njt_http_v2_connection_t *h2c; + + h2c = njt_http_v2_get_connection(c); + + njt_http_v2_buf_dec_refs(b); + +#ifdef NJT_HTTP_V2_DEBUG_ALLOC + njt_log_debug2(NJT_LOG_DEBUG_EVENT, c->log, 0, + "quic free buffer %p r:%ui", + b, (njt_uint_t) njt_http_v2_buf_refs(b)); +#endif + + shadow = b->shadow; + + if (njt_http_v2_buf_refs(b) == 0) { + shadow->shadow = h2c->free_bufs; + h2c->free_bufs = shadow; + } + + if (b != shadow) { + b->shadow = h2c->free_shadow_bufs; + h2c->free_shadow_bufs = b; + } + +} + + +static njt_buf_t * +njt_http_v2_clone_buf(njt_connection_t *c, njt_buf_t *b) +{ + njt_buf_t *nb; + njt_http_v2_connection_t *h2c; + + h2c = njt_http_v2_get_connection(c); + + nb = h2c->free_shadow_bufs; + + if (nb) { + h2c->free_shadow_bufs = nb->shadow; + + } else { + nb = njt_palloc(h2c->pool, sizeof(njt_buf_t)); + if (nb == NULL) { + return NULL; + } + +#ifdef NJT_HTTP_V2_DEBUG_ALLOC + njt_log_debug1(NJT_LOG_DEBUG_EVENT, c->log, 0, + "quic new shadow buffer n:%ui", ++h2c->nshadowbufs); +#endif + } + + *nb = *b; + + njt_http_v2_buf_inc_refs(b); + +#ifdef NJT_HTTP_V2_DEBUG_ALLOC + njt_log_debug3(NJT_LOG_DEBUG_EVENT, c->log, 0, + "quic clone buffer %p %p r:%ui", + b, nb, (njt_uint_t) njt_http_v2_buf_refs(b)); +#endif + + return nb; +} + +static njt_int_t +njt_http_v2_split_chain(njt_connection_t *c, njt_chain_t *cl, off_t offset) +{ + njt_buf_t *b, *tb; + njt_chain_t *tail; + + b = cl->buf; + + tail = njt_alloc_chain_link(c->pool); + if (tail == NULL) { + return NJT_ERROR; + } + + tb = njt_http_v2_clone_buf(c, b); + if (tb == NULL) { + return NJT_ERROR; + } + + tail->buf = tb; + + tb->pos += offset; + + b->last = tb->pos; + b->last_buf = 0; + + tail->next = cl->next; + cl->next = tail; + + return NJT_OK; +} + +/*void +njt_http_v2_out_chain( njt_chain_t *out, int w) +{ + njt_chain_t *cl; + int i = 0; + while (out) { + cl = out; + out = out->next; + printf("%s:%d -- cl:%p, next:%p , pnext:%p, buf:%p, sync:%d, len:%ld\n", + w ==1 ? "write":"read",i++, cl,cl->next,&cl->next,cl->buf,cl->buf->sync, + cl->buf->last - cl->buf->pos); + } +}*/ + +njt_chain_t * +njt_http_v2_read_buffer(njt_connection_t *c, njt_http_v2_stream_buffer_t *qb, uint64_t limit) +{ + uint64_t n; + njt_buf_t *b; + njt_chain_t *out, **ll; + + out = qb->chain; + for (ll = &out; *ll; ll = &(*ll)->next) { + b = (*ll)->buf; + + if (b->sync) { + /*not used*/ + break;; + } + if (limit == 0) { + break; + } + + n = b->last - b->pos; + + if (n > limit) { + if (njt_http_v2_split_chain(c, *ll, limit) != NJT_OK) { + return NJT_CHAIN_ERROR; + } + if (qb->last_chain == &(*ll)->next) { + qb->last_chain = &(*ll)->next->next; + } + n = limit; + } + + qb->size -= n; + limit -= n; + } + + if (*ll == NULL || qb->last_chain == ll) { + qb->last_chain = &qb->chain; + } + + qb->chain = *ll; + *ll = NULL; + + if (out && out->buf->sync) { + return NULL; + } + + return out; +} + +njt_chain_t * +njt_http_v2_alloc_chain(njt_connection_t *c) +{ + njt_chain_t *cl; + + cl = njt_alloc_chain_link(c->pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = njt_http_v2_alloc_buf(c); + if (cl->buf == NULL) { + return NULL; + } + + return cl; +} + +njt_int_t +njt_http_v2_write_buffer(njt_connection_t *c, u_char *data,size_t len) { + njt_buf_t buf; + njt_chain_t cl; + njt_http_v2_stream_t *stream; + + stream = c->stream; + + njt_memzero(&buf, sizeof(njt_buf_t)); + + buf.pos = data; + buf.last = buf.pos + len; + buf.temporary = 1; + + cl.buf = &buf; + cl.next = NULL; + + if (njt_http_v2_write_chain(c, &stream->recv, &cl, len) == NJT_CHAIN_ERROR) { + return NJT_ERROR; + } + return NJT_OK; +} + + +njt_chain_t * +njt_http_v2_write_chain(njt_connection_t *c, njt_http_v2_stream_buffer_t *qb, + njt_chain_t *in, uint64_t limit) +{ + u_char *p; + uint64_t n; + njt_buf_t *b; + njt_chain_t *cl, **chain; + + chain = qb->last_chain; + while (in && limit) { + + cl = *chain; + if (cl == NULL) { + cl = njt_http_v2_alloc_chain(c); + if (cl == NULL) { + return NJT_CHAIN_ERROR; + } + + cl->buf->last = cl->buf->end; + cl->buf->sync = 1; /* not used */ + cl->next = NULL; + *chain = cl; + } + + b = cl->buf; + p = b->pos; + while (in) { + + if (!njt_buf_in_memory(in->buf) || in->buf->pos == in->buf->last) { + in = in->next; + continue; + } + + if (p == b->last || limit == 0) { + break; + } + + n = njt_min(b->last - p, in->buf->last - in->buf->pos); + n = njt_min(n, limit); + + + njt_memcpy(p, in->buf->pos, n); + qb->size += n; + + p += n; + in->buf->pos += n; + limit -= n; + } + + if (b->sync && p == b->last) { + b->sync = 0; + chain = &cl->next; + continue; + } + + if (b->sync && p != b->pos) { + if (njt_http_v2_split_chain(c, cl, p - b->pos) != NJT_OK) { + return NJT_CHAIN_ERROR; + } + b->sync = 0; + chain = &cl->next; + } + } + qb->last_chain = chain; + return in; +} + +void +njt_http_v2_free_chain(njt_connection_t *c, njt_chain_t *in) +{ + njt_chain_t *cl; + njt_http_v2_connection_t *h2c; + + h2c = njt_http_v2_get_connection(c); + + while (in) { + cl = in; + in = in->next; + + njt_http_v2_free_buf(c, cl->buf); + njt_free_chain(h2c->pool, cl); + } +} + +void +njt_http_v2_free_buffer(njt_connection_t *c, njt_http_v2_stream_buffer_t *qb) +{ + njt_http_v2_free_chain(c, qb->chain); + + qb->chain = NULL; +} + +ssize_t +njt_http_v2_stream_recv(njt_connection_t *c, u_char *buf, size_t size) +{ + ssize_t len; + njt_buf_t *b; + njt_chain_t *cl, *in; + njt_event_t *rev; + njt_http_v2_stream_t *stream; + njt_http_v2_connection_t *h2c; + + stream = c->stream; + h2c = stream->connection; + rev = c->read; + + if (c->error) { + njt_log_debug1(NJT_LOG_DEBUG_EVENT, c->log, 0, + "http2 stream id:0x%xL bad recv state", + stream->node->id); + return NJT_ERROR; + } + + njt_log_debug2(NJT_LOG_DEBUG_EVENT, c->log, 0, + "http2 stream id:0x%xL expected recv buf:%uz", + stream->node->id, size); + + if (size == 0) { + return 0; + } + + in = njt_http_v2_read_buffer(c, &stream->recv, size); + if (in == NJT_CHAIN_ERROR) { + return NJT_ERROR; + } + + len = 0; + + for (cl = in; cl; cl = cl->next) { + b = cl->buf; + len += b->last - b->pos; + buf = njt_cpymem(buf, b->pos, b->last - b->pos); + } + + njt_http_v2_free_chain(c, in); + + if (len == 0) { + rev->ready = 0; + + if (stream->in_closed) { + rev->eof = 1; + return 0; + } + + njt_log_debug1(NJT_LOG_DEBUG_EVENT, c->log, 0, + "http2 stream id:0x%xL recv() not ready", stream->node->id); + return NJT_AGAIN; + } + + if (njt_http_v2_send_window_update(stream->connection, + stream->node->id, len) + == NJT_ERROR) + { + stream->skip_data = 1; + return NJT_ERROR; + } + + h2c = stream->connection; + + if (!h2c->blocked) { + if (njt_http_v2_send_output_queue(h2c) == NJT_ERROR) { + stream->skip_data = 1; + return NJT_ERROR; + } + } + + stream->recv_window += len; + + njt_log_debug2(NJT_LOG_DEBUG_EVENT, c->log, 0, + "http2 stream id:0x%xL actual recv len:%z", stream->node->id, len); + + return len; +} + +ssize_t +njt_http_v2_stream_send(njt_connection_t *c, u_char *buf, size_t size) +{ + njt_log_error(NJT_LOG_ERR,c->log,0,"call njt_http_v2_stream_send error"); + return 0; +} + + +static void +ng_http_v2_copy_chain_data(njt_chain_t *dst_chain, njt_chain_t *src_chain) +{ + u_char *rpos, *wpos; + size_t data_size, buf_size, len; + njt_chain_t *src, *dst; + + src = src_chain; + dst = dst_chain; + + rpos = src->buf->pos; + wpos = dst->buf->last; + + while (src && dst) { + + data_size = src->buf->last - rpos; + buf_size = dst->buf->end - wpos; + + len = njt_min(data_size, buf_size); + + njt_memcpy(wpos, rpos, len); + + rpos += len; + wpos += len; + + if (rpos == src->buf->last) { + src = src->next; + if (src) { + rpos = src->buf->pos; + } + } + + if (wpos == dst->buf->end) { + dst = dst->next; + if (dst) { + wpos = dst->buf->last; + } + } + } +} + +ssize_t +njt_http_v2_recv_chain(njt_connection_t *fc, njt_chain_t *in, off_t limit) { + size_t len; + njt_buf_t *b; + njt_chain_t *cl, *out; + njt_event_t *rev; + njt_http_v2_stream_t *stream; + njt_http_v2_connection_t *h2c; + + stream = fc->stream; + h2c = stream->connection; + rev = fc->read; + + njt_log_debug1(NJT_LOG_DEBUG_EVENT, fc->log, 0, + "http2 stream id:0x%xL recv chain", stream->node->id); + + len = 0; + for (cl = in; cl; cl = cl->next) { + len += cl->buf->end - cl->buf->last; + } + + if (limit && len > (size_t) limit) { + len = limit; + } + + out = njt_http_v2_read_buffer(fc, &stream->recv, len); + if (out == NJT_CHAIN_ERROR) { + return NJT_ERROR; + } + + len = 0; + + for (cl = out; cl; cl = cl->next) { + b = cl->buf; + len += b->last - b->pos; + } + + if (len == 0) { + rev->ready = 0; + + if (stream->in_closed) { + rev->eof = 1; + return 0; + } + + njt_log_debug1(NJT_LOG_DEBUG_EVENT, fc->log, 0, + "http2 stream id:0x%xL recv chain() not ready", stream->node->id); + return NJT_AGAIN; + } + + ng_http_v2_copy_chain_data(in, out); + + njt_http_v2_free_chain(fc, out); + + njt_log_debug2(NJT_LOG_DEBUG_EVENT, fc->log, 0, + "http2 stream id:0x%xL actual recv chain len:%z", stream->node->id, len); + + if (njt_http_v2_send_window_update(stream->connection, + stream->node->id, len) + == NJT_ERROR) + { + stream->skip_data = 1; + return NJT_ERROR; + } + + h2c = stream->connection; + + if (!h2c->blocked) { + if (njt_http_v2_send_output_queue(h2c) == NJT_ERROR) { + stream->skip_data = 1; + return NJT_ERROR; + } + } + + stream->recv_window += len; + return len; + +} + +/*const int MAX_BUFF_SIZE = 1024*20; +const int MAX_RECV_SIZE = 5000; +const int MAX_RECV_4096 = 4096; + +njt_int_t +njt_http_v2_stream_buf_test_rand() +{ + njt_pool_t *pool; + njt_connection_t *conn; + njt_chain_t *cl, *in; + njt_http_v2_stream_t *stream; + njt_buf_t *b; + njt_http_v2_stream_buffer_t *qb; + njt_http_v2_connection_t *h2c; + + // 使用当前时间作为随机数种子 + srand(time(0)); + u_char wr[MAX_BUFF_SIZE+10]; + u_char rd[MAX_BUFF_SIZE+10]; + + pool = njt_create_pool(1024, njt_cycle->log); + if (pool == NULL) { + return 0; + } + + h2c = njt_pcalloc(pool, sizeof(njt_http_v2_connection_t)); + if (h2c == NULL) { + return 0; + } + + conn = njt_palloc(pool, sizeof(njt_connection_t)); + if (conn == NULL) { + return 0; + } + conn->pool = pool; + h2c->connection = conn; + conn->data = h2c; + h2c->pool = pool; + + + stream = njt_pcalloc(pool, sizeof(njt_http_v2_stream_t)); + if (stream == NULL) { + return 0; + } + + stream->connection = h2c; + stream->recv.last_chain = &stream->recv.chain; + conn->stream = stream; + stream->fc = conn; + qb = &stream->recv; + + size_t len, i = 0; + size_t recv_stat = 0, send_stat = 0 ,actual_read = 0, actual_write = 0; + size_t expect_read = 0, expecpt_write = 0; + + while (1) { + int w = rand() % 20 + 2; + actual_read = 0; + actual_write = 0; + expect_read = 0; + expecpt_write = 0; + while(w--) { + size_t size = (rand() % MAX_BUFF_SIZE) + 1; + char ch = 'a' + i++ % 26; + memset(wr,ch,size); + njt_http_v2_write_buffer(conn,wr,size); + send_stat += size; + actual_write += size; + expecpt_write += size; + } + + int r = rand() % 20 + 6; + while(r--) { + size_t size = rand() % MAX_BUFF_SIZE + 1; + + in = njt_http_v2_read_buffer(conn, &stream->recv, size); + if (in == NJT_CHAIN_ERROR) { + return 0; + } + + len = 0; + u_char *buf = rd; + for (cl = in; cl; cl = cl->next) { + b = cl->buf; + len += b->last - b->pos; + buf = njt_cpymem(buf, b->pos, b->last - b->pos); + } + + + recv_stat += len; + actual_read += len; + expect_read += size; + njt_http_v2_free_chain(conn, in); + + njt_chain_t *out = qb->chain; + len = 0; + while (out) { + cl = out; + if (!cl->buf->sync) { + len += cl->buf->last - cl->buf->pos; + } + out = out->next; + } + + if (len != qb->size) { + printf("qb->size:%ld, buf:%ld\n", qb->size, len); + return 0; + } + } + + printf("recv:%ld, send:%ld, buf:%ld, expecpt_write:%ld, actual_write:%ld, expecpt_read:%ld, actual_read:%ld\n", + recv_stat, send_stat, qb->size, expecpt_write, actual_write,expect_read,actual_read); + njt_http_v2_out_chain(qb->chain,0); + printf("last_chain:%p, last_chain**:%p\n", *qb->last_chain, qb->last_chain); + njt_http_v2_out_chain(*qb->last_chain,0); + printf("----------------------------------\n\n"); + + if (send_stat != recv_stat + qb->size) { + printf("recv:%ld, send:%ld, buf:%ld\n", recv_stat, send_stat, qb->size); + return 0; + } + + njt_chain_t *out = qb->chain; + len = 0; + while (out) { + cl = out; + if (!cl->buf->sync) { + len += cl->buf->last - cl->buf->pos; + } + out = out->next; + } + + if (len != qb->size) { + printf("qb->size:%ld, buf:%ld\n", qb->size, len); + return 0; + } + } + + return 0; +} + +njt_int_t +njt_http_v2_stream_buf_test_4096() +{ + njt_pool_t *pool; + njt_connection_t *conn; + njt_chain_t *cl, *in; + njt_http_v2_stream_t *stream; + njt_buf_t *b; + njt_http_v2_stream_buffer_t *qb; + njt_http_v2_connection_t *h2c; + + // 使用当前时间作为随机数种子 + srand(time(0)); + u_char wr[MAX_BUFF_SIZE+10]; + u_char rd[MAX_BUFF_SIZE+10]; + + pool = njt_create_pool(1024, njt_cycle->log); + if (pool == NULL) { + return 0; + } + + h2c = njt_pcalloc(pool, sizeof(njt_http_v2_connection_t)); + if (h2c == NULL) { + return 0; + } + + + conn = njt_palloc(pool, sizeof(njt_connection_t)); + if (conn == NULL) { + return 0; + } + conn->pool = pool; + h2c->connection = conn; + conn->data = h2c; + h2c->pool = pool; + + + stream = njt_pcalloc(pool, sizeof(njt_http_v2_stream_t)); + if (stream == NULL) { + return 0; + } + + stream->connection = h2c; + stream->recv.last_chain = &stream->recv.chain; + conn->stream = stream; + stream->fc = conn; + qb = &stream->recv; + + size_t len, i = 0; + size_t recv_stat = 0, send_stat = 0 ,actual_read = 0, actual_write = 0; + size_t expect_read = 0, expecpt_write = 0; + + while (1) { + int w = rand() % 20 + 2; + actual_read = 0; + actual_write = 0; + expect_read = 0; + expecpt_write = 0; + while(w--) { + size_t size = MAX_RECV_4096; + char ch = 'a' + i++ % 26; + memset(wr,ch,size); + njt_http_v2_write_buffer(conn,wr,size); + send_stat += size; + actual_write += size; + expecpt_write += size; + } + + int r = rand() % 20 + 6; + while(r--) { + size_t size = MAX_RECV_4096; + + in = njt_http_v2_read_buffer(conn, &stream->recv, size); + if (in == NJT_CHAIN_ERROR) { + return 0; + } + + len = 0; + u_char *buf = rd; + for (cl = in; cl; cl = cl->next) { + b = cl->buf; + len += b->last - b->pos; + buf = njt_cpymem(buf, b->pos, b->last - b->pos); + } + + + recv_stat += len; + actual_read += len; + expect_read += size; + njt_http_v2_free_chain(conn, in); + + njt_chain_t *out = qb->chain; + len = 0; + while (out) { + cl = out; + if (!cl->buf->sync) { + len += cl->buf->last - cl->buf->pos; + } + out = out->next; + } + + if (len != qb->size) { + printf("qb->size:%ld, buf:%ld\n", qb->size, len); + return 0; + } + } + + printf("recv:%ld, send:%ld, buf:%ld, expecpt_write:%ld, actual_write:%ld, expecpt_read:%ld, actual_read:%ld\n", + recv_stat, send_stat, qb->size, expecpt_write, actual_write,expect_read,actual_read); + njt_http_v2_out_chain(qb->chain,0); + printf("last_chain:%p, last_chain**:%p\n", *qb->last_chain, qb->last_chain); + njt_http_v2_out_chain(*qb->last_chain,0); + printf("----------------------------------\n\n"); + + if (send_stat != recv_stat + qb->size) { + printf("recv:%ld, send:%ld, buf:%ld\n", recv_stat, send_stat, qb->size); + return 0; + } + + njt_chain_t *out = qb->chain; + len = 0; + while (out) { + cl = out; + if (!cl->buf->sync) { + len += cl->buf->last - cl->buf->pos; + } + out = out->next; + } + + if (len != qb->size) { + printf("qb->size:%ld, buf:%ld\n", qb->size, len); + return 0; + } + } + + return 0; +} + +njt_int_t +njt_http_v2_stream_buf_test() { + njt_http_v2_stream_buf_test_4096(); + njt_http_v2_stream_buf_test_rand(); + return 0; +} +*/ \ No newline at end of file diff --git a/src/http/v2/njt_http_v2_stream.h b/src/http/v2/njt_http_v2_stream.h new file mode 100644 index 00000000..126d09ae --- /dev/null +++ b/src/http/v2/njt_http_v2_stream.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 Kern + */ + +#ifndef _NJT_HTTP_V2_STREAM_H_INCLUDED_ +#define _NJT_HTTP_V2_STREAM_H_INCLUDED_ + +#include +#include +#include + +typedef struct { + uint64_t size; + njt_chain_t *chain; + njt_chain_t **last_chain; +} njt_http_v2_stream_buffer_t; + +#define njt_http_v2_get_connection(c) \ + (((c)->stream) ? (((njt_http_v2_stream_t *)((c)->stream))->connection) : NULL) + +ssize_t njt_http_v2_stream_send(njt_connection_t *c, u_char *buf, size_t size); +ssize_t njt_http_v2_stream_recv(njt_connection_t *c, u_char *buf, size_t size); +njt_chain_t *njt_http_v2_write_chain(njt_connection_t *c, njt_http_v2_stream_buffer_t *qb, + njt_chain_t *in, uint64_t limit); +njt_int_t njt_http_v2_write_buffer(njt_connection_t *c, u_char *data,size_t len); +ssize_t njt_http_v2_recv_chain(njt_connection_t *fc, njt_chain_t *in, off_t limit); + +#endif \ No newline at end of file diff --git a/src/http/v2/njt_http_v2_table.c b/src/http/v2/njt_http_v2_table.c index 1fcb92a3..c78b6acb 100755 --- a/src/http/v2/njt_http_v2_table.c +++ b/src/http/v2/njt_http_v2_table.c @@ -349,7 +349,9 @@ njt_http_v2_table_size(njt_http_v2_connection_t *h2c, size_t size) njt_log_debug2(NJT_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 new hpack table size: %uz was:%uz", size, h2c->hpack.size); - + if (h2c->fake) { + return NJT_ERROR; + } needed = h2c->hpack.size - size; while (needed > (ssize_t) h2c->hpack.free) { -- Gitee