socketio / socket.io-client-java

Full-featured Socket.IO Client Library for Java, which is compatible with Socket.IO v1.0 and later.
https://socketio.github.io/socket.io-client-java/installation.html
Other
5.34k stars 976 forks source link

Websocket Upgrade https #88

Closed KieranWebber closed 3 years ago

KieranWebber commented 9 years ago

I've been having an issue with this library and https for quite a while now. When attempting to connect to my node.js server over https the connection never upgrades to a Websocket and permanent uses xhr polling. This is with both https termination on HAPROXY and a direct connection to a node server running the https itself. As soon as I remove https and connect via https the connection instantly upgrades to a Web socket. Having had a look at the logs the client probes correctly and creates a Web socket Transport. This transport immediately errors and the client falls back to xhr polling. The only success I had was giving IO an sslcontext which accepted all certs and host names (My Certificate is a valid one and trusted by Android by defualt) and setting the only transport to Web socket. Now the first time this is run it gets into a infinite Web socket loop and starts leaking memory. However on later attempts having been restarted the client does sometimes connect correctly via a Web socket. Having looked at the Engine.io source the Web socket is failing the ping/pong probe when using https. I've personally exhausted all things I can think of and any help would be much appreciated - I can provide any logs if you need them. It just seems odd that it upgrades perfectly on http and adding ssl breaks it immediatlty

dytsai commented 9 years ago

We encounter similar issue as well. We also use SSL and the protocol is never upgraded to websocket.

KieranWebber commented 9 years ago

I managed to get it to upgrade of https on a emulator only after passing it a ssl context that accepted all certs. However when tested on a actual device it still failed to upgrade

nkzawa commented 9 years ago

I'm not sure, but it seems some preparations are required for Android according to README of Java-WebSocket.

https://github.com/TooTallNate/Java-WebSocket#wss-support http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/

KieranWebber commented 9 years ago

I'll give that another go but my ssl cert is trusted by default by Android. So it would seem odd that I have to provide it to the Trust store

Alex--Kang commented 9 years ago

CarbonAssassin, did you have any luck on this issue? We did some experiments and noticed that connection over HTTP works perfectly fine( upgrading to websocket). But once we switched it to SSL, it never upgrades, and stays in xhr polling. FYI, my ssl cert is trusted by default by Android as well

nkzawa commented 9 years ago

It looks Java-WebSocket has some issues for SSL like https://github.com/TooTallNate/Java-WebSocket/issues/293. Are they applicable to you?

Alex--Kang commented 9 years ago

We are currently using Android 4.3, so the above issue is not applicable to our situation. I did some tracing and notice something interesting. For now, we use a commercial ssl certificate, and I have the following setup :

        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, null, null);
        IO.setDefaultSSLContext(sc);
        IO.Options opts = new IO.Options();
        opts.secure = true;
        opts.sslContext = sc;
        socket = IO.socket("https://www.myapp.xxx", opts);

With the above, I do see that Websocket instance gets called (WebSocket.java), and it tries to connect with the following : Line 100 : this.ws.connect();

However, right after it calls connect(), the onClose() method gets called, and the error message is com.android.org.conscrypt.OpenSSLSessionImpl cannot be cast to com.android.org.conscrypt.SSLSessionImpl

With a HTTP connection, the onOpen() method would get called instead, and websocket is upgraded successfully...

Is there anything wrong with my SSLContext setup steps? Again, thanks for your help

nkzawa commented 9 years ago

@Alex--Kang https://github.com/TooTallNate/Java-WebSocket/issues/141 looks resemble.

Alex--Kang commented 9 years ago

ok . good news.

After trying all the possible work-arounds ( btw, http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/ didn't work for me either.. ), i decided to look into the source code and found the following.

In Java-WebSocket-master, it provides a SSLClientExample.java and it shows that we should assign the SSLContext with:

    SSLContext sslContext = null;
    sslContext = SSLContext.getInstance( "TLS" );
    sslContext.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null );
    // sslContext.init( null, null, null ); // will use java's default key and trust store which is sufficient unless you deal with self-signed certificates

    SSLSocketFactory factory = sslContext.getSocketFactory();// (SSLSocketFactory) SSLSocketFactory.getDefault();

    chatclient.setSocket( factory.createSocket() );

    chatclient.connectBlocking();

So, instead of using Java-websocket-1.3.0.jar, i replaced it with the latest source code( commit # 05d2e2). Also, I modified the WebSocket.java (/engineio/client/transports) with :

        if (this.sslContext != null) {
            //this.ws.setWebSocketFactory(new DefaultSSLWebSocketClientFactory(this.sslContext));

            SSLSocketFactory factory = sslContext.getSocketFactory();
            try {
                ws.setSocket(factory.createSocket());
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }

And in my android client, I simply use the default setting

        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, null, null);
        IO.Options opts = new IO.Options();
        opts.secure = true;
        opts.sslContext = sc;
        socket = IO.socket(url, opts);

With these modifications, socketio connects successfully and the websocket would upgrade correctly without giving errors.

I noticed that the latest "release" of Java-websocket is 1.3 as of April, 2013, and the above changes was introduced in Sept, 2013. (https://github.com/TooTallNate/Java-WebSocket/commit/9b0a4b8c182bbe3f384d8ee8ede54eb754900bf5?diff=unified) I wonder what's the best way to tackle this problem. Would you consider fork your own java-websocket and fix from there? or wait for the next release of jws

Thanks

nkzawa commented 9 years ago

@Alex--Kang good news! I'm waiting update of the lib, thought it would not be released in the near feature :(

dytsai commented 9 years ago

@nkzawa I am also wondering why the author doesn't bump up the jar version while actually he's still actively developing it. In this case, is it possible for you to grab the latest source of Java-WebSocket instead of its outdated 1.3 jar file in engine.io? It does fix our SSL issue.

nkzawa commented 9 years ago

@dytsai I'm not thinking to do that for now since not sure the latest source of master branch is stable enough or not. If you are using a build system like Gradle or Maven, I believe you can override the version of the dependency if you need. Though it would be troublesome :(

dytsai commented 9 years ago

That's what we did and how we finally got it working. However, like you said, it's gonna be very troublesome without fully tested. It's not socket.io client, not engine.io but the underlying Java websocket library. Now we are really stuck here sigh.

mhmeadows63 commented 9 years ago

I have a Wireshark trace of a Java Desktop app attempting an HTTPS connect to a NodeJS SocketIO server, and it shows the websocket-upgrade request is transmitted as plain-text, while XHR polling requests are encrypted.

Sadly, I don't think sharing the trace would be much help without my TLS key for decode.

However, the following ladder diagrams attempts to detail my observation: 1) encrypted XHR poll exchange to port 8080

[----------Client----------]    [----------Server:8080-----------]
                         SYN ->
                             <- SYN,ACK
                         ACK ->
        TLSv1.2(ClientHello) ->
                             <- ACK
                             <- TLSv1.2(ServerHello)
                             <- TLSv1.2(Certificate)
                         ACK ->
  TLSv1.2(ClientKeyExchange) ->
   TLSv1.2(ChangeCipherSpec) ->
           TLSv1.2(Finished) ->
                             <- ACK
                             <- TLSv1.2(ChangeCipherSpec,Finished)
   TLS(GET /socket.io/..etc) ->
                             <- HTTP(200 OK)
TLSv1.2(Alert,Warning:Close) ->
                     FIN,ACK ->
                             <- FIN,ACK
                         ACK ->

2) plain-text websocket-upgrade to port 8080

[----------Client----------]    [----------Server:8080-----------]
                         SYN ->
                             <- SYN,ACK
                         ACK ->
 PLAIN(GET /socket.io/..etc) ->
                             <- ACK
                     RST,ACK ->

Anybody else think this might be relevant?

b95505017 commented 9 years ago

Maybe we should replace Java-WebSocket with OkHttp I've tried to use latest API, and it works https://github.com/nkzawa/engine.io-client.java/pull/14

mhmeadows63 commented 9 years ago

Ambivalence to my earlier post suggests the observation was old news.

Cutting past the socket.io level, my investigation at the engine.io level using a basic NodeJS script has pointed me to Java-WebSocket as the cause of the https upgrade failure, explaining the discussions around JW and a possible switch to OkHttp. Please correct me if I'm wrong.

My interest is in using socket.io-client.java over https from within a Windows desktop Java app, while I see most others interest revolves around Android Java apps, where I have no experience.

Am I right in thinking that websocket upgrade https is not yet working on any Java platform, and if so, does anyone have a view on a likely time-frame.

Thanks in advance.

mhmeadows63 commented 9 years ago

Apologies to all, the sample at https://github.com/jonathanve/socket-io-android put me on the right track.

As my SSL cert is trusted by default, I wrongly assumed the following would work:

                socket = IO.socket("https://<hostname>");

However, https requires opts that explicitly supply the sslContext

                IO.Options opts = new IO.Options();
                opts.sslContext = SSLContext.getDefault();
                socket = IO.socket("https://<hostname>", opts);

It could help new-comers like myself if the IO.socket(uri, opts) method were enhanced to populate the otherwise empty opts = new Options(); with the default SSLContext, such as:

                if (source.scheme.equals('https') && opts.sslContext == null) {
                        opts.sslContext = SSLContext.getDefault();
                }
nkzawa commented 9 years ago

Please try just released v0.5.0 which the websocket transport is replaced with OkHttp.

hekar commented 9 years ago

Had the same issue. Works with v0.5.0.

IO.Options opts = new IO.Options();
opts.secure = true;
opts.sslContext = SSLContext.getDefault();

final String url = ...;
socket = IO.socket(url, opts);
shuoli84 commented 9 years ago

@hekar Could you pls shed some light here? We are using this lib in production with https. For now, we can connect to our server without any issue. Do you still have issues? If yes, could you pls add some log here?

douglasjunior commented 9 years ago

I make it work, see https://github.com/TooTallNate/Java-WebSocket/issues/141#issuecomment-149303289

leoganda commented 8 years ago

Hello,

I am unable to connect from android to my socket io (https), the SSL certificate is valid and works well on web browser.

Here's my code,

` IO.Options opts = new IO.Options(); opts.secure = true; opts.sslContext = SSLContext.getDefault(); opts.hostnameVerifier = new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } };

socket = IO.socket("https://domain.com:3700",opts); socket.connect(); `

The socket is never connected without any error log, any suggestion for what's wrong with my code?

Lumbi commented 8 years ago

@leoganda

I'm also having issues connecting over HTTPS. You can try to log transport error like this:

socket.io().on(Manager.EVENT_TRANSPORT, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                Transport transport = (Transport) args[0];
                transport.on(Transport.EVENT_ERROR, new Emitter.Listener() {
                    @Override
                    public void call(Object... args) {
                        Exception e = (Exception) args[0];
                        Log.e("TEST", "transport error " + e);
                        e.printStackTrace();
                        e.getCause().printStackTrace();
                    }
                });
            }
        });

@nkzawa

Connecting to localhost Node works fine. But when connecting to the same Node hosted somewhere via HTTPS I keep getting xhr poll error caused by a 400 status code in the response.

E/TEST: xhr open GET: https://xxx/socket.io/?EIO=3&transport=polling
E/TEST: sending xhr with url https://xxx/socket.io/?EIO=3&transport=polling | data null
E/TEST: transport error io.socket.engineio.client.EngineIOException: xhr poll error

Specifying the SSLContext or the HostnameVerifier didn't help.

Lumbi commented 8 years ago

EDIT I could finally connect the socket by adding a "Origin" header to the requests!

socket.io().on(Manager.EVENT_TRANSPORT, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                Transport transport = (Transport) args[0];
                transport.on(Transport.EVENT_REQUEST_HEADERS, new Emitter.Listener() {
                    @Override
                    public void call(Object... args) {
                        Map<String, List<String>> headers = (Map<String, List<String>>)args[0];
                        headers.put("Origin", Arrays.asList("https://domain"));
                    }
                });
            }
        });
shobhitpuri commented 8 years ago

@Lumbi It is not working for me with 0.7.0 version and is giving same errors. Did you have to add anything else in options?

PhonegapProjects commented 6 years ago

Not Working Transport error coming

darrachequesne commented 3 years ago

Closed due to inactivity, please reopen if needed.