This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/10.1.x by this push:
new 910d3adc9e Add limit to HTTP response header size in upgrade to
WebSocket
910d3adc9e is described below
commit 910d3adc9efcc5c2ddba4a6ccbbdd62b6484f60f
Author: Mark Thomas <[email protected]>
AuthorDate: Fri Jul 24 17:12:38 2026 +0100
Add limit to HTTP response header size in upgrade to WebSocket
---
java/org/apache/tomcat/websocket/Constants.java | 11 ++++++++++
.../tomcat/websocket/LocalStrings.properties | 1 +
.../tomcat/websocket/WsWebSocketContainer.java | 24 ++++++++++++++++++----
webapps/docs/changelog.xml | 10 +++++++++
webapps/docs/web-socket-howto.xml | 7 +++++++
5 files changed, 49 insertions(+), 4 deletions(-)
diff --git a/java/org/apache/tomcat/websocket/Constants.java
b/java/org/apache/tomcat/websocket/Constants.java
index ca25057797..b82530da22 100644
--- a/java/org/apache/tomcat/websocket/Constants.java
+++ b/java/org/apache/tomcat/websocket/Constants.java
@@ -94,6 +94,17 @@ public class Constants {
* Default I/O timeout in milliseconds for WebSocket client connections.
*/
public static final long IO_TIMEOUT_MS_DEFAULT = 5000;
+ /**
+ * Property name to set to configure the maximum number of bytes that will
be allowed for the HTTP response to a
+ * WebSocket handshake request. The default is {@link
#MAX_HTTP_RESPONSE_HEADER_BYTES_DEFAULT}.
+ */
+ public static final String MAX_HTTP_RESPONSE_HEADER_BYTES_PROPERTY =
+ "org.apache.tomcat.websocket.MAX_HTTP_RESPONSE_HEADER_BYTES";
+ /**
+ * Default maximum number of bytes that will be allowed for the HTTP
response to a
+ * WebSocket handshake request.
+ */
+ public static final long MAX_HTTP_RESPONSE_HEADER_BYTES_DEFAULT = 8192;
// RFC 2068 recommended a limit of 5
// Most browsers have a default limit of 20
diff --git a/java/org/apache/tomcat/websocket/LocalStrings.properties
b/java/org/apache/tomcat/websocket/LocalStrings.properties
index 34fb844a6c..9d2ee88932 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -151,6 +151,7 @@ wsWebSocketContainer.pathWrongScheme=The scheme [{0}] is
not supported. The supp
wsWebSocketContainer.proxyConnectFail=Failed to connect to the configured
Proxy [{0}]. The HTTP response code was [{1}]
wsWebSocketContainer.redirectThreshold=Cyclic Location header [{0}] detected /
reached max number of redirects [{1}] of max [{2}]
wsWebSocketContainer.responseFail=The HTTP upgrade to WebSocket failed but
partial data may have been received: Status Code [{0}], HTTP headers [{1}]
+wsWebSocketContainer.responseHeadersLimit=The HTTP upgrade to WebSocket failed
because the size of the HTTP response headers exceeded the limit
wsWebSocketContainer.sessionCloseFail=Session with ID [{0}] did not close
cleanly
wsWebSocketContainer.shutdown=The web application is stopping
wsWebSocketContainer.sslEngineFail=Unable to create SSLEngine to support
SSL/TLS connections
diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
index cb832674f1..4a80443c64 100644
--- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
@@ -306,6 +306,13 @@ public class WsWebSocketContainer implements
WebSocketContainer, BackgroundProce
Transformation transformation = null;
AsyncChannelWrapper channel = null;
+ long maxHttpResponseHeaderBytes =
Constants.MAX_HTTP_RESPONSE_HEADER_BYTES_DEFAULT;
+ String maxHttpResponseHeaderBytesValue =
+ (String)
userProperties.get(Constants.MAX_HTTP_RESPONSE_HEADER_BYTES_PROPERTY);
+ if (maxHttpResponseHeaderBytesValue != null) {
+ maxHttpResponseHeaderBytes =
Long.parseLong(maxHttpResponseHeaderBytesValue);
+ }
+
try {
// Open the connection
Future<Void> fConnect = socketChannel.connect(sa);
@@ -315,7 +322,7 @@ public class WsWebSocketContainer implements
WebSocketContainer, BackgroundProce
// Proxy CONNECT is clear text
channel = new AsyncChannelWrapperNonSecure(socketChannel);
writeRequest(channel, proxyConnect, timeout);
- HttpResponse httpResponse = processResponse(response, channel,
timeout);
+ HttpResponse httpResponse = processResponse(response, channel,
timeout, maxHttpResponseHeaderBytes);
if (httpResponse.status ==
Constants.PROXY_AUTHENTICATION_REQUIRED) {
return
processAuthenticationChallenge(clientEndpointHolder,
clientEndpointConfiguration,
serverEndpointUri, redirectSet, userProperties,
Method.CONNECT,
@@ -358,7 +365,7 @@ public class WsWebSocketContainer implements
WebSocketContainer, BackgroundProce
}
writeRequest(channel, upgradeRequest, timeout);
- HttpResponse httpResponse = processResponse(response, channel,
timeout);
+ HttpResponse httpResponse = processResponse(response, channel,
timeout, maxHttpResponseHeaderBytes);
// Check maximum permitted redirects
int maxRedirects = Constants.MAX_REDIRECTIONS_DEFAULT;
@@ -854,8 +861,9 @@ public class WsWebSocketContainer implements
WebSocketContainer, BackgroundProce
* @throws DeploymentException if the response status line is not
correctly formatted
* @throws TimeoutException if the response was not read within the
expected timeout
*/
- private HttpResponse processResponse(ByteBuffer response,
AsyncChannelWrapper channel, long timeout)
- throws InterruptedException, ExecutionException,
DeploymentException, EOFException, TimeoutException {
+ private HttpResponse processResponse(ByteBuffer response,
AsyncChannelWrapper channel, long timeout,
+ long maxHttpResponseHeaderBytes) throws InterruptedException,
ExecutionException, DeploymentException,
+ EOFException, TimeoutException {
Map<String,List<String>> headers = new CaseInsensitiveKeyMap<>();
@@ -864,6 +872,7 @@ public class WsWebSocketContainer implements
WebSocketContainer, BackgroundProce
boolean readHeaders = false;
StringBuilder lineBuffer = new StringBuilder();
String line = null;
+ long headerByteCount = 0;
while (!readHeaders) {
// On entering loop buffer will be empty and at the start of a new
// loop the buffer will have been fully read.
@@ -883,6 +892,7 @@ public class WsWebSocketContainer implements
WebSocketContainer, BackgroundProce
throw new EOFException(
sm.getString("wsWebSocketContainer.responseFail",
Integer.toString(status), headers));
}
+ headerByteCount += bytesRead.intValue();
response.flip();
while (response.hasRemaining() && !readHeaders) {
if (readLine(response, lineBuffer)) {
@@ -901,6 +911,12 @@ public class WsWebSocketContainer implements
WebSocketContainer, BackgroundProce
line = null;
}
}
+ if (readHeaders) {
+ headerByteCount -= response.remaining();
+ }
+ if (headerByteCount > maxHttpResponseHeaderBytes) {
+ throw new
DeploymentException(sm.getString("wsWebSocketContainer.responseHeadersLimit"));
+ }
}
return new HttpResponse(status, new WsHandshakeResponse(headers));
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index d717eb17c3..abc18f432d 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -203,6 +203,16 @@
</add>
</changelog>
</subsection>
+ <subsection name="WebSocket">
+ <changelog>
+ <add>
+ Add a limit (defaults to 8KB) on the size of the HTTP response headers
+ accepted during a WebSocket HTTP upgrade. This is configured via the
+ <code>org.apache.tomcat.websocket.MAX_HTTP_RESPONSE_HEADER_BYTES</code>
+ user property. (markt)
+ </add>
+ </changelog>
+ </subsection>
</section>
<section name="Tomcat 10.1.57 (schultz)" rtext="2026-07-07">
<subsection name="Catalina">
diff --git a/webapps/docs/web-socket-howto.xml
b/webapps/docs/web-socket-howto.xml
index 6c0697029f..f24590fab6 100644
--- a/webapps/docs/web-socket-howto.xml
+++ b/webapps/docs/web-socket-howto.xml
@@ -159,6 +159,13 @@
is 20. Redirection support can be disabled by configuring a value of zero.
</p>
+<p>When using the WebSocket client to connect to server endpoints, the maximum
+ number of bytes permitted in the HTTP response headers is controlled by the
+ <code>userProperties</code> of the provided
+ <code>jakarta.websocket.ClientEndpointConfig</code>. The property is
+ <code>org.apache.tomcat.websocket.MAX_HTTP_RESPONSE_HEADER_BYTES</code>. The
+ default is 8192 (8KB).</p>
+
<p>When using the WebSocket client to connect to a server endpoint that
requires
BASIC or DIGEST authentication, the following user properties must be set:
</p>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]