WebSocket via HTTP proxy

As you might know, WebSocket can be used for bi-directional "real-time" communication with multiple clients. What does that mean in proxy environments and how this even works? WebSocket uses HTTP upgrade mechanism specified in HTTP 1.1 and by design requires open (TCP) connection.

HTTP CONNECT is there for exactly these usecases. It is usually used for tunneling HTTPS via proxy, but it can be used for WebSocket as well.

I will describe complete "proxified" handshake using captured connection of Tyrus Client connecting to public echo service - echo.websocket.org. Please note that we are directly using Tyrus API - not WebSocket specification (JSR-356), because we need to set a proxy.

final ClientManager client = ClientManager.createClient();

client.getProperties().put(
  GrizzlyClientSocket.PROXY_URI, "http://my.proxy:8080"
);
final Session session = client.connectToServer(new Endpoint() {

  @Override
  public void onOpen(Session session, EndpointConfig config) {
    session.addMessageHandler(new MessageHandler.Whole<String>() {
      @Override
      public void onMessage(String message) {
        System.out.println("# Message received: " + message);
      }
    });
  }
}, ClientEndpointConfig.Builder.create().build(),
   URI.create("ws://echo.websocket.org"));

session.getBasicRemote().sendText("test message");

BTW, Tyrus Client proxy support can be improved, currently it does not support proxy authentication, JDK's ProxySelector and so on. Please vote/comment on TYRUS-204 if you lack some of the mentioned options or anything else related to proxy support.

Current modern browsers do support this out of the box, so all you need is to set your HTTP proxy and described handshake will be done automatically. There might be limitation for parallel open connection in browser tab/instance, but I don't have any exact data about this.

Also, you might ask whether there is some need to server-side support - simple answer is "no". HTTP containers will see regular connection (from proxy), there is no additional work or overhead on that side.

Lets see our dumped communication:

client > proxy
CONNECT echo.websocket.org:80 HTTP/1.1
Host: echo.websocket.org
Proxy-Connection: keep-alive
Connection: keep-alive

Firstly, client need to send a request to proxy for new "permanent" connection. As already mentioned, CONNECT method handles this. First argument is a hostname (echo.websocket.org) and standard HTTP version.

proxy > client
HTTP/1.0 200 Connection established

If you are lucky, your proxy does support CONNECT and allows you to create connection (HTTP 200 is returned).

client > proxy
GET / HTTP/1.1
Connection: Upgrade
Host: echo.websocket.org
Origin: echo.websocket.org
Sec-WebSocket-Key : sDD3Wk7PMRCPE9+C0VyOcQ==
Sec-WebSocket-Version: 13
Upgrade: websocket

This is standard WebSocket handshake request, which will be passed to target HTTP container.

proxy > client
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: 8TNIHr7bJHqQadjXYvqLql6RFEA=
Date: Tue, 16 Jul 2013 15:30:53 GMT
...

And there is a valid response to our handshake request, connection is established and communication can be started; there is nothing else different than in proxy-less environment. Please note that proxies do have limited resources and your request may be turned down because proxy "CONNECT" pool is empty.

Conclusion here is that WebSocket can work via proxies without any limitation, it just introduces different kind of traffic than pure HTTP and might cause some additional requirements related to proxy performance in case you are going to use WebSocket for long-running client connections.

Comments:

Hi Pavel,

Thanks for the explanation, that really did help me to understand the handshake. I have a scenario, wherein the connection initiation is successful and the proxy returns a HTTP/1.0 200 Connection established, but the SSL handshake fails.

HTTP/1.0 500 handshakefailed
Via: 1.0 10.X.X.X (McAfee Web Gateway 7.2.0.1.0.13253)
Content-Type: text/html
Cache-Control: no-cache
Content-Length: 2063
Proxy-Connection: Close

Any suggestions why is it failing?

Posted by Srini on September 04, 2013 at 08:05 AM CEST #

Hi Srini,

you should add -Djavax.net.debug=all (see http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/ReadDebug.html for more details). It usually is certificate issue, in your case it might be because the other side does not have properly signed certificate and you don't have truststore and/or keystore properly set on client side.

