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

Chrome: WebSocket connection to 'ws://localhost:4081/' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received #1

Closed
satoshinm opened this issue Apr 9, 2017 · 2 comments

Comments

@satoshinm
Copy link
Owner

Google Chrome 57.0.2987.133 fails to connect to the Netty WebSocket, giving this error:

WebSocket connection to 'ws://localhost:4081/' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received

Safari Technolgy Preview 27 and Firefox 53.0b8 are able to connect without error. Chrome doesn't have a problem with connecting through websockify to the Python server, so this points to some kind of interoperability incompatibility issue perhaps with Netty 4.1.9.Final or how I am using it.

@satoshinm
Copy link
Owner Author

satoshinm commented Apr 12, 2017

Found this netty issue: netty/netty#2953 Websocket examples broken in Chrome (extension negotiation problem)

There is a fix in the example example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerHandler.java, setting allow extensions to true instead of false, server-side websocket example fix: https://github.com/netty/netty/pull/3042/files - but the fix is in "WebSocketServerHandler.java" which no longer exists in 4.1: https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/http/websocketx/server

closest is either WebSocketFrameHandler.java or WebSocketIndexPageHandler.java, but no WebSocketServerHandshakerFactory. And I based WebSandboxMC on this very example.


http://netty.io/4.1/api/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.html allowExtensions

ok, I do pass allowExtensions: true to WebSocketServerProtocolHandler in WebSocketServerInitializer (and subprotocols: null):

        pipeline.addLast(new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true));

To do: get the official netty example working in chrome

@satoshinm
Copy link
Owner Author

satoshinm commented Apr 12, 2017

The official Netty websocket server example does work in Chrome: https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/http/websocketx/server

There may be something I'm doing differently in the WebSocketMC copy, or in the emscripten-based client. The demo works even with editing WebSocketServerInitializer.java to change WEBSOCKET_PATH from "/websocket" to "/", and updating the paths accordingly. Updating port to 4081, their Web Socket Test page is able to connect to WebSandboxMC and logs [object Blob]. I'm using binary ws frames instead of text frames. But the handshake is completed and connection established to the Netty demo ws server, and to the WebSandboxMC server.

Final test: using the NetCraft web client, connecting to the Netty sample server - this fails with the same "craft.js:9451 WebSocket connection to 'ws://localhost:4081/' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received". So, this points to something incompatible emscripten is doing.

For comparison, this is the working Netty web socket test page, straightforward use of the WebSocket interface:

<html><head><title>Web Socket Test</title></head>
<body>
<script type="text/javascript">
var socket;
if (!window.WebSocket) {
  window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
  //socket = new WebSocket("ws://localhost:8080/websocket");
  //socket = new WebSocket("ws://localhost:8080/");
  socket = new WebSocket("ws://localhost:4081/");
  socket.onmessage = function(event) {
    var ta = document.getElementById('responseText');
    ta.value = ta.value + '\n' + event.data
  };
  socket.onopen = function(event) {
    var ta = document.getElementById('responseText');
    ta.value = "Web Socket opened!";
  };
  socket.onclose = function(event) {
    var ta = document.getElementById('responseText');
    ta.value = ta.value + "Web Socket closed"; 
  };
} else {
  alert("Your browser does not support Web Socket.");
}

function send(message) {
  if (!window.WebSocket) { return; }
  if (socket.readyState == WebSocket.OPEN) {
    socket.send(message);
  } else {
    alert("The socket is not open.");
  }
}
</script>
<form onsubmit="return false;">
<input type="text" name="message" value="Hello, World!"/><input type="button" value="Send Web Socket Data"
       onclick="send(this.form.message.value)" />
<h3>Output</h3>
<textarea id="responseText" style="width:500px;height:300px;"></textarea>
</form>
</body>
</html>

Found this hint in the generated craft.js -- emscripten defaults to the "binary" subprotocol!

          } else {
            // create the actual websocket object and connect
            try {
              // runtimeConfig gets set to true if WebSocket runtime configuration is available.
              var runtimeConfig = (Module['websocket'] && ('object' === typeof Module['websocket']));
              
              // The default value is 'ws://' the replace is needed because the compiler replaces '//' comments with '#'
              // comments without checking context, so we'd end up with ws:#, the replace swaps the '#' for '//' again.
              var url = 'ws:#'.replace('#', '//');
              
              if (runtimeConfig) {
                if ('string' === typeof Module['websocket']['url']) {
                  url = Module['websocket']['url']; // Fetch runtime WebSocket URL config.
                }
              }
              
              if (url === 'ws://' || url === 'wss://') { // Is the supplied URL config just a prefix, if so complete it.
                var parts = addr.split('/'); 
                url = url + parts[0] + ":" + port + "/" + parts.slice(1).join('/');
              }
              
              // Make the WebSocket subprotocol (Sec-WebSocket-Protocol) default to binary if no configuration is set.
              var subProtocols = 'binary'; // The default value is 'binary'
              
              if (runtimeConfig) {
                if ('string' === typeof Module['websocket']['subprotocol']) {
                  subProtocols = Module['websocket']['subprotocol']; // Fetch runtime WebSocket subprotocol config.
                }
              }
              
              // The regex trims the string (removes spaces at the beginning and end, then splits the string by
              // <any space>,<any space> into an Array. Whitespace removal is important for Websockify and ws.
              subProtocols = subProtocols.replace(/^ +| +$/g,"").split(/ *, */);
              
              // The node ws library API for specifying optional subprotocol is slightly different than the browser's.
              var opts = ENVIRONMENT_IS_NODE ? {'protocol': subProtocols.toString()} : subProtocols;
              
              // If node we use the ws library.
              var WebSocketConstructor;
              if (ENVIRONMENT_IS_NODE) {
                WebSocketConstructor = require('ws');
              } else if (ENVIRONMENT_IS_WEB) {
                WebSocketConstructor = window['WebSocket'];
              } else {
                WebSocketConstructor = WebSocket;
              }
              ws = new WebSocketConstructor(url, opts);
              ws.binaryType = 'arraybuffer';

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

No branches or pull requests

1 participant