Skip to content

Upstream: fixed reinit request with gRPC and Early Hints. #750

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 1 commit into from
Jun 23, 2025

Conversation

pluknet
Copy link
Contributor

@pluknet pluknet commented Jun 23, 2025

The gRPC module context has connection specific state, which can be lost
after request reinitialization when it comes to processing early hints.

The fix is to do only a portion of u->reinit_request() implementation
required after processing early hints, now inlined in modules.

Now NGX_HTTP_UPSTREAM_EARLY_HINTS is returned from u->process_header()
for early hints. When reading a cached response, this code is mapped
to NGX_HTTP_UPSTREAM_INVALID_HEADER to indicate invalid header format.

@pluknet pluknet added this to the nginx-1.29.0 milestone Jun 23, 2025
@pluknet pluknet requested a review from arut June 23, 2025 11:28
@pluknet pluknet self-assigned this Jun 23, 2025
@pluknet pluknet added the bug label Jun 23, 2025
@@ -1986,6 +1986,10 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
"http proxy header done");

if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
if (ngx_http_proxy_reinit_request(r) != NGX_OK) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we already process early hints separately, it's probably better to do the necessary part of ngx_http_proxy_reinit_request() here instead of calling it.

Comment on lines 1931 to 1940
if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
ctx->status = 0;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here there's no return, which means the entire block below will be executed for early hints, which seems wrong. However we still need to handle the end stream case for eary hints.

@arut
Copy link
Contributor

arut commented Jun 23, 2025

Here's my take:

diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
index d74b17708..4c95525b4 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -1928,6 +1928,17 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
                 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                                "grpc header done");
 
+                if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
+                    if (ctx->end_stream) {
+                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                      "upstream prematurely closed stream");
+                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+                    }
+
+                    ctx->status = 0;
+                    return NGX_HTTP_UPSTREAM_EARLY_HINTS;
+                }
+
                 if (ctx->end_stream) {
                     u->headers_in.content_length_n = 0;
 
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index ff7460e44..8d5385c1d 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -1985,8 +1985,18 @@ 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");
 
+            ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
             if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
-                return NGX_OK;
+                ctx->status.code = 0;
+                ctx->status.count = 0;
+                ctx->status.start = NULL;
+                ctx->status.end = NULL;
+
+                r->upstream->process_header =
+                                            ngx_http_proxy_process_status_line;
+                r->state = 0;
+                return NGX_HTTP_UPSTREAM_EARLY_HINTS;
             }
 
             /*
@@ -2036,8 +2046,6 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
              * connections alive in case of r->header_only or X-Accel-Redirect
              */
 
-            ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
-
             if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
                 || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
                 || ctx->head
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index d1bcdbbe0..de0f92a4f 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -1123,7 +1123,7 @@ ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)
         return NGX_ERROR;
     }
 
-    if (rc == NGX_AGAIN) {
+    if (rc == NGX_AGAIN || rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) {
         rc = NGX_HTTP_UPSTREAM_INVALID_HEADER;
     }
 
@@ -2533,9 +2533,7 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
             continue;
         }
 
-        if (rc == NGX_OK
-            && u->headers_in.status_n == NGX_HTTP_EARLY_HINTS)
-        {
+        if (rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) {
             rc = ngx_http_upstream_process_early_hints(r, u);
 
             if (rc == NGX_OK) {
@@ -2651,10 +2649,6 @@ ngx_http_upstream_process_early_hints(ngx_http_request_t *r,
         }
     }
 
-    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));
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index 7a47675eb..f3e9f7979 100644
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -43,6 +43,7 @@
                                              |NGX_HTTP_UPSTREAM_FT_HTTP_429)
 
 #define NGX_HTTP_UPSTREAM_INVALID_HEADER     40
+#define NGX_HTTP_UPSTREAM_EARLY_HINTS        41
 
 
 #define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT    0x00000002

The gRPC module context has connection specific state, which can be lost
after request reinitialization when it comes to processing early hints.

The fix is to do only a portion of u->reinit_request() implementation
required after processing early hints, now inlined in modules.

Now NGX_HTTP_UPSTREAM_EARLY_HINTS is returned from u->process_header()
for early hints.  When reading a cached response, this code is mapped
to NGX_HTTP_UPSTREAM_INVALID_HEADER to indicate invalid header format.
@pluknet pluknet force-pushed the early-hints-grpc-fixup branch from 722a1b9 to 80276b6 Compare June 23, 2025 14:52
Copy link
Contributor

@arut arut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok.

@pluknet pluknet merged commit cdf7a9c into nginx:master Jun 23, 2025
1 check passed
@pluknet pluknet deleted the early-hints-grpc-fixup branch June 23, 2025 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy