Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hybi13 support (was: Webkit Remote Debugging WebSocket client connections fail) #39

Closed
patrickmn opened this issue Mar 19, 2013 · 13 comments

Comments

@patrickmn
Copy link

Run Chrome with remote debugging: google-chrome --remote-debugging-port=9222

Try to connect to one of the URLs from http://localhost:9222/json, e.g. ws://127.0.0.1:9222/devtools/page/12_1. After sending/receiving a message, the connection is closed/broken pipe, regardless of whether you use ByteStrings/BinaryData/BinaryProtocol or Text/TextData/TextProtocol.

Example message: {"method":"Inspector.enable","id":1}

@jaspervdj
Copy link
Owner

Thanks for the report! Would it be possible to include the code for the server as well?

@patrickmn
Copy link
Author

Unfortunately I have no idea how the (websocket) server works. It's built into Chrome. The API itself is a JSON-RPC API, so the response is supposed to be e.g. {"result":{},"id":1}. The issue is that the websockets library (supposedly) sends the message in such a way that Chrome feels the connection should be terminated.

I tried to dig around for it in the source, but that repository is monstrous. It's supposed to be based on the Webkit Remote Debugging protocol: https://www.webkit.org/blog/1875/announcing-remote-debugging-protocol-v1-0/

I made a JavaScript client which connects to the same URL, and works fine:

-- test.js

function testRemoteDebug() {
    console.log("Connecting");
    try {
        var successful = false;
        socket = new WebSocket("ws://localhost:9222/devtools/page/20_1");
        socket.onopen = function() {
            successful = true;
            console.log("Connection established");
            socket.logAndSend('{"method":"Inspector.enable","id":1}');
        }
        socket.onmessage = function(msg) {
            console.log("Received: " + msg.data);
        }
        socket.onclose = function() {
            if (successful) {
                console.log("Connection closed");
            } else {
                console.log("Connection failed");
            }
        }
        socket.logAndSend = function(s) {
            console.log("Sending: " + s);
            socket.send(s)
        }
    } catch (e) {
        console.log("Error connecting: " + e);
    }
}

-- test.html

<!DOCTYPE html>
<html>
<head>
    <title>Remote Debugger Test</title>
    <script type="text/javascript" src="test.js"></script>
</head>
<body>
    <script type="text/javascript">
    if (!("WebSocket" in window)) {
        $('#std, input, button, #examples').fadeOut("fast");
        $('<p>Please use a browser that supports WebSockets, like <a href="http://www.google.com/chrome">Google Chrome</a>.</p>').appendTo('#container');
    } else {
            testRemoteDebug();
        }
    </script>
</body>
</html>

(Change WS URL to that reflected in localhost:9222/json for an active tab)

Result (from JS console):

Connecting test.js:2
Connection established test.js:8
Sending: {"method":"Inspector.enable","id":1} test.js:22
Received: {"result":{},"id":1} test.js:12

Since this worked in Chrome, but not Firefox, I was wondering if it might be related to support for the webkit-deflate-frame extension, but as far as I can tell, the extension is not negotiated in the upgrade request/response. (Perhaps it expects only Hybi13?)

I wrote this Python test using https://pypi.python.org/pypi/websocket-client/0.7.0 (which supports Hybi13):

#!/usr/bin/env python
from websocket import create_connection

def main():
    ws = create_connection("ws://localhost:9222/devtools/page/20_1")
    ws.send("""{"method":"Inspector.enable","id":1}""")
    res = ws.recv()
    print "Received: {0}".format(res)
    ws.close()

if __name__ == "__main__":
    main()

Output: Received: {"result":{},"id":1}

So, it seems to me the most likely cause is either that it expects a more recent Hybi, or that the hs websockets library communicates the path differently somehow?

@patrickmn
Copy link
Author

After a little investigation, I've become fairly convinced that the issue is indeed just that hs-websockets doesn't speak Hybi13. I also noticed that this version has become the default/only supported version in many other WS libraries.

