This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new 67b01c13bc Add limit to HTTP response header size in upgrade to
WebSocket
67b01c13bc is described below
commit 67b01c13bc489d5c8ce2b9e006ccb52161815316
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 | 6 ++++++
webapps/docs/web-socket-howto.xml | 7 +++++++
5 files changed, 45 insertions(+), 4 deletions(-)
diff --git a/java/org/apache/tomcat/websocket/Constants.java
b/java/org/apache/tomcat/websocket/Constants.java
index 7841f6b010..4e529b8b68 100644
--- a/java/org/apache/tomcat/websocket/Constants.java
+++ b/java/org/apache/tomcat/websocket/Constants.java
@@ -71,6 +71,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 f1f7c16e0d..a520f332cf 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -165,6 +165,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.sslEngineFail=Unable to create SSLEngine to support
SSL/TLS connections
wsWebSocketContainer.unsupportedAuthScheme=Failed to handle HTTP response code
[{0}]. Unsupported Authentication scheme [{1}] returned in response
diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
index 252e287677..495ef6c1b7 100644
--- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
@@ -301,6 +301,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);
+ }
+
HandshakeResponse handshakeResponse = EMPTY_HANDSHAKE_RESPONSE;
try {
// Open the connection
@@ -311,7 +318,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,
@@ -354,7 +361,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;
@@ -850,8 +857,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<>();
@@ -860,6 +868,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.
@@ -879,6 +888,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)) {
@@ -897,6 +907,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 fe15f828b2..3bf0614603 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -415,6 +415,12 @@
<code>Writer</code> and <code>OutputStream</code>. (markt)
</fix>
<!-- Entries for backport and removal before 12.0.0-M1 below this line
-->
+ <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>
<subsection name="Web applications">
diff --git a/webapps/docs/web-socket-howto.xml
b/webapps/docs/web-socket-howto.xml
index c159f1dc99..00d910b7e7 100644
--- a/webapps/docs/web-socket-howto.xml
+++ b/webapps/docs/web-socket-howto.xml
@@ -120,6 +120,13 @@
timeout as a <code>String</code> in milliseconds. The default is 5000 (5
seconds).</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 server endpoints, the number
of
HTTP redirects that the client will follow is controlled by the
<code>userProperties</code> of the provided
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]