mirror of
https://github.com/nginx/nginx.git
synced 2025-07-21 11:46:20 +08:00
The change implements processing upstream early hints response in ngx_http_proxy_module and ngx_http_grpc_module. A new directive "early_hints" enables sending early hints to the client. By default, sending early hints is disabled. Example: map $http_sec_fetch_mode $early_hints { navigate $http2$http3; } early_hints $early_hints; proxy_pass http://example.com;
This commit is contained in:
parent
ea001feb10
commit
662c1dd2a9
@ -1869,7 +1869,8 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
|
|||||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status < NGX_HTTP_OK) {
|
if (status < NGX_HTTP_OK && status != NGX_HTTP_EARLY_HINTS)
|
||||||
|
{
|
||||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||||
"upstream sent unexpected :status \"%V\"",
|
"upstream sent unexpected :status \"%V\"",
|
||||||
status_line);
|
status_line);
|
||||||
@ -1902,6 +1903,10 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
|
|||||||
h->lowcase_key = h->key.data;
|
h->lowcase_key = h->key.data;
|
||||||
h->hash = ngx_hash_key(h->key.data, h->key.len);
|
h->hash = ngx_hash_key(h->key.data, h->key.len);
|
||||||
|
|
||||||
|
if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
|
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
|
||||||
h->lowcase_key, h->key.len);
|
h->lowcase_key, h->key.len);
|
||||||
|
|
||||||
@ -4413,6 +4418,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
|
|||||||
conf->upstream.pass_request_body = 1;
|
conf->upstream.pass_request_body = 1;
|
||||||
conf->upstream.force_ranges = 0;
|
conf->upstream.force_ranges = 0;
|
||||||
conf->upstream.pass_trailers = 1;
|
conf->upstream.pass_trailers = 1;
|
||||||
|
conf->upstream.pass_early_hints = 1;
|
||||||
conf->upstream.preserve_output = 1;
|
conf->upstream.preserve_output = 1;
|
||||||
|
|
||||||
conf->headers_source = NGX_CONF_UNSET_PTR;
|
conf->headers_source = NGX_CONF_UNSET_PTR;
|
||||||
|
@ -1888,6 +1888,13 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r)
|
|||||||
u->headers_in.status_n, &u->headers_in.status_line);
|
u->headers_in.status_n, &u->headers_in.status_line);
|
||||||
|
|
||||||
if (ctx->status.http_version < NGX_HTTP_VERSION_11) {
|
if (ctx->status.http_version < NGX_HTTP_VERSION_11) {
|
||||||
|
|
||||||
|
if (ctx->status.code == NGX_HTTP_EARLY_HINTS) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||||
|
"upstream sent HTTP/1.0 response with early hints");
|
||||||
|
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
u->headers_in.connection_close = 1;
|
u->headers_in.connection_close = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1949,6 +1956,14 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
|||||||
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
|
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||||
|
"http proxy header: \"%V: %V\"",
|
||||||
|
&h->key, &h->value);
|
||||||
|
|
||||||
|
if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
|
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
|
||||||
h->lowcase_key, h->key.len);
|
h->lowcase_key, h->key.len);
|
||||||
|
|
||||||
@ -1960,10 +1975,6 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
||||||
"http proxy header: \"%V: %V\"",
|
|
||||||
&h->key, &h->value);
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1974,6 +1985,10 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
|||||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||||
"http proxy header done");
|
"http proxy header done");
|
||||||
|
|
||||||
|
if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if no "Server" and "Date" in header line,
|
* if no "Server" and "Date" in header line,
|
||||||
* then add the special empty headers
|
* then add the special empty headers
|
||||||
@ -3628,10 +3643,10 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
|
|||||||
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
|
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* "proxy_cyclic_temp_file" is disabled */
|
/* the hardcoded values */
|
||||||
conf->upstream.cyclic_temp_file = 0;
|
conf->upstream.cyclic_temp_file = 0;
|
||||||
|
|
||||||
conf->upstream.change_buffering = 1;
|
conf->upstream.change_buffering = 1;
|
||||||
|
conf->upstream.pass_early_hints = 1;
|
||||||
|
|
||||||
conf->headers_source = NGX_CONF_UNSET_PTR;
|
conf->headers_source = NGX_CONF_UNSET_PTR;
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ ngx_uint_t ngx_http_max_module;
|
|||||||
|
|
||||||
|
|
||||||
ngx_http_output_header_filter_pt ngx_http_top_header_filter;
|
ngx_http_output_header_filter_pt ngx_http_top_header_filter;
|
||||||
|
ngx_http_output_header_filter_pt ngx_http_top_early_hints_filter;
|
||||||
ngx_http_output_body_filter_pt ngx_http_top_body_filter;
|
ngx_http_output_body_filter_pt ngx_http_top_body_filter;
|
||||||
ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;
|
ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;
|
||||||
|
|
||||||
|
@ -152,6 +152,7 @@ ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,
|
|||||||
ngx_int_t ngx_http_read_unbuffered_request_body(ngx_http_request_t *r);
|
ngx_int_t ngx_http_read_unbuffered_request_body(ngx_http_request_t *r);
|
||||||
|
|
||||||
ngx_int_t ngx_http_send_header(ngx_http_request_t *r);
|
ngx_int_t ngx_http_send_header(ngx_http_request_t *r);
|
||||||
|
ngx_int_t ngx_http_send_early_hints(ngx_http_request_t *r);
|
||||||
ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,
|
ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,
|
||||||
ngx_int_t error);
|
ngx_int_t error);
|
||||||
ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
|
ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
|
||||||
@ -191,6 +192,7 @@ extern ngx_str_t ngx_http_html_default_types[];
|
|||||||
|
|
||||||
|
|
||||||
extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
|
extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
|
||||||
|
extern ngx_http_output_header_filter_pt ngx_http_top_early_hints_filter;
|
||||||
extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
|
extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
|
||||||
extern ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;
|
extern ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;
|
||||||
|
|
||||||
|
@ -670,6 +670,13 @@ static ngx_command_t ngx_http_core_commands[] = {
|
|||||||
offsetof(ngx_http_core_loc_conf_t, etag),
|
offsetof(ngx_http_core_loc_conf_t, etag),
|
||||||
NULL },
|
NULL },
|
||||||
|
|
||||||
|
{ ngx_string("early_hints"),
|
||||||
|
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||||
|
ngx_http_set_predicate_slot,
|
||||||
|
NGX_HTTP_LOC_CONF_OFFSET,
|
||||||
|
offsetof(ngx_http_core_loc_conf_t, early_hints),
|
||||||
|
NULL },
|
||||||
|
|
||||||
{ ngx_string("error_page"),
|
{ ngx_string("error_page"),
|
||||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|
||||||
|NGX_CONF_2MORE,
|
|NGX_CONF_2MORE,
|
||||||
@ -1857,6 +1864,37 @@ ngx_http_send_header(ngx_http_request_t *r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_send_early_hints(ngx_http_request_t *r)
|
||||||
|
{
|
||||||
|
ngx_int_t rc;
|
||||||
|
ngx_http_core_loc_conf_t *clcf;
|
||||||
|
|
||||||
|
if (r->post_action) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r->header_sent) {
|
||||||
|
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
||||||
|
"header already sent");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||||
|
|
||||||
|
rc = ngx_http_test_predicates(r, clcf->early_hints);
|
||||||
|
|
||||||
|
if (rc != NGX_DECLINED) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||||
|
"http send early hints \"%V?%V\"", &r->uri, &r->args);
|
||||||
|
|
||||||
|
return ngx_http_top_early_hints_filter(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ngx_int_t
|
ngx_int_t
|
||||||
ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||||
{
|
{
|
||||||
@ -3637,6 +3675,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf)
|
|||||||
clcf->chunked_transfer_encoding = NGX_CONF_UNSET;
|
clcf->chunked_transfer_encoding = NGX_CONF_UNSET;
|
||||||
clcf->etag = NGX_CONF_UNSET;
|
clcf->etag = NGX_CONF_UNSET;
|
||||||
clcf->server_tokens = NGX_CONF_UNSET_UINT;
|
clcf->server_tokens = NGX_CONF_UNSET_UINT;
|
||||||
|
clcf->early_hints = NGX_CONF_UNSET_PTR;
|
||||||
clcf->types_hash_max_size = NGX_CONF_UNSET_UINT;
|
clcf->types_hash_max_size = NGX_CONF_UNSET_UINT;
|
||||||
clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;
|
clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;
|
||||||
|
|
||||||
@ -3917,6 +3956,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
|||||||
ngx_conf_merge_uint_value(conf->server_tokens, prev->server_tokens,
|
ngx_conf_merge_uint_value(conf->server_tokens, prev->server_tokens,
|
||||||
NGX_HTTP_SERVER_TOKENS_ON);
|
NGX_HTTP_SERVER_TOKENS_ON);
|
||||||
|
|
||||||
|
ngx_conf_merge_ptr_value(conf->early_hints, prev->early_hints, NULL);
|
||||||
|
|
||||||
ngx_conf_merge_ptr_value(conf->open_file_cache,
|
ngx_conf_merge_ptr_value(conf->open_file_cache,
|
||||||
prev->open_file_cache, NULL);
|
prev->open_file_cache, NULL);
|
||||||
|
|
||||||
|
@ -430,6 +430,8 @@ struct ngx_http_core_loc_conf_s {
|
|||||||
ngx_http_complex_value_t *disable_symlinks_from;
|
ngx_http_complex_value_t *disable_symlinks_from;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ngx_array_t *early_hints; /* early_hints */
|
||||||
|
|
||||||
ngx_array_t *error_pages; /* error_page */
|
ngx_array_t *error_pages; /* error_page */
|
||||||
|
|
||||||
ngx_path_t *client_body_temp_path; /* client_body_temp_path */
|
ngx_path_t *client_body_temp_path; /* client_body_temp_path */
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf);
|
static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf);
|
||||||
static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r);
|
static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r);
|
||||||
|
static ngx_int_t ngx_http_early_hints_filter(ngx_http_request_t *r);
|
||||||
|
|
||||||
|
|
||||||
static ngx_http_module_t ngx_http_header_filter_module_ctx = {
|
static ngx_http_module_t ngx_http_header_filter_module_ctx = {
|
||||||
@ -50,6 +51,9 @@ static u_char ngx_http_server_string[] = "Server: nginx" CRLF;
|
|||||||
static u_char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;
|
static u_char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;
|
||||||
static u_char ngx_http_server_build_string[] = "Server: " NGINX_VER_BUILD CRLF;
|
static u_char ngx_http_server_build_string[] = "Server: " NGINX_VER_BUILD CRLF;
|
||||||
|
|
||||||
|
static ngx_str_t ngx_http_early_hints_status_line =
|
||||||
|
ngx_string("HTTP/1.1 103 Early Hints" CRLF);
|
||||||
|
|
||||||
|
|
||||||
static ngx_str_t ngx_http_status_lines[] = {
|
static ngx_str_t ngx_http_status_lines[] = {
|
||||||
|
|
||||||
@ -625,10 +629,113 @@ ngx_http_header_filter(ngx_http_request_t *r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_http_early_hints_filter(ngx_http_request_t *r)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
ngx_buf_t *b;
|
||||||
|
ngx_uint_t i;
|
||||||
|
ngx_chain_t out;
|
||||||
|
ngx_list_part_t *part;
|
||||||
|
ngx_table_elt_t *header;
|
||||||
|
|
||||||
|
if (r != r->main) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r->http_version < NGX_HTTP_VERSION_11) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
part = &r->headers_out.headers.part;
|
||||||
|
header = part->elts;
|
||||||
|
|
||||||
|
for (i = 0; /* void */; i++) {
|
||||||
|
|
||||||
|
if (i >= part->nelts) {
|
||||||
|
if (part->next == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
part = part->next;
|
||||||
|
header = part->elts;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header[i].hash == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len
|
||||||
|
+ sizeof(CRLF) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += ngx_http_early_hints_status_line.len
|
||||||
|
/* the end of the early hints */
|
||||||
|
+ sizeof(CRLF) - 1;
|
||||||
|
|
||||||
|
b = ngx_create_temp_buf(r->pool, len);
|
||||||
|
if (b == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
b->last = ngx_copy(b->last, ngx_http_early_hints_status_line.data,
|
||||||
|
ngx_http_early_hints_status_line.len);
|
||||||
|
|
||||||
|
part = &r->headers_out.headers.part;
|
||||||
|
header = part->elts;
|
||||||
|
|
||||||
|
for (i = 0; /* void */; i++) {
|
||||||
|
|
||||||
|
if (i >= part->nelts) {
|
||||||
|
if (part->next == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
part = part->next;
|
||||||
|
header = part->elts;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header[i].hash == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
|
||||||
|
*b->last++ = ':'; *b->last++ = ' ';
|
||||||
|
|
||||||
|
b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
|
||||||
|
*b->last++ = CR; *b->last++ = LF;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||||
|
"%*s", (size_t) (b->last - b->pos), b->pos);
|
||||||
|
|
||||||
|
/* the end of HTTP early hints */
|
||||||
|
*b->last++ = CR; *b->last++ = LF;
|
||||||
|
|
||||||
|
r->header_size = b->last - b->pos;
|
||||||
|
|
||||||
|
b->flush = 1;
|
||||||
|
|
||||||
|
out.buf = b;
|
||||||
|
out.next = NULL;
|
||||||
|
|
||||||
|
return ngx_http_write_filter(r, &out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_http_header_filter_init(ngx_conf_t *cf)
|
ngx_http_header_filter_init(ngx_conf_t *cf)
|
||||||
{
|
{
|
||||||
ngx_http_top_header_filter = ngx_http_header_filter;
|
ngx_http_top_header_filter = ngx_http_header_filter;
|
||||||
|
ngx_http_top_early_hints_filter = ngx_http_early_hints_filter;
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@
|
|||||||
#define NGX_HTTP_CONTINUE 100
|
#define NGX_HTTP_CONTINUE 100
|
||||||
#define NGX_HTTP_SWITCHING_PROTOCOLS 101
|
#define NGX_HTTP_SWITCHING_PROTOCOLS 101
|
||||||
#define NGX_HTTP_PROCESSING 102
|
#define NGX_HTTP_PROCESSING 102
|
||||||
|
#define NGX_HTTP_EARLY_HINTS 103
|
||||||
|
|
||||||
#define NGX_HTTP_OK 200
|
#define NGX_HTTP_OK 200
|
||||||
#define NGX_HTTP_CREATED 201
|
#define NGX_HTTP_CREATED 201
|
||||||
|
@ -48,6 +48,9 @@ static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
|
|||||||
static void ngx_http_upstream_read_request_handler(ngx_http_request_t *r);
|
static void ngx_http_upstream_read_request_handler(ngx_http_request_t *r);
|
||||||
static void ngx_http_upstream_process_header(ngx_http_request_t *r,
|
static void ngx_http_upstream_process_header(ngx_http_request_t *r,
|
||||||
ngx_http_upstream_t *u);
|
ngx_http_upstream_t *u);
|
||||||
|
static ngx_int_t ngx_http_upstream_process_early_hints(ngx_http_request_t *r,
|
||||||
|
ngx_http_upstream_t *u);
|
||||||
|
static void ngx_http_upstream_early_hints_writer(ngx_http_request_t *r);
|
||||||
static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,
|
static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,
|
||||||
ngx_http_upstream_t *u);
|
ngx_http_upstream_t *u);
|
||||||
static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
|
static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
|
||||||
@ -2530,6 +2533,20 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rc == NGX_OK
|
||||||
|
&& u->headers_in.status_n == NGX_HTTP_EARLY_HINTS)
|
||||||
|
{
|
||||||
|
rc = ngx_http_upstream_process_early_hints(r, u);
|
||||||
|
|
||||||
|
if (rc == NGX_OK) {
|
||||||
|
rc = u->process_header(r);
|
||||||
|
|
||||||
|
if (rc == NGX_AGAIN) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2567,6 +2584,152 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_http_upstream_process_early_hints(ngx_http_request_t *r,
|
||||||
|
ngx_http_upstream_t *u)
|
||||||
|
{
|
||||||
|
u_char *p;
|
||||||
|
ngx_uint_t i;
|
||||||
|
ngx_list_part_t *part;
|
||||||
|
ngx_table_elt_t *h, *ho;
|
||||||
|
ngx_connection_t *c;
|
||||||
|
|
||||||
|
c = r->connection;
|
||||||
|
|
||||||
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream early hints");
|
||||||
|
|
||||||
|
if (u->conf->pass_early_hints) {
|
||||||
|
|
||||||
|
u->early_hints_length += u->buffer.pos - u->buffer.start;
|
||||||
|
|
||||||
|
if (u->early_hints_length <= (off_t) u->conf->buffer_size) {
|
||||||
|
|
||||||
|
part = &u->headers_in.headers.part;
|
||||||
|
h = part->elts;
|
||||||
|
|
||||||
|
for (i = 0; /* void */; i++) {
|
||||||
|
|
||||||
|
if (i >= part->nelts) {
|
||||||
|
if (part->next == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
part = part->next;
|
||||||
|
h = part->elts;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
|
||||||
|
h[i].lowcase_key, h[i].key.len))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ho = ngx_list_push(&r->headers_out.headers);
|
||||||
|
if (ho == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ho = h[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngx_http_send_early_hints(r) == NGX_ERROR) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->buffered) {
|
||||||
|
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->write_event_handler = ngx_http_upstream_early_hints_writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||||
|
"upstream sent too big early hints");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u->reinit_request(r) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_http_clean_header(r);
|
||||||
|
|
||||||
|
ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
|
||||||
|
u->headers_in.content_length_n = -1;
|
||||||
|
u->headers_in.last_modified_time = -1;
|
||||||
|
|
||||||
|
if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
|
||||||
|
sizeof(ngx_table_elt_t))
|
||||||
|
!= NGX_OK)
|
||||||
|
{
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
|
||||||
|
sizeof(ngx_table_elt_t))
|
||||||
|
!= NGX_OK)
|
||||||
|
{
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = u->buffer.pos;
|
||||||
|
|
||||||
|
u->buffer.pos = u->buffer.start;
|
||||||
|
|
||||||
|
#if (NGX_HTTP_CACHE)
|
||||||
|
|
||||||
|
if (r->cache) {
|
||||||
|
u->buffer.pos += r->cache->header_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
u->buffer.last = ngx_movemem(u->buffer.pos, p, u->buffer.last - p);
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ngx_http_upstream_early_hints_writer(ngx_http_request_t *r)
|
||||||
|
{
|
||||||
|
ngx_connection_t *c;
|
||||||
|
ngx_http_upstream_t *u;
|
||||||
|
|
||||||
|
c = r->connection;
|
||||||
|
u = r->upstream;
|
||||||
|
|
||||||
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||||
|
"http upstream early hints writer");
|
||||||
|
|
||||||
|
c->log->action = "sending early hints to client";
|
||||||
|
|
||||||
|
if (ngx_http_write_filter(r, NULL) == NGX_ERROR) {
|
||||||
|
ngx_http_upstream_finalize_request(r, u,
|
||||||
|
NGX_HTTP_INTERNAL_SERVER_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->buffered) {
|
||||||
|
if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
|
||||||
|
r->write_event_handler =
|
||||||
|
ngx_http_upstream_wr_check_broken_connection;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
r->write_event_handler = ngx_http_request_empty_handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
|
||||||
|
ngx_http_upstream_finalize_request(r, u,
|
||||||
|
NGX_HTTP_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)
|
ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)
|
||||||
{
|
{
|
||||||
|
@ -185,6 +185,7 @@ typedef struct {
|
|||||||
ngx_flag_t pass_request_headers;
|
ngx_flag_t pass_request_headers;
|
||||||
ngx_flag_t pass_request_body;
|
ngx_flag_t pass_request_body;
|
||||||
ngx_flag_t pass_trailers;
|
ngx_flag_t pass_trailers;
|
||||||
|
ngx_flag_t pass_early_hints;
|
||||||
|
|
||||||
ngx_flag_t ignore_client_abort;
|
ngx_flag_t ignore_client_abort;
|
||||||
ngx_flag_t intercept_errors;
|
ngx_flag_t intercept_errors;
|
||||||
@ -354,6 +355,7 @@ struct ngx_http_upstream_s {
|
|||||||
|
|
||||||
ngx_buf_t buffer;
|
ngx_buf_t buffer;
|
||||||
off_t length;
|
off_t length;
|
||||||
|
off_t early_hints_length;
|
||||||
|
|
||||||
ngx_chain_t *out_bufs;
|
ngx_chain_t *out_bufs;
|
||||||
ngx_chain_t *busy_bufs;
|
ngx_chain_t *busy_bufs;
|
||||||
|
@ -213,6 +213,7 @@ struct ngx_http_v2_stream_s {
|
|||||||
|
|
||||||
ngx_pool_t *pool;
|
ngx_pool_t *pool;
|
||||||
|
|
||||||
|
unsigned initialized:1;
|
||||||
unsigned waiting:1;
|
unsigned waiting:1;
|
||||||
unsigned blocked:1;
|
unsigned blocked:1;
|
||||||
unsigned exhausted:1;
|
unsigned exhausted:1;
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
|
|
||||||
static ngx_int_t ngx_http_v2_header_filter(ngx_http_request_t *r);
|
static ngx_int_t ngx_http_v2_header_filter(ngx_http_request_t *r);
|
||||||
|
static ngx_int_t ngx_http_v2_early_hints_filter(ngx_http_request_t *r);
|
||||||
|
static ngx_int_t ngx_http_v2_init_stream(ngx_http_request_t *r);
|
||||||
|
|
||||||
static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame(
|
static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame(
|
||||||
ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin);
|
ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin);
|
||||||
@ -97,6 +99,7 @@ ngx_module_t ngx_http_v2_filter_module = {
|
|||||||
|
|
||||||
|
|
||||||
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
|
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
|
||||||
|
static ngx_http_output_header_filter_pt ngx_http_next_early_hints_filter;
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
@ -109,7 +112,6 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
|
|||||||
ngx_list_part_t *part;
|
ngx_list_part_t *part;
|
||||||
ngx_table_elt_t *header;
|
ngx_table_elt_t *header;
|
||||||
ngx_connection_t *fc;
|
ngx_connection_t *fc;
|
||||||
ngx_http_cleanup_t *cln;
|
|
||||||
ngx_http_v2_stream_t *stream;
|
ngx_http_v2_stream_t *stream;
|
||||||
ngx_http_v2_out_frame_t *frame;
|
ngx_http_v2_out_frame_t *frame;
|
||||||
ngx_http_v2_connection_t *h2c;
|
ngx_http_v2_connection_t *h2c;
|
||||||
@ -614,7 +616,196 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
|
|||||||
|
|
||||||
ngx_http_v2_queue_blocked_frame(h2c, frame);
|
ngx_http_v2_queue_blocked_frame(h2c, frame);
|
||||||
|
|
||||||
stream->queued = 1;
|
stream->queued++;
|
||||||
|
|
||||||
|
if (ngx_http_v2_init_stream(r) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ngx_http_v2_filter_send(fc, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_http_v2_early_hints_filter(ngx_http_request_t *r)
|
||||||
|
{
|
||||||
|
u_char *pos, *start, *tmp;
|
||||||
|
size_t len, tmp_len;
|
||||||
|
ngx_uint_t i;
|
||||||
|
ngx_list_part_t *part;
|
||||||
|
ngx_table_elt_t *header;
|
||||||
|
ngx_connection_t *fc;
|
||||||
|
ngx_http_v2_stream_t *stream;
|
||||||
|
ngx_http_v2_out_frame_t *frame;
|
||||||
|
ngx_http_v2_connection_t *h2c;
|
||||||
|
|
||||||
|
stream = r->stream;
|
||||||
|
|
||||||
|
if (!stream) {
|
||||||
|
return ngx_http_next_early_hints_filter(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r != r->main) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
fc = r->connection;
|
||||||
|
|
||||||
|
if (fc->error) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
tmp_len = 0;
|
||||||
|
|
||||||
|
part = &r->headers_out.headers.part;
|
||||||
|
header = part->elts;
|
||||||
|
|
||||||
|
for (i = 0; /* void */; i++) {
|
||||||
|
|
||||||
|
if (i >= part->nelts) {
|
||||||
|
if (part->next == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
part = part->next;
|
||||||
|
header = part->elts;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header[i].hash == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
|
||||||
|
ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
|
||||||
|
"too long response header name: \"%V\"",
|
||||||
|
&header[i].key);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
|
||||||
|
ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
|
||||||
|
"too long response header value: \"%V: %V\"",
|
||||||
|
&header[i].key, &header[i].value);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
|
||||||
|
+ NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
|
||||||
|
|
||||||
|
if (header[i].key.len > tmp_len) {
|
||||||
|
tmp_len = header[i].key.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header[i].value.len > tmp_len) {
|
||||||
|
tmp_len = header[i].value.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2c = stream->connection;
|
||||||
|
|
||||||
|
len += h2c->table_update ? 1 : 0;
|
||||||
|
len += 1 + ngx_http_v2_literal_size("418");
|
||||||
|
|
||||||
|
tmp = ngx_palloc(r->pool, tmp_len);
|
||||||
|
pos = ngx_pnalloc(r->pool, len);
|
||||||
|
|
||||||
|
if (pos == NULL || tmp == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = pos;
|
||||||
|
|
||||||
|
if (h2c->table_update) {
|
||||||
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||||||
|
"http2 table size update: 0");
|
||||||
|
*pos++ = (1 << 5) | 0;
|
||||||
|
h2c->table_update = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||||||
|
"http2 output header: \":status: %03ui\"",
|
||||||
|
(ngx_uint_t) NGX_HTTP_EARLY_HINTS);
|
||||||
|
|
||||||
|
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);
|
||||||
|
*pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;
|
||||||
|
pos = ngx_sprintf(pos, "%03ui", (ngx_uint_t) NGX_HTTP_EARLY_HINTS);
|
||||||
|
|
||||||
|
part = &r->headers_out.headers.part;
|
||||||
|
header = part->elts;
|
||||||
|
|
||||||
|
for (i = 0; /* void */; i++) {
|
||||||
|
|
||||||
|
if (i >= part->nelts) {
|
||||||
|
if (part->next == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
part = part->next;
|
||||||
|
header = part->elts;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header[i].hash == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (NGX_DEBUG)
|
||||||
|
if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
|
||||||
|
ngx_strlow(tmp, header[i].key.data, header[i].key.len);
|
||||||
|
|
||||||
|
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||||||
|
"http2 output header: \"%*s: %V\"",
|
||||||
|
header[i].key.len, tmp, &header[i].value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*pos++ = 0;
|
||||||
|
|
||||||
|
pos = ngx_http_v2_write_name(pos, header[i].key.data,
|
||||||
|
header[i].key.len, tmp);
|
||||||
|
|
||||||
|
pos = ngx_http_v2_write_value(pos, header[i].value.data,
|
||||||
|
header[i].value.len, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
frame = ngx_http_v2_create_headers_frame(r, start, pos, 0);
|
||||||
|
if (frame == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_http_v2_queue_blocked_frame(h2c, frame);
|
||||||
|
|
||||||
|
stream->queued++;
|
||||||
|
|
||||||
|
if (ngx_http_v2_init_stream(r) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ngx_http_v2_filter_send(fc, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_http_v2_init_stream(ngx_http_request_t *r)
|
||||||
|
{
|
||||||
|
ngx_connection_t *fc;
|
||||||
|
ngx_http_cleanup_t *cln;
|
||||||
|
ngx_http_v2_stream_t *stream;
|
||||||
|
|
||||||
|
stream = r->stream;
|
||||||
|
fc = r->connection;
|
||||||
|
|
||||||
|
if (stream->initialized) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->initialized = 1;
|
||||||
|
|
||||||
cln = ngx_http_cleanup_add(r, 0);
|
cln = ngx_http_cleanup_add(r, 0);
|
||||||
if (cln == NULL) {
|
if (cln == NULL) {
|
||||||
@ -628,7 +819,7 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
|
|||||||
fc->need_last_buf = 1;
|
fc->need_last_buf = 1;
|
||||||
fc->need_flush_buf = 1;
|
fc->need_flush_buf = 1;
|
||||||
|
|
||||||
return ngx_http_v2_filter_send(fc, stream);
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1569,5 +1760,8 @@ ngx_http_v2_filter_init(ngx_conf_t *cf)
|
|||||||
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
||||||
ngx_http_top_header_filter = ngx_http_v2_header_filter;
|
ngx_http_top_header_filter = ngx_http_v2_header_filter;
|
||||||
|
|
||||||
|
ngx_http_next_early_hints_filter = ngx_http_top_early_hints_filter;
|
||||||
|
ngx_http_top_early_hints_filter = ngx_http_v2_early_hints_filter;
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ typedef struct {
|
|||||||
|
|
||||||
|
|
||||||
static ngx_int_t ngx_http_v3_header_filter(ngx_http_request_t *r);
|
static ngx_int_t ngx_http_v3_header_filter(ngx_http_request_t *r);
|
||||||
|
static ngx_int_t ngx_http_v3_early_hints_filter(ngx_http_request_t *r);
|
||||||
static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r,
|
static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r,
|
||||||
ngx_chain_t *in);
|
ngx_chain_t *in);
|
||||||
static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r,
|
static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r,
|
||||||
@ -75,6 +76,7 @@ ngx_module_t ngx_http_v3_filter_module = {
|
|||||||
|
|
||||||
|
|
||||||
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
|
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
|
||||||
|
static ngx_http_output_header_filter_pt ngx_http_next_early_hints_filter;
|
||||||
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
|
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
|
||||||
|
|
||||||
|
|
||||||
@ -588,6 +590,154 @@ ngx_http_v3_header_filter(ngx_http_request_t *r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_http_v3_early_hints_filter(ngx_http_request_t *r)
|
||||||
|
{
|
||||||
|
size_t len, n;
|
||||||
|
ngx_buf_t *b;
|
||||||
|
ngx_uint_t i;
|
||||||
|
ngx_chain_t *out, *hl, *cl;
|
||||||
|
ngx_list_part_t *part;
|
||||||
|
ngx_table_elt_t *header;
|
||||||
|
ngx_http_v3_session_t *h3c;
|
||||||
|
|
||||||
|
if (r->http_version != NGX_HTTP_VERSION_30) {
|
||||||
|
return ngx_http_next_early_hints_filter(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r != r->main) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
part = &r->headers_out.headers.part;
|
||||||
|
header = part->elts;
|
||||||
|
|
||||||
|
for (i = 0; /* void */; i++) {
|
||||||
|
|
||||||
|
if (i >= part->nelts) {
|
||||||
|
if (part->next == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
part = part->next;
|
||||||
|
header = part->elts;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header[i].hash == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += ngx_http_v3_encode_field_l(NULL, &header[i].key,
|
||||||
|
&header[i].value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0);
|
||||||
|
|
||||||
|
len += ngx_http_v3_encode_field_lri(NULL, 0,
|
||||||
|
NGX_HTTP_V3_HEADER_STATUS_200,
|
||||||
|
NULL, 3);
|
||||||
|
|
||||||
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||||
|
"http3 header len:%uz", len);
|
||||||
|
|
||||||
|
b = ngx_create_temp_buf(r->pool, len);
|
||||||
|
if (b == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last,
|
||||||
|
0, 0, 0);
|
||||||
|
|
||||||
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||||
|
"http3 output header: \":status: %03ui\"",
|
||||||
|
(ngx_uint_t) NGX_HTTP_EARLY_HINTS);
|
||||||
|
|
||||||
|
b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,
|
||||||
|
NGX_HTTP_V3_HEADER_STATUS_200,
|
||||||
|
NULL, 3);
|
||||||
|
b->last = ngx_sprintf(b->last, "%03ui", (ngx_uint_t) NGX_HTTP_EARLY_HINTS);
|
||||||
|
|
||||||
|
part = &r->headers_out.headers.part;
|
||||||
|
header = part->elts;
|
||||||
|
|
||||||
|
for (i = 0; /* void */; i++) {
|
||||||
|
|
||||||
|
if (i >= part->nelts) {
|
||||||
|
if (part->next == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
part = part->next;
|
||||||
|
header = part->elts;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header[i].hash == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||||
|
"http3 output header: \"%V: %V\"",
|
||||||
|
&header[i].key, &header[i].value);
|
||||||
|
|
||||||
|
b->last = (u_char *) ngx_http_v3_encode_field_l(b->last,
|
||||||
|
&header[i].key,
|
||||||
|
&header[i].value);
|
||||||
|
}
|
||||||
|
|
||||||
|
b->flush = 1;
|
||||||
|
|
||||||
|
cl = ngx_alloc_chain_link(r->pool);
|
||||||
|
if (cl == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
cl->buf = b;
|
||||||
|
cl->next = NULL;
|
||||||
|
|
||||||
|
n = b->last - b->pos;
|
||||||
|
|
||||||
|
h3c = ngx_http_v3_get_session(r->connection);
|
||||||
|
h3c->payload_bytes += n;
|
||||||
|
|
||||||
|
len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS)
|
||||||
|
+ ngx_http_v3_encode_varlen_int(NULL, n);
|
||||||
|
|
||||||
|
b = ngx_create_temp_buf(r->pool, len);
|
||||||
|
if (b == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last,
|
||||||
|
NGX_HTTP_V3_FRAME_HEADERS);
|
||||||
|
b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n);
|
||||||
|
|
||||||
|
hl = ngx_alloc_chain_link(r->pool);
|
||||||
|
if (hl == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
hl->buf = b;
|
||||||
|
hl->next = cl;
|
||||||
|
|
||||||
|
out = hl;
|
||||||
|
|
||||||
|
for (cl = out; cl; cl = cl->next) {
|
||||||
|
h3c->total_bytes += cl->buf->last - cl->buf->pos;
|
||||||
|
r->header_size += cl->buf->last - cl->buf->pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ngx_http_write_filter(r, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||||
{
|
{
|
||||||
@ -845,6 +995,9 @@ ngx_http_v3_filter_init(ngx_conf_t *cf)
|
|||||||
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
||||||
ngx_http_top_header_filter = ngx_http_v3_header_filter;
|
ngx_http_top_header_filter = ngx_http_v3_header_filter;
|
||||||
|
|
||||||
|
ngx_http_next_early_hints_filter = ngx_http_top_early_hints_filter;
|
||||||
|
ngx_http_top_early_hints_filter = ngx_http_v3_early_hints_filter;
|
||||||
|
|
||||||
ngx_http_next_body_filter = ngx_http_top_body_filter;
|
ngx_http_next_body_filter = ngx_http_top_body_filter;
|
||||||
ngx_http_top_body_filter = ngx_http_v3_body_filter;
|
ngx_http_top_body_filter = ngx_http_v3_body_filter;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user