Author: markt
Date: Mon Nov 23 21:34:06 2015
New Revision: 1715965
URL: http://svn.apache.org/viewvc?rev=1715965&view=rev
Log:
Add ProxySelector support to the WebSocket client
Based on a patch by Niki Dokovski.
Added:
tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainerWithProxy.java
(with props)
Modified:
tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
tomcat/trunk/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java
tomcat/trunk/webapps/docs/changelog.xml
Modified: tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties?rev=1715965&r1=1715964&r2=1715965&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
(original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties Mon
Nov 23 21:34:06 2015
@@ -114,12 +114,12 @@ wsWebSocketContainer.endpointCreateFail=
wsWebSocketContainer.httpRequestFailed=The HTTP request to initiate the
WebSocket connection failed
wsWebSocketContainer.invalidExtensionParameters=The server responded with
extension parameters the client is unable to support
wsWebSocketContainer.invalidHeader=Unable to parse HTTP header as no colon is
present to delimit header name and header value in [{0}]. The header has been
skipped.
-wsWebSocketContainer.invalidScheme=The requested scheme, [{0}], is not
supported. The supported schemes are ws and wss
wsWebSocketContainer.invalidStatus=The HTTP response from the server [{0}] did
not permit the HTTP upgrade to WebSocket
wsWebSocketContainer.invalidSubProtocol=The WebSocket server returned multiple
values for the Sec-WebSocket-Protocol header
wsWebSocketContainer.maxBuffer=This implementation limits the maximum size of
a buffer to Integer.MAX_VALUE
wsWebSocketContainer.missingAnnotation=Cannot use POJO class [{0}] as it is
not annotated with @ClientEndpoint
wsWebSocketContainer.pathNoHost=No host was specified in URI
-wsWebSocketContainer.pathWrongScheme=The scheme [{0}] is not supported
+wsWebSocketContainer.pathWrongScheme=The scheme [{0}] is not supported. The
supported schemes are ws and wss
+wsWebSocketContainer.proxyConnectFail=Failed to connect to the configured
Proxy [{0}]. The HTTP response code was [{1}]
wsWebSocketContainer.sessionCloseFail=Session with ID [{0}] did not close
cleanly
wsWebSocketContainer.sslEngineFail=Unable to create SSLEngine to support
SSL/TLS connections
Modified:
tomcat/trunk/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsWebSocketContainer.java?rev=1715965&r1=1715964&r2=1715965&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
(original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/WsWebSocketContainer.java Mon
Nov 23 21:34:06 2015
@@ -22,6 +22,8 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.nio.ByteBuffer;
@@ -190,51 +192,80 @@ public class WsWebSocketContainer implem
throws DeploymentException {
boolean secure = false;
+ ByteBuffer proxyConnect = null;
+ URI proxyPath;
+ // Validate scheme (and build proxyPath)
String scheme = path.getScheme();
- if (!("ws".equalsIgnoreCase(scheme) ||
- "wss".equalsIgnoreCase(scheme))) {
+ if ("ws".equalsIgnoreCase(scheme)) {
+ proxyPath = URI.create("http" + path.toString().substring(2));
+ } else if ("wss".equalsIgnoreCase(scheme)) {
+ proxyPath = URI.create("https" + path.toString().substring(3));
+ } else {
throw new DeploymentException(sm.getString(
"wsWebSocketContainer.pathWrongScheme", scheme));
}
+
+ // Validate host
String host = path.getHost();
if (host == null) {
throw new DeploymentException(
sm.getString("wsWebSocketContainer.pathNoHost"));
}
int port = path.getPort();
- Map<String,List<String>> reqHeaders = createRequestHeaders(host, port,
- clientEndpointConfiguration.getPreferredSubprotocols(),
- clientEndpointConfiguration.getExtensions());
- clientEndpointConfiguration.getConfigurator().
- beforeRequest(reqHeaders);
- SocketAddress sa;
- if (port == -1) {
- if ("ws".equalsIgnoreCase(scheme)) {
- sa = new InetSocketAddress(host, 80);
- } else if ("wss".equalsIgnoreCase(scheme)) {
- sa = new InetSocketAddress(host, 443);
- secure = true;
+ SocketAddress sa = null;
+
+ // Check to see if a proxy is configured. Javadoc indicates return
value
+ // will never be null
+ List<Proxy> proxies = ProxySelector.getDefault().select(proxyPath);
+ Proxy selectedProxy = null;
+ for (Proxy proxy : proxies) {
+ if (proxy.type().equals(Proxy.Type.HTTP)) {
+ sa = proxy.address();
+ if (sa instanceof InetSocketAddress) {
+ InetSocketAddress inet = (InetSocketAddress) sa;
+ if (inet.isUnresolved()) {
+ sa = new InetSocketAddress(inet.getHostName(),
inet.getPort());
+ }
+ }
+ selectedProxy = proxy;
+ break;
+ }
+ }
+
+ // If sa is null, no proxy is configured so need to create sa
+ if (sa == null) {
+ if (port == -1) {
+ if ("ws".equalsIgnoreCase(scheme)) {
+ sa = new InetSocketAddress(host, 80);
+ } else {
+ // Must be wss due to scheme validation above
+ sa = new InetSocketAddress(host, 443);
+ secure = true;
+ }
} else {
- throw new DeploymentException(
- sm.getString("wsWebSocketContainer.invalidScheme"));
+ if ("wss".equalsIgnoreCase(scheme)) {
+ secure = true;
+ }
+ sa = new InetSocketAddress(host, port);
}
} else {
- if ("wss".equalsIgnoreCase(scheme)) {
- secure = true;
- }
- sa = new InetSocketAddress(host, port);
+ proxyConnect = createProxyRequest(host, port);
}
- // Origin header
+ // Create the initial HTTP request to open the WebSocket connection
+ Map<String,List<String>> reqHeaders = createRequestHeaders(host, port,
+ clientEndpointConfiguration.getPreferredSubprotocols(),
+ clientEndpointConfiguration.getExtensions());
+ clientEndpointConfiguration.getConfigurator().
+ beforeRequest(reqHeaders);
if (Constants.DEFAULT_ORIGIN_HEADER_VALUE != null &&
!reqHeaders.containsKey(Constants.ORIGIN_HEADER_NAME)) {
List<String> originValues = new ArrayList<>(1);
originValues.add(Constants.DEFAULT_ORIGIN_HEADER_VALUE);
reqHeaders.put(Constants.ORIGIN_HEADER_NAME, originValues);
}
-
ByteBuffer request = createRequest(path, reqHeaders);
AsynchronousSocketChannel socketChannel;
@@ -245,17 +276,6 @@ public class WsWebSocketContainer implem
"wsWebSocketContainer.asynchronousSocketChannelFail"),
ioe);
}
- Future<Void> fConnect = socketChannel.connect(sa);
-
- AsyncChannelWrapper channel;
- if (secure) {
- SSLEngine sslEngine = createSSLEngine(
- clientEndpointConfiguration.getUserProperties());
- channel = new AsyncChannelWrapperSecure(socketChannel, sslEngine);
- } else {
- channel = new AsyncChannelWrapperNonSecure(socketChannel);
- }
-
// Get the connection timeout
long timeout = Constants.IO_TIMEOUT_MS_DEFAULT;
String timeoutValue = (String)
clientEndpointConfiguration.getUserProperties().get(
@@ -264,36 +284,67 @@ public class WsWebSocketContainer implem
timeout = Long.valueOf(timeoutValue).intValue();
}
- ByteBuffer response;
+ // Set-up
+ // Same size as the WsFrame input buffer
+ ByteBuffer response = ByteBuffer.allocate(maxBinaryMessageBufferSize);
String subProtocol;
boolean success = false;
List<Extension> extensionsAgreed = new ArrayList<>();
Transformation transformation = null;
+ // Open the connection
+ Future<Void> fConnect = socketChannel.connect(sa);
+ AsyncChannelWrapper channel = null;
+
+ if (proxyConnect != null) {
+ try {
+ fConnect.get(timeout, TimeUnit.MILLISECONDS);
+ // Proxy CONNECT is clear text
+ channel = new AsyncChannelWrapperNonSecure(socketChannel);
+ writeRequest(channel, proxyConnect, timeout);
+ HttpResponse httpResponse = processResponse(response, channel,
timeout);
+ if (httpResponse.getStatus() != 200) {
+ throw new DeploymentException(sm.getString(
+ "wsWebSocketContainer.proxyConnectFail",
selectedProxy,
+ Integer.toString(httpResponse.getStatus())));
+ }
+ } catch (TimeoutException | InterruptedException |
ExecutionException |
+ EOFException e) {
+ channel.close();
+ throw new DeploymentException(
+
sm.getString("wsWebSocketContainer.httpRequestFailed"), e);
+ }
+ }
+
+ if (secure) {
+ // Regardless of whether a non-secure wrapper was created for a
+ // proxy CONNECT, need to use TLS from this point on so wrap the
+ // original AsynchronousSocketChannel
+ SSLEngine sslEngine = createSSLEngine(
+ clientEndpointConfiguration.getUserProperties());
+ channel = new AsyncChannelWrapperSecure(socketChannel, sslEngine);
+ } else if (channel == null) {
+ // Only need to wrap as this point if it wasn't wrapped to process
a
+ // proxy CONNECT
+ channel = new AsyncChannelWrapperNonSecure(socketChannel);
+ }
+
try {
fConnect.get(timeout, TimeUnit.MILLISECONDS);
Future<Void> fHandshake = channel.handshake();
fHandshake.get(timeout, TimeUnit.MILLISECONDS);
- int toWrite = request.limit();
+ writeRequest(channel, request, timeout);
- Future<Integer> fWrite = channel.write(request);
- Integer thisWrite = fWrite.get(timeout, TimeUnit.MILLISECONDS);
- toWrite -= thisWrite.intValue();
-
- while (toWrite > 0) {
- fWrite = channel.write(request);
- thisWrite = fWrite.get(timeout, TimeUnit.MILLISECONDS);
- toWrite -= thisWrite.intValue();
- }
- // Same size as the WsFrame input buffer
- response = ByteBuffer.allocate(maxBinaryMessageBufferSize);
-
- HandshakeResponse handshakeResponse =
- processResponse(response, channel, timeout);
- clientEndpointConfiguration.getConfigurator().
- afterResponse(handshakeResponse);
+ HttpResponse httpResponse = processResponse(response, channel,
timeout);
+ // TODO: Handle redirects
+ if (httpResponse.status != 101) {
+ throw new
DeploymentException(sm.getString("wsWebSocketContainer.invalidStatus",
+ Integer.toString(httpResponse.status)));
+ }
+ HandshakeResponse handshakeResponse =
httpResponse.getHandshakeResponse();
+
clientEndpointConfiguration.getConfigurator().afterResponse(handshakeResponse);
// Sub-protocol
List<String> protocolHeaders = handshakeResponse.getHeaders().get(
@@ -379,6 +430,43 @@ public class WsWebSocketContainer implem
}
+ private static void writeRequest(AsyncChannelWrapper channel, ByteBuffer
request,
+ long timeout) throws TimeoutException, InterruptedException,
ExecutionException {
+ int toWrite = request.limit();
+
+ Future<Integer> fWrite = channel.write(request);
+ Integer thisWrite = fWrite.get(timeout, TimeUnit.MILLISECONDS);
+ toWrite -= thisWrite.intValue();
+
+ while (toWrite > 0) {
+ fWrite = channel.write(request);
+ thisWrite = fWrite.get(timeout, TimeUnit.MILLISECONDS);
+ toWrite -= thisWrite.intValue();
+ }
+ }
+
+
+ private static ByteBuffer createProxyRequest(String host, int port) {
+ StringBuilder request = new StringBuilder();
+ request.append("CONNECT ");
+ request.append(host);
+ if (port != -1) {
+ request.append(':');
+ request.append(port);
+ }
+ request.append(" HTTP/1.1\r\nProxy-Connection:
keep-alive\r\nConnection: keepalive\r\nHost: ");
+ request.append(host);
+ if (port != -1) {
+ request.append(':');
+ request.append(port);
+ }
+ request.append("\r\n\r\n");
+
+ byte[] bytes =
request.toString().getBytes(StandardCharsets.ISO_8859_1);
+ return ByteBuffer.wrap(bytes);
+ }
+
+
protected void registerSession(Endpoint endpoint, WsSession wsSession) {
if (!wsSession.isOpen()) {
@@ -429,7 +517,7 @@ public class WsWebSocketContainer implem
return result;
}
- private Map<String,List<String>> createRequestHeaders(String host,
+ private static Map<String,List<String>> createRequestHeaders(String host,
int port, List<String> subProtocols, List<Extension> extensions) {
Map<String,List<String>> headers = new HashMap<>();
@@ -479,7 +567,7 @@ public class WsWebSocketContainer implem
}
- private List<String> generateExtensionHeaders(List<Extension> extensions) {
+ private static List<String> generateExtensionHeaders(List<Extension>
extensions) {
List<String> result = new ArrayList<>(extensions.size());
for (Extension extension : extensions) {
StringBuilder header = new StringBuilder();
@@ -499,14 +587,14 @@ public class WsWebSocketContainer implem
}
- private String generateWsKeyValue() {
+ private static String generateWsKeyValue() {
byte[] keyBytes = new byte[16];
random.nextBytes(keyBytes);
return Base64.encodeBase64String(keyBytes);
}
- private ByteBuffer createRequest(URI uri, Map<String,List<String>>
reqHeaders) {
+ private static ByteBuffer createRequest(URI uri, Map<String,List<String>>
reqHeaders) {
ByteBuffer result = ByteBuffer.allocate(4 * 1024);
// Request line
@@ -539,7 +627,7 @@ public class WsWebSocketContainer implem
}
- private void addHeader(ByteBuffer result, String key, List<String> values)
{
+ private static void addHeader(ByteBuffer result, String key, List<String>
values) {
StringBuilder sb = new StringBuilder();
Iterator<String> iter = values.iterator();
@@ -566,13 +654,14 @@ public class WsWebSocketContainer implem
* @throws DeploymentException
* @throws TimeoutException
*/
- private HandshakeResponse processResponse(ByteBuffer response,
+ private HttpResponse processResponse(ByteBuffer response,
AsyncChannelWrapper channel, long timeout) throws
InterruptedException,
ExecutionException, DeploymentException, EOFException,
TimeoutException {
Map<String,List<String>> headers = new CaseInsensitiveKeyMap<>();
+ int status = 0;
boolean readStatus = false;
boolean readHeaders = false;
String line = null;
@@ -599,7 +688,7 @@ public class WsWebSocketContainer implem
if (readStatus) {
parseHeaders(line, headers);
} else {
- parseStatus(line);
+ status = parseStatus(line);
readStatus = true;
}
line = null;
@@ -607,14 +696,22 @@ public class WsWebSocketContainer implem
}
}
- return new WsHandshakeResponse(headers);
+ return new HttpResponse(status, new WsHandshakeResponse(headers));
}
- private void parseStatus(String line) throws DeploymentException {
- // This client only understands HTTP 1.1
+ private int parseStatus(String line) throws DeploymentException {
+ // This client only understands HTTP 1.
// RFC2616 is case specific
- if (!line.startsWith("HTTP/1.1 101")) {
+ String[] parts = line.trim().split(" ");
+ // CONNECT for proxy may return a 1.0 response
+ if (parts.length < 2 || !("HTTP/1.0".equals(parts[0]) ||
"HTTP/1.1".equals(parts[0]))) {
+ throw new DeploymentException(sm.getString(
+ "wsWebSocketContainer.invalidStatus", line));
+ }
+ try {
+ return Integer.parseInt(parts[1]);
+ } catch (NumberFormatException nfe) {
throw new DeploymentException(sm.getString(
"wsWebSocketContainer.invalidStatus", line));
}
@@ -865,4 +962,25 @@ public class WsWebSocketContainer implem
public int getProcessPeriod() {
return processPeriod;
}
+
+
+ private static class HttpResponse {
+ private final int status;
+ private final HandshakeResponse handshakeResponse;
+
+ public HttpResponse(int status, HandshakeResponse handshakeResponse) {
+ this.status = status;
+ this.handshakeResponse = handshakeResponse;
+ }
+
+
+ public int getStatus() {
+ return status;
+ }
+
+
+ public HandshakeResponse getHandshakeResponse() {
+ return handshakeResponse;
+ }
+ }
}
Modified:
tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java?rev=1715965&r1=1715964&r2=1715965&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java
(original)
+++ tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java
Mon Nov 23 21:34:06 2015
@@ -101,7 +101,7 @@ public class TestWsWebSocketContainer ex
Session wsSession = wsContainer.connectToServer(
TesterProgrammaticEndpoint.class,
ClientEndpointConfig.Builder.create().build(),
- new URI("ws://localhost:" + getPort() +
+ new URI("ws://" + getHostName() + ":" + getPort() +
TesterEchoServer.Config.PATH_ASYNC));
CountDownLatch latch = new CountDownLatch(1);
BasicText handler = new BasicText(latch);
@@ -133,7 +133,7 @@ public class TestWsWebSocketContainer ex
ContainerProvider.getWebSocketContainer();
wsContainer.connectToServer(TesterProgrammaticEndpoint.class,
ClientEndpointConfig.Builder.create().build(),
- new URI("ftp://localhost:" + getPort() +
+ new URI("ftp://" + getHostName() + ":" + getPort() +
TesterEchoServer.Config.PATH_ASYNC));
}
@@ -241,7 +241,7 @@ public class TestWsWebSocketContainer ex
Session wsSession = wsContainer.connectToServer(
TesterProgrammaticEndpoint.class,
ClientEndpointConfig.Builder.create().build(),
- new URI("ws://localhost:" + getPort() +
+ new URI("ws://" + getHostName() + ":" + getPort() +
TesterEchoServer.Config.PATH_BASIC));
BasicHandler<?> handler;
CountDownLatch latch = new CountDownLatch(1);
@@ -334,7 +334,7 @@ public class TestWsWebSocketContainer ex
Session wsSession = wsContainer.connectToServer(
TesterProgrammaticEndpoint.class,
ClientEndpointConfig.Builder.create().build(),
- new URI("ws://localhost:" + getPort() + BlockingConfig.PATH));
+ new URI("ws://" + getHostName() + ":" + getPort() +
BlockingConfig.PATH));
if (!setTimeoutOnContainer) {
wsSession.getAsyncRemote().setSendTimeout(TIMEOUT_MS);
@@ -414,7 +414,7 @@ public class TestWsWebSocketContainer ex
Session wsSession = wsContainer.connectToServer(
TesterProgrammaticEndpoint.class,
ClientEndpointConfig.Builder.create().build(),
- new URI("ws://localhost:" + getPort() +
+ new URI("ws://" + getHostName() + ":" + getPort() +
ConstantTxConfig.PATH));
wsSession.addMessageHandler(new BlockingBinaryHandler());
@@ -780,7 +780,7 @@ public class TestWsWebSocketContainer ex
Endpoint endpoint, String path) throws Exception {
return wsContainer.connectToServer(endpoint,
ClientEndpointConfig.Builder.create().build(),
- new URI("ws://localhost:" + getPort() + path));
+ new URI("ws://" + getHostName() + ":" + getPort() + path));
}
public static final class EndpointA extends Endpoint {
@@ -825,7 +825,7 @@ public class TestWsWebSocketContainer ex
Session wsSession = wsContainer.connectToServer(
TesterProgrammaticEndpoint.class,
clientEndpointConfig,
- new URI("wss://localhost:" + getPort() +
+ new URI("wss://" + getHostName() + ":" + getPort() +
TesterEchoServer.Config.PATH_ASYNC));
CountDownLatch latch = new CountDownLatch(1);
BasicText handler = new BasicText(latch);
@@ -992,7 +992,7 @@ public class TestWsWebSocketContainer ex
Session wsSession = wsContainer.connectToServer(
TesterProgrammaticEndpoint.class,
clientConfig,
- new URI("ws://localhost:" + getPort() +
+ new URI("ws://" + getHostName() + ":" + getPort() +
TesterEchoServer.Config.PATH_ASYNC));
CountDownLatch latch = new CountDownLatch(count);
BasicText handler = new BasicText(latch, msg);
@@ -1007,4 +1007,12 @@ public class TestWsWebSocketContainer ex
((WsWebSocketContainer) wsContainer).destroy();
}
+
+
+ /*
+ * Make this possible to override so sub-class can more easily test proxy
+ */
+ protected String getHostName() {
+ return "localhost";
+ }
}
Added:
tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainerWithProxy.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainerWithProxy.java?rev=1715965&view=auto
==============================================================================
---
tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainerWithProxy.java
(added)
+++
tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainerWithProxy.java
Mon Nov 23 21:34:06 2015
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.websocket;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+
+@Ignore // Additional infrastructure is required to run this test
+public class TestWsWebSocketContainerWithProxy extends
TestWsWebSocketContainer {
+
+ @BeforeClass
+ public static void init() {
+ // Set the system properties for a HTTP proxy on 192.168.0.100:80
+ // I used an httpd instance configured as an open forward proxy for
this
+ // Update the IP/hostname as required
+ System.setProperty("http.proxyHost", "192.168.0.100");
+ System.setProperty("http.proxyPort", "80");
+ System.setProperty("http.nonProxyHosts", "");
+ }
+
+ @Before
+ public void setPort() {
+ // With httpd 2.2, AllowCONNECT requires fixed ports. From 2.4, a range
+ // can be used.
+ getTomcatInstance().getConnector().setPort(8080);
+ getTomcatInstance().getConnector().setProperty("address","0.0.0.0");
+ }
+
+ @Override
+ protected String getHostName() {
+ // The IP/hostname where the tests are running. The proxy will connect
+ // back to this expecting to find the Tomcat instance created by the
+ // unit test.
+ return "192.168.0.200";
+ }
+}
Propchange:
tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainerWithProxy.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: tomcat/trunk/webapps/docs/changelog.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1715965&r1=1715964&r2=1715965&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Mon Nov 23 21:34:06 2015
@@ -112,6 +112,16 @@
</fix>
</changelog>
</subsection>
+ <subsection name="WebSocket">
+ <changelog>
+ <add>
+ <bug>55006</bug>: The WebSocket client now honors the
+ <code>java.net.java.net.ProxySelector</code> configuration (using the
+ HTTP type) when establishing WebSocket connections to servers. Based on
+ a patch by Niki Dokovski. (markt)
+ </add>
+ </changelog>
+ </subsection>
<subsection name="jdbc-pool">
<changelog>
<fix>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]