This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new b84cd5819b Add limit to HTTP response header size in upgrade to
WebSocket
b84cd5819b is described below
commit b84cd5819b41b38ad6db86b05e1056a4c6f8f469
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 0e1626262e..ee71df8673 100644
--- a/java/org/apache/tomcat/websocket/Constants.java
+++ b/java/org/apache/tomcat/websocket/Constants.java
@@ -86,6 +86,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 821a115b4b..385d39e1d3 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -153,6 +153,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 8e750c38d7..12bd393bb0 100644
--- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
@@ -304,6 +304,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);
@@ -313,7 +320,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,
@@ -356,7 +363,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;
@@ -840,8 +847,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<>();
@@ -850,6 +858,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.
@@ -869,6 +878,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)) {
@@ -887,6 +897,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 465191b80f..976ccdb170 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 9.0.120 (remm)" rtext="release in progress">
<subsection name="Catalina">
diff --git a/webapps/docs/web-socket-howto.xml
b/webapps/docs/web-socket-howto.xml
index e6e03c72f4..2e0795f6b1 100644
--- a/webapps/docs/web-socket-howto.xml
+++ b/webapps/docs/web-socket-howto.xml
@@ -164,6 +164,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]