Feel free to send more details to users@tyrus.java.net.

Posted by guest on September 09, 2013 at 10:11 AM CEST #

Finally some extra info on websokets and proxies...not much on the web. I read this article on WS & proxies that every other article seemed to link to http://www.infoq.com/articles/Web-Sockets-Proxy-Servers
Problem is its dated 2010 yet the protocol was standaridzed end of 2011 and there is a part on '_Proxy Usage_:' that explains that the client can send info to a proxy asking it to connect to the given server. To me this sounds like there shouldnt be any problems as put out in the 2010 article but wasnt sure because i know about proxies but not their nitty gritties. Your article supports what I thought but i'd like to know for sure, are there any limitatons to HTTP proxy traversal when using Websockets?

Posted by tom on December 06, 2013 at 11:15 PM CET #

Hi Tom,

there are limitations, but it is very unlikely you will hit them - it is the same as for creating HTTPS connections. From what I know, the theoretical limit would be something around 60k parallel connections for one proxy. Also depends on the proxy software itself, the number of "CONNECT"s is usually capped there to be able to process other standard http requests.

Posted by Pavel on December 06, 2013 at 11:34 PM CET #

Hi Pavel,
for a work flow system, I've build and which may handle advertisement production for multiple customers (news providers) within a single instance.
Internaly it is based on ip-reachability of all components.
In some cases it is necessary to integrate the personality who recorded the commercial ad order (somewher, within the customers LAN).
I'm on with WEB Sockets to allow the quasi synchronous connection to connect to the data base and via RMI to the Production server.
At the moment I'v started to build a test application using the NETBEAN IDE 8.0.2.
This IDE is based on JSR_API.
You used tyrus the uese proxies.
Could you plaese lat me know, which implementation I should use in my case.

Jochen

Posted by Jochen Seliger on May 12, 2015 at 10:29 AM CEST #

Hey Jochen,

I'm not sure whether I understood the question; Netbeans itself are not shipping with Java EE implementation by default. However, you can download prepackaged bundle with Glassfish, which does contain Tyrus - then you should be all set to start your work with Tyrus. Or you can use libraries from some other supported application server and then you will use their implementation, which might or might not be Tyrus.

The usecase is not really clear to me - I don't know where in your design you plan to use WebSocket. You can create more comprehensive description and share it on users@tyrus.java.net - responding there is much easier than here.

Thanks,
Pavel

Posted by Pavel on May 12, 2015 at 11:02 AM CEST #

Hi Pavel,
as I'm not sure yet, if I should use tyrus or JAVA7 (with JSR356), I'm not sure where to post my questions.
Concerning your question:
I'v build a work flow solution, which will speed up the advertisement production for newsproviders (mainly for newspapers).
Internally (between the compoments of the solution) I need a visibility of all components cross over [LAN or VPN]).
The solution is build to handle productions for unlimited number of customers (newspapers).
As the customers commercial advertisement systems will have work stations, spread over an unknown range of network structures, where in some cases the recorders of the commercial order, should be incorporabable into the work flow processing, we will provide an easy way to do that.
The needed part within the work flow logic is already implemented.
But the quasi synchronous connection quasi from any computer is the question.
Generally there could be opened an VPN, including all mashines.
But if an customers will handle production, lets say one in UK, one on Polans one in China, the establishing of one or several VPN's will be slightly complicated.
Therefor I'd like to have the opportunity, to let our special WF-Clients to get connected to an WEB-Service, running at the central WF-Instance and letting them them retrieve and use the jdbc-Connection and the RMI-reagistries (for and backwars)to connect to the relavant production server by a client and to exchange messages from any connected clienet to any other client.
That's the scenario, where I need the WEB-socket connection, first to retrieve the DBConnection and the RMI-registry and run the connection via these objects.

Regards
Jochen

Posted by guest on May 12, 2015 at 02:27 PM CEST #

Hi Jochen,

seems like you are looking for "TCP over WebSocket". Maybe not exactly TCP, you certainly could use some higher level protocols.. well. Not saying this is a bad idea (on the contrary - this should nicely get you around most firewalls, proxies and other nasty stuff out there, since WebSocket is HTTP at the very beginning), but it is not something which we do have implemented in Tyrus.

There are some people which are using WebSocket protocol as a tunnel for almost anything, like RFC 7118 (SIP over WebSocket), RFC 7395 (XMPP over WebSocket) and others. (see https://datatracker.ietf.org/doc/search/?name=websocket&sort=&rfcs=on&activedrafts=on for more complete result).

I doubt you want to create new standard, but you can! :)

So you can use websocket for your project and it should work (no guaranties about effectiveness etc), but it will mean significant investment in proprietary solution. I know about one company which do specialize on WebSocket applications - look for Kaazing, they might have something which will almost suite your needs, but you should also consider the VPN based solution you mentioned - in the long run, it could be cheaper and more sustainable one. Anyway, this is really out of scope of this article/blog, so I should stop now :) feel free to contact me privately if you think I can help more.

Regards,
Pavel

Posted by Pavel on May 12, 2015 at 03:24 PM CEST #

Hi,
I am using tyrus for standalone client/server application and facing problem with passing secured websocket through proxy server.
There is no problem with secured websocket without proxy or non-secured websocket with proxy.

After initial negotiation connection stuck and then closed on timeout.
I placed wireshark on both (client and server) sides and can see the following

Client
17576 36.801068000 CLIENT_IP SERVER_IP TCP 66 49839→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
17577 36.801123000 CLIENT_IP SERVER_IP TCP 66 [TCP Out-Of-Order] 49839→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
17578 36.801587000 SERVER_IP CLIENT_IP TCP 66 5555→49839 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
17579 36.801669000 CLIENT_IP SERVER_IP TCP 54 49839→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0
17580 36.801704000 CLIENT_IP SERVER_IP TCP 54 [TCP Dup ACK 17579#1] 49839→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0

Server
3416 44.101237000 CLIENT_IP SERVER_IP TCP 66 49839→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
3417 44.101428000 SERVER_IP CLIENT_IP TCP 66 5555→49839 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
3418 44.101769000 CLIENT_IP SERVER_IP TCP 60 49839→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0

Here is the code I am using for client side

ClientManager client = ClientManager.createClient();
if(proxyHost != null) {
client.getProperties().put(ClientProperties.PROXY_URI, "http://" + proxyHost + ":" + proxyPort);
}

if("wss".equalsIgnoreCase(protocol)) {
final SslContextConfigurator defaultConfig = new SslContextConfigurator();
defaultConfig.retrieve(System.getProperties());
SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(defaultConfig, true, false, false);
sslEngineConfigurator.setHostVerificationEnabled(false);
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
}

client.getProperties().put(ClientProperties.LOG_HTTP_UPGRADE, true);
client.connectToServer(WebSocketClientEndpoint.class, new URI(protocol + "://" + host + ":" + port + "/websockets/echo"));

On server side I am using approach you suggested with creating and registering new service class inherited from GrizzlyServerContainer

if("wss".equalsIgnoreCase(System.getProperty("wsprotocol"))) {
server.getListener("grizzly").setSecure(true);
final SSLContextConfigurator defaultConfig = new SSLContextConfigurator();
defaultConfig.retrieve(System.getProperties());

SSLEngineConfigurator sslEngineConfigurator = new SSLEngineConfigurator(defaultConfig, false, false, false);
server.getListener("grizzly").setSSLEngineConfig(sslEngineConfigurator);
}

I am using tyrus 1.12 with grizzly 2.3.22.
Thank you in advance.

P.S.
In order to check that problem is not with proxy configuration I tried another library (TooTallNate/Java-WebSocket) and did succeed to pass proxy with secured websocket.

Posted by guest on October 19, 2015 at 08:31 AM CEST #

Hi guest,

that is quite extensive description, thanks! ;) But.. there is one key thing missing - communication between client and the proxy. I'm sure there is something, since the proxy attempted to connect to the server host.

Infortunate part is that content will be encrypted.. anyway, we could still get lots of information from run with "-Djavax.net.debug=all", see http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/ReadDebug.html.

My personal tip is that there is some additional info requested from the server, but client does not respond for some reason (usually because it doesn't have the info).

Last question - did the other client ran against the same server? Or have you used its server implementation?

Posted by Pavel on October 19, 2015 at 09:35 AM CEST #

Hi Pavel,
Thank you for your quick response.
First i would like to mention that without proxy both client and server communicate correct with secured websocket.
So i don't believe that client does not sends to server something (only if proxy blocks it, but i did not see nothing and again - the second library passes with n oproblem). I checked this on both our corporative proxy and jHTTPp2 (OpenSource HTTP Proxy Server).
I did enable -Djavax.net.debug=all,ssl but on client side i can see is "adding trust store" and "ignoring unavailable cyther suite:..."
lines only and then it stuck. Nothing can be seen on server side.
When i kill client (or it closed on timeout) i can see ssl initializarion prints on server side.

I did use both client and server implementation of the TootallNate.

The most interesting is that TootallNate client does succeed to communicate tyrus server :) At least they pass ssl handshake and then something else failed.

Any hints on how to proceed here will appreciated.

Thank you in advance,
Genadi

Posted by guest on October 19, 2015 at 10:00 AM CEST #

Can you see response from the proxy to the client?

And, if you can, you might want to try "cross check" - tyrus client with TooTallNate client and vice versa, which could tell us where is the issue - whether on client or server side.

Posted by Pavel on October 19, 2015 at 10:18 AM CEST #

Hi Pavel,

I checked the following configurations:
1. tyrus client --> tyrus wss server without proxy success
2. tyrus client --> tyrus wss server with proxy no success
3. TootallNate client --> tyrus wss server with proxy success (partly with ssl handshale)

From logs below you can see that 1. and 3. (both succeeded) send as sixth packet [PSH, ACK] while 2. does not. Seems like there is a problem on client side with wss through proxy.
Is this something tyrus deals with or this is glassfish issue?

Below are logs from wireshark

Thank you in advance,
Genadi

tyrus wss without proxy : SUCCESS
Client
352 11.180798000 CLIENT_IP SERVER_IP TCP 66 53679→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
353 11.181273000 CLIENT_IP SERVER_IP TCP 66 [TCP Out-Of-Order] 53679→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
354 11.181274000 SERVER_IP CLIENT_IP TCP 66 5555→53679 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
355 11.181378000 CLIENT_IP SERVER_IP TCP 54 53679→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0
356 11.183263000 CLIENT_IP SERVER_IP TCP 54 [TCP Dup ACK 355#1] 53679→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0
365 11.309918000 CLIENT_IP SERVER_IP TCP 266 53679→5555 [PSH, ACK] Seq=1 Ack=1 Win=65536 Len=212
366 11.310247000 CLIENT_IP SERVER_IP TCP 266 [TCP Retransmission] 53679→5555 [PSH, ACK] Seq=1 Ack=1 Win=65536 Len=212
370 11.366727000 SERVER_IP CLIENT_IP TCP 60 5555→53679 [ACK] Seq=1 Ack=213 Win=131328 Len=0
786 13.854951000 SERVER_IP CLIENT_IP TCP 1366 5555→53679 [PSH, ACK] Seq=1 Ack=213 Win=131328 Len=1312
.....
Server
1200 22.395671000 CLIENT_IP SERVER_IP TCP 66 53679→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
1201 22.395815000 SERVER_IP CLIENT_IP TCP 66 5555→53679 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
1202 22.396209000 CLIENT_IP SERVER_IP TCP 60 53679→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0
1206 22.524803000 CLIENT_IP SERVER_IP TCP 266 53679→5555 [PSH, ACK] Seq=1 Ack=1 Win=65536 Len=212
1215 22.579235000 SERVER_IP CLIENT_IP TCP 54 5555→53679 [ACK] Seq=1 Ack=213 Win=131328 Len=0
1594 25.069244000 SERVER_IP CLIENT_IP TCP 1366 5555→53679 [PSH, ACK] Seq=1 Ack=213 Win=131328 Len=1312
......
tyrus wss with proxy : FAILURE
Client
3972 39.656440000 CLIENT_IP SERVER_IP TCP 66 53609→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
3973 39.656497000 CLIENT_IP SERVER_IP TCP 66 [TCP Out-Of-Order] 53609→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
3974 39.657070000 SERVER_IP CLIENT_IP TCP 66 5555→53609 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
3975 39.657159000 CLIENT_IP SERVER_IP TCP 54 53609→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0
3976 39.657196000 CLIENT_IP SERVER_IP TCP 54 [TCP Dup ACK 3975#1] 53609→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0
....
Server
1931 24.718986000 CLIENT_IP SERVER_IP TCP 66 53609→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
1932 24.719231000 SERVER_IP CLIENT_IP TCP 66 5555→53609 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
1933 24.719589000 CLIENT_IP SERVER_IP TCP 60 53609→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0
....
ToolTallNate wss with proxy : SUCCESS
Client
15553 41.419259000 CLIENT_IP SERVER_IP TCP 66 53641→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
15554 41.419313000 CLIENT_IP SERVER_IP TCP 66 [TCP Out-Of-Order] 53641→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
15555 41.420037000 SERVER_IP CLIENT_IP TCP 66 5555→53641 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
15556 41.420123000 CLIENT_IP SERVER_IP TCP 54 53641→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0
15557 41.420155000 CLIENT_IP SERVER_IP TCP 54 [TCP Dup ACK 15556#1] 53641→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0
15563 41.690507000 CLIENT_IP SERVER_IP TCP 266 53641→5555 [PSH, ACK] Seq=1 Ack=1 Win=65536 Len=212
15564 41.690568000 CLIENT_IP SERVER_IP TCP 266 [TCP Retransmission] 53641→5555 [PSH, ACK] Seq=1 Ack=1 Win=65536 Len=212
15566 41.750117000 SERVER_IP CLIENT_IP TCP 60 5555→53641 [ACK] Seq=1 Ack=213 Win=131328 Len=0
17114 43.817585000 SERVER_IP CLIENT_IP TCP 1366 5555→53641 [PSH, ACK] Seq=1 Ack=213 Win=131328 Len=1312
.....
Server
2564 49.416849000 CLIENT_IP SERVER_IP TCP 66 53641→5555 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
2565 49.417020000 SERVER_IP CLIENT_IP TCP 66 5555→53641 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
2566 49.417639000 CLIENT_IP SERVER_IP TCP 60 53641→5555 [ACK] Seq=1 Ack=1 Win=65536 Len=0
2593 49.688115000 CLIENT_IP SERVER_IP TCP 266 53641→5555 [PSH, ACK] Seq=1 Ack=1 Win=65536 Len=212
2598 49.747052000 SERVER_IP CLIENT_IP TCP 54 5555→53641 [ACK] Seq=1 Ack=213 Win=131328 Len=0
2771 51.814480000 SERVER_IP CLIENT_IP TCP 1366 5555→53641 [PSH, ACK] Seq=1 Ack=213 Win=131328 Len=1312
.......

Posted by Genadi on October 19, 2015 at 12:51 PM CEST #

Hi Pavel,
Did you have a chance to look what is the problem with tyrus client over wss when connecting through proxy?

Thank you in advance,
Genadi

Posted by Genadi on October 22, 2015 at 04:58 PM CEST #

Hi Genadi,

I'm really sorry, but I'm preparing for JavaOne and I don't have any cycles to spend on this right now :-/. Please send me a reminder in 2 weeks. (you can contact me directly - pavel.bucek@oracle.com or feel free to use users@tyrus.java.net).

Sorry for any inconvenience.
Pavel

Posted by Pavel on October 22, 2015 at 05:03 PM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Pavel Bucek-Oracle

Search

Categories
Archives
« May 2016
SunMonTueWedThuFriSat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    
       
Today