[fix][broker][proxy] Add httpMaxResponseHeaderSize config and preemptive header overflow check - #26255
Open
geniusjoe wants to merge 1 commit into
Open
[fix][broker][proxy] Add httpMaxResponseHeaderSize config and preemptive header overflow check#26255geniusjoe wants to merge 1 commit into
geniusjoe wants to merge 1 commit into
Conversation
…ive response header overflow check
geniusjoe
force-pushed
the
dev/jetty-max-header-size
branch
from
July 31, 2026 06:27
4b944c2 to
cfa319f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #26229
Motivation
When a message contains properties that cause the HTTP response header size to exceed Jetty's
HttpConfiguration.responseHeaderSize(default 8KB), the Admin API endpointsgetMessageById/getMessagesById,peekNthMessage, andexamineMessagefail silently. Since message metadata is placed in response headers likeX-Pulsar-PROPERTY-*, the 8KB default limit is relatively easy to hit — for example, a message with a few dozen properties or several long property values can exceed it.When the header size exceeds the limit, Jetty cannot write any valid HTTP response and directly resets/closes the connection. This causes two problems:
java.util.logging) instead ofpulsar.log, because Jersey catches theBadMessageException: 500: Response header too largeinServerRuntime$Responder.writeResponseand logs it at SEVERE level via JUL, which goes to stdout. This makes the issue hard to diagnose.PulsarAdminExceptionwithstatusCode=500wrapping aTimeoutException, with no indication that the root cause is header overflow. The 500 status code is misleading — it does not mean the server returned HTTP 500; it is the default error code assigned client-side when no HTTP response was received.See the detailed root cause analysis in #26229 for the full call chain.
Modifications
httpMaxResponseHeaderSizeconfiguration to bothServiceConfiguration(broker) andProxyConfiguration(proxy), with a default value of 8192 bytes (matching Jetty's default). Apply the config to Jetty'sHttpConfiguration.responseHeaderSizeinWebService(broker) andWebServer(proxy).PersistentTopicsBase.generateResponseWithEntry(), which is the shared response generation path forgetMessageById/getMessagesById,peekNthMessage, andexamineMessage:Response.getStringHeaders().httpMaxResponseHeaderSize, throw aRestExceptionwith HTTP 431 (Request Header Fields Too Large) and a descriptive error message that includes both the estimated size and the configured limit, and suggests increasinghttpMaxResponseHeaderSize.pulsar.logwith structured attributes (topic, ledgerId, entryId, estimatedHeaderSize, httpMaxResponseHeaderSize).RestExceptionpropagation in callback handlers to prevent 431 being wrapped as 500:internalGetMessageById(readEntryComplete): CatchRestExceptionseparately beforecatch (Exception).internalPeekNthMessageAsync: Addcatch (RestException)to the lambda.internalExamineMessageAsync(generateResponseWithEntrycall): Addcatch (RestException)beforecatch (IOException).generateResponseWithEntryto build headers into aheaderOnlyResponsefirst, estimate the size, then construct the final response withResponse.fromResponse(headerOnlyResponse).entity(stream).build().broker.confandproxy.confentries for the new configuration.Verifying this change
This change added tests and can be verified as follows:
testGetMessageByIdHeaderTooLarge431: Verifies thatgetMessageByIdreturns HTTP 431 with a descriptive error message containing "exceeds HTTP response header limit" and "httpMaxResponseHeaderSize" when the response header exceeds the configured limit.testGetMessageByIdHeaderWithinLimit: Verifies thatgetMessageByIdworks normally when the response header is within the default 8KB limit (50 properties).Does this pull request potentially affect one of the following parts:
httpMaxResponseHeaderSizeis added to both broker and proxy with a default value of 8192 bytes, which matches Jetty's existing implicitresponseHeaderSizedefault. This does not change any existing default behavior.getMessageById/getMessagesById,peekNthMessage,examineMessagenow return 431 instead of 500 when response header size exceeds the limit