This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/pulsar.git

commit 6f3c88ad322996cb80e04c85291d1219548befbe
Author: Lari Hotari <[email protected]>
AuthorDate: Wed Jun 3 11:54:30 2026 +0300

    [fix][proxy] Avoid intermittent 502 when admin proxy follows a broker 
redirect for a request with a body (#25919)
    
    (cherry picked from commit 2acee322ef8ace78759677b997a57be454ed2eca)
---
 .../pulsar/proxy/server/AdminProxyHandler.java     | 31 +++++++++++++++++++++-
 1 file changed, 30 insertions(+), 1 deletion(-)

diff --git 
a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/AdminProxyHandler.java
 
b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/AdminProxyHandler.java
index 7992cd20d11..ba49714b89b 100644
--- 
a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/AdminProxyHandler.java
+++ 
b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/AdminProxyHandler.java
@@ -46,6 +46,7 @@ import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.ProtocolHandlers;
 import org.eclipse.jetty.client.RedirectProtocolHandler;
 import org.eclipse.jetty.client.Request;
+import org.eclipse.jetty.client.Response;
 import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP;
 import org.eclipse.jetty.ee8.proxy.ProxyServlet;
 import org.eclipse.jetty.http.HttpField;
@@ -121,7 +122,7 @@ class AdminProxyHandler extends ProxyServlet {
 
         ProtocolHandlers protocolHandlers = httpClient.getProtocolHandlers();
         if (protocolHandlers != null) {
-            protocolHandlers.put(new RedirectProtocolHandler(httpClient));
+            protocolHandlers.put(new 
NonAbortingRedirectProtocolHandler(httpClient));
         }
 
         httpClient.setIdleTimeout(config.getHttpProxyIdleTimeout());
@@ -129,6 +130,34 @@ class AdminProxyHandler extends ProxyServlet {
         setTimeout(config.getHttpProxyTimeout());
     }
 
+    /**
+     * A {@link RedirectProtocolHandler} that does not abort the in-flight 
request when a redirect
+     * response is received.
+     *
+     * <p>Jetty's default {@link RedirectProtocolHandler#onSuccess(Response)} 
aborts a request that
+     * still has a body to send when a redirect status is received, raising
+     * {@code HttpRequestException: "Aborting request after receiving a NNN 
response"}. When a broker
+     * returns a 307 (to redirect an admin request to the bundle-owner broker) 
before the proxy has
+     * finished streaming the request body, that abort can race ahead of the 
redirect continuation in
+     * {@link RedirectProtocolHandler#onComplete} and surface to the proxy as 
a spurious HTTP 502 Bad
+     * Gateway. The redirect itself is driven by {@code onComplete} from the 
response (its status and
+     * {@code Location} header) and does not depend on the abort, so skipping 
it lets the redirect
+     * always be followed on a fresh request (with the body replayed by
+     * {@link ReplayableProxyContentProvider}), which is the behavior this 
proxy needs.
+     */
+    static class NonAbortingRedirectProtocolHandler extends 
RedirectProtocolHandler {
+        NonAbortingRedirectProtocolHandler(HttpClient client) {
+            super(client);
+        }
+
+        @Override
+        public void onSuccess(Response response) {
+            // Intentionally do NOT abort the request here. The redirect is 
followed in onComplete();
+            // aborting the in-flight request only races a 502 to the proxy 
when the broker returns a
+            // redirect before the request body has finished sending.
+        }
+    }
+
     // This class allows the request body to be replayed, the default 
implementation
     // does not
     protected class ReplayableProxyContentProvider extends 
ProxyInputStreamRequestContent {

Reply via email to