Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebSocket: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received

Tags:

I'm trying to create a WS connection with my tornado server. The server code is simple:

class WebSocketHandler(tornado.websocket.WebSocketHandler):

    def open(self):
        print("WebSocket opened")

    def on_message(self, message):
        self.write_message(u"You said: " + message)

    def on_close(self):
        print("WebSocket closed")

def main():

    settings = {
        "static_path": os.path.join(os.path.dirname(__file__), "static")
    }


    app = tornado.web.Application([
            (r'/ws', WebSocketHandler),
            (r"/()$", tornado.web.StaticFileHandler, {'path':'static/index.html'}),
        ], **settings)


    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

I copy pasted the client code from here:

$(document).ready(function () {
    if ("WebSocket" in window) {

        console.log('WebSocket is supported by your browser.');

        var serviceUrl = 'ws://localhost:8888/ws';
        var protocol = 'Chat-1.0';
        var socket = new WebSocket(serviceUrl, protocol);

        socket.onopen = function () {
            console.log('Connection Established!');
        };

        socket.onclose = function () {
            console.log('Connection Closed!');
        };

        socket.onerror = function (error) {
            console.log('Error Occured: ' + error);
        };

        socket.onmessage = function (e) {
            if (typeof e.data === "string") {
                console.log('String message received: ' + e.data);
            }
            else if (e.data instanceof ArrayBuffer) {
                console.log('ArrayBuffer received: ' + e.data);
            }
            else if (e.data instanceof Blob) {
                console.log('Blob received: ' + e.data);
            }
        };

        socket.send("Hello WebSocket!");
        socket.close();
    }
});

When it tries to connect i get the following output on the browser's console:

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

Why is that?

like image 638
Midiparse Avatar asked Dec 10 '15 09:12

Midiparse


People also ask

How do I fix WebSocket connection error?

Solution 1Check that all the Bot Insight services are running. Check that your firewall settings are configured to accept incoming websocket data. Try to use a different web browser. Restart the Bot Insight Visualization and Bot Insight Scheduler services.

How do I enable WSS protocol?

- In Control Panel, click Programs and Features, and then click Turn Windows features on or off. Expand Internet Information Services, expand World Wide Web Services, expand Application Development Features, and then select WebSocket Protocol. Click OK. Click Close.

When should you not use a WebSocket?

You might be using WebSockets incorrectly if: The connection is used only for a very small number of events, or a very small amount of time, and the client does not need to quickly react to the events. Your feature requires multiple WebSockets to be open to the same service at once.

Does each WebSocket use a port?

The WebSocket protocol is an independent TCP-based protocol. Its only relationship to HTTP is that its handshake is interpreted by HTTP servers as an Upgrade request. By default the WebSocket protocol uses port 80 for regular WebSocket connections and port 443 for WebSocket connections tunneled over TLS [RFC2818].


1 Answers

As pointed out in whatwg.org's Websocket documentation (it's a copy from the standard's draft):

The WebSocket(url, protocols) constructor takes one or two arguments. The first argument, url, specifies the URL to which to connect. The second, protocols, if present, is either a string or an array of strings. If it is a string, it is equivalent to an array consisting of just that string; if it is omitted, it is equivalent to the empty array. Each string in the array is a subprotocol name. The connection will only be established if the server reports that it has selected one of these subprotocols. The subprotocol names must all be strings that match the requirements for elements that comprise the value of Sec-WebSocket-Protocol fields as defined by the WebSocket protocol specification.

Your server answers the websocket connection request with an empty Sec-WebSocket-Protocol header, since it doesn't support the Chat-1 subprotocol.

Since you're writing both the server side and the client side (and unless your writing an API you intend to share), it shouldn't be super important to set a specific subprotocol name.

You can fix this by either removing the subprotocol name from the javascript connection:

var socket = new WebSocket(serviceUrl);

Or by modifying your server to support the protocol requested.

I could give a Ruby example, but I can't give a Python example since I don't have enough information.

EDIT (Ruby example)

Since I was asked in the comments, here's a Ruby example.

This example requires the iodine HTTP/WebSockets server, since it supports the rack.upgrade specification draft (concept detailed here) and adds a pub/sub API.

The server code can be either executed through the terminal or as a Rack application in a config.ru file (run iodine from the command line to start the server):

# frozen_string_literal: true

class ChatClient
  def on_open client
    @nickname = client.env['PATH_INFO'].to_s.split('/')[1] || "Guest"
    client.subscribe :chat    
    client.publish :chat , "#{@nickname} joined the chat."
    if client.env['my_websocket.protocol']
      client.write "You're using the #{client.env['my_websocket.protocol']} protocol"
    else
      client.write "You're not using a protocol, but we let it slide"
    end
  end
  def on_close client
    client.publish :chat , "#{@nickname} left the chat."
  end
  def on_message client, message
    client.publish :chat , "#{@nickname}: #{message}"
  end
end

module APP
  # the Rack application
  def self.call env
    return [200, {}, ["Hello World"]] unless env["rack.upgrade?"]
    env["rack.upgrade"] = ChatClient.new
    protocol = select_protocol(env)
    if protocol
      # we will use the same client for all protocols, because it's a toy example
      env['my_websocket.protocol'] = protocol # <= used by the client
      [101, { "Sec-Websocket-Protocol" => protocol }, []]
    else
      # we can either refuse the connection, or allow it without a match
      # here, it is allowed
      [101, {}, []]
    end
  end

  # the allowed protocols
  PROTOCOLS = %w{ chat-1.0 soap raw }

  def select_protocol(env)
    request_protocols = env["HTTP_SEC_WEBSOCKET_PROTOCOL"]
    unless request_protocols.nil?
      request_protocols = request_protocols.split(/,\s?/) if request_protocols.is_a?(String)
      request_protocols.detect { |request_protocol| PROTOCOLS.include? request_protocol }
    end # either `nil` or the result of `request_protocols.detect` are returned
  end

  # make functions available as a singleton module
  extend self
end

# config.ru
if __FILE__.end_with? ".ru"
  run APP 
else
# terminal?
  require 'iodine'
  Iodine.threads = 1
  Iodine.listen2http app: APP, log: true
  Iodine.start
end

To test the code, the following JavaScript should work:

ws = new WebSocket("ws://localhost:3000/Mitchel", "chat-1.0");
ws.onmessage = function(e) { console.log(e.data); };
ws.onclose = function(e) { console.log("Closed"); };
ws.onopen = function(e) { e.target.send("Yo!"); };
like image 142
Myst Avatar answered Sep 16 '22 20:09

Myst