8000 HTTP Early Hints by arut · Pull Request #326 · nginx/nginx · GitHub
[go: up one dir, main page]

Skip to content

HTTP Early Hints #326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations < 8000 div class="select-menu-blankslate select-menu-error"> Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/http/modules/ngx_http_grpc_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -1869,7 +1869,8 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
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,
"upstream sent unexpected :status \"%V\"",
status_line);
Expand Down Expand Up @@ -1902,6 +1903,10 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
h->lowcase_key = h->key.data;
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,
h->lowcase_key, h->key.len);

Expand Down Expand Up @@ -4413,6 +4418,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
conf->upstream.pass_request_body = 1;
conf->upstream.force_ranges = 0;
conf->upstream.pass_trailers = 1;
conf->upstream.pass_early_hints = 1;
conf->upstream.preserve_output = 1;

conf->headers_source = NGX_CONF_UNSET_PTR;
Expand Down
27 changes: 21 additions & 6 deletions src/http/modules/ngx_http_proxy_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

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;
}

Expand Down Expand Up @@ -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_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,
h->lowcase_key, h->key.len);

Expand All @@ -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;
}

Expand All @@ -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,
"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,
* then add the special empty headers
Expand Down Expand Up @@ -3628,10 +3643,10 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
#endif

/* "proxy_cyclic_temp_file" is disabled */
/* the hardcoded values */
conf->upstream.cyclic_temp_file = 0;

conf->upstream.change_buffering = 1;
conf->upstream.pass_early_hints = 1;

conf->headers_source = NGX_CONF_UNSET_PTR;

Expand Down
1 change: 1 addition & 0 deletions src/http/ngx_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_early_hints_filter;
ngx_http_output_body_filter_pt ngx_http_top_body_filter;
ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;

Expand Down
2 changes: 2 additions & 0 deletions src/http/ngx_http.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_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 error);
ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
Expand Down Expand Up @@ -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_early_hints_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;

Expand Down
41 changes: 41 additions & 0 deletions src/http/ngx_http_core_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,13 @@ static ngx_command_t ngx_http_core_commands[] = {
offsetof(ngx_http_core_loc_conf_t, etag),
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_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_2MORE,
Expand Down Expand Up @@ -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");
9E88 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_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
Expand Down Expand Up @@ -3637,6 +3675,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf)
clcf->chunked_transfer_encoding = NGX_CONF_UNSET;
clcf->etag = NGX_CONF_UNSET;
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_bucket_size = NGX_CONF_UNSET_UINT;

Expand Down Expand Up @@ -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_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,
prev->open_file_cache, NULL);

Expand Down
2 changes: 2 additions & 0 deletions src/http/ngx_http_core_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ struct ngx_http_core_loc_conf_s {
ngx_http_complex_value_t *disable_symlinks_from;
#endif

ngx_array_t *early_hints; /* early_hints */

ngx_array_t *error_pages; /* error_page */

ngx_path_t *client_body_temp_path; /* client_body_temp_path */
Expand Down
107 changes: 107 additions & 0 deletions src/http/ngx_http_header_filter_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(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 = {
Expand Down Expand Up @@ -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_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[] = {

Expand Down Expand Up @@ -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
ngx_http_header_filter_init(ngx_conf_t *cf)
{
ngx_http_top_header_filter = ngx_http_header_filter;
ngx_http_top_early_hints_filter = ngx_http_early_hints_filter;

return NGX_OK;
}
1 change: 1 addition & 0 deletions src/http/ngx_http_request.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
#define NGX_HTTP_CONTINUE 100
#define NGX_HTTP_SWITCHING_PROTOCOLS 101
#define NGX_HTTP_PROCESSING 102
#define NGX_HTTP_EARLY_HINTS 103

#define NGX_HTTP_OK 200
#define NGX_HTTP_CREATED 201
Expand Down
Loading
0