For convenience, here are the differences: https://tools.ietf.org/rfcdiff?url1=draft-ietf-hybi-thewebsocketprotocol-10.txt&url2=draft-ietf-hybi-thewebsocketprotocol-13.txt (not to worry, most are just rearrangements/rephrasing.) I might open a pull request later, but at the moment I am unfortunately not very savvy with WS internals (lib and spec.)

@fhaust
Copy link

fhaust commented May 17, 2013

Is the fact that something like this just fails related to this issue?

λ let app = liftIO (print "Connected") :: WS.WebSockets WS.Hybi10 ()
λ WS.connect "websocket.mtgox.com" 80 "/mtgox" app
*** Exception: MalformedResponse (ResponseHttpPart {responseHttpCode = 400, responseHttpMessage = "Bad Request", responseHttpHeaders = []}) "Wrong response status or message."

@patrickmn
Copy link
Author

At some point between version 0.7.2.1 and 0.7.4.0, this was solved on my end. I can now successfully connect to and communicate with the Chrome remote debugger, which (I assume) uses Hybi13 or higher.

If you didn't address this, presumably something changed on the Chrome side.

Apologies again for being so vague.

@patrickmn
Copy link
Author

Looking at a few of the recent commits, I think Chrome might have disconnected me because the frames weren't masked. It works when I use sendTextData (after the masking change), not with sendBinaryData.

@fhaust
Copy link

fhaust commented Jul 16, 2013

Doesn't work for me :(

2013/7/16 Patrick Mylund Nielsen notifications@github.com

Looking at a few of the recent commits, I think Chrome might have
disconnected me because the frames weren't masked. It works when I use
sendTextData (after the masking change), not with sendBinaryData.


Reply to this email directly or view it on GitHubhttps://github.com//issues/39#issuecomment-21072921
.

@jaspervdj
Copy link
Owner

This is fixed in the latest verion, websockets-0.8.0.0.

@fhaust
Copy link

fhaust commented Oct 27, 2013

I just tried to connect to the mtgox websocket api with websockets-0.8. Apparently this still doesn't work. Here is the code I suspect should at least not fail:

opSubscribe :: Text 
opSubscribe = "{\n\"channel\":\"dbf1dee9-4f2e-4a08-8cb7-748919a71b21\",\n\"op\":\"subscribe\"\n}\n"

app :: WS.Connection -> IO ()
app c = do
  print "connecting"
  WS.sendTextData c opSubscribe
  print "connected"

main :: IO ()
main = do
    print "starting!"
    WS.runClient "websocket.mtgox.com" 80 "/mtgox" app

running it yields:

"starting!"
mtgox-client: MalformedResponse (ResponseHead {responseCode = 403, responseMessage = "Forbidden", responseHeaders = []}) "Wrong response status or message."

@patrickmn
Copy link
Author

I can confirm the connection to Chrome (sending text data) still works as expected in 0.8.

@jaspervdj
Copy link
Owner

@fhaust If I run the following JavaScript code in Chromium:

var ws = new WebSocket ('ws://websocket.mtgox.com/mtgox');
ws.onopen = function() {
    ws.send('{\n\"channel\":\"dbf1dee9-4f2e-4a08-8cb7-748919a71b21\",\n\"op\":\"subscribe\"\n}\n');
};
ws.onmessage = function(event) {
    console.log('msg: ' + event.data);
};

It fails with the same 403 error. I'm quite sure Chromium's WebSockets implementation is not broken, so something else might be off. Since 403 deals with authorization, possibly you are required to set some Cookie or connect over SSH?

@fhaust
Copy link

fhaust commented Oct 28, 2013

According to the API (https://en.bitcoin.it/wiki/MtGox/API/Streaming) authorization is only required when accessing private features. There is a way to connect via SSL ... but afaik websockets doesn't support SSL/TLS, does it?

@jaspervdj
Copy link
Owner

It doesn't support it directly but with some boilerplate setting up an SSL connection is not that hard, see e.g. this example: https://gist.github.com/jaspervdj/7198388

I'll probably add this to the WebSockets library at some point, however I don't want to directly depend upon HsOpenSSL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants