WebSocket: WebSocket handshake error: Non-empty "Sec-WebSocket-Protocol" header sent, but no response received

I am trying to create a WS connection to my tornado server. 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 will copy 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 in the browser 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 this?

+9
python websocket
source share
1 answer

As stated in the documentation for whatwg.org Websocket (this is a copy from a standard project):

The constructor of WebSocket (url, protocol) takes one or two arguments. The first argument, url, specifies the URL to connect to. Second, the protocols, if any, are either a string or an array of strings. If it is a string, it is equivalent to an array consisting only of that string; if omitted, it is equivalent to an empty array. Each row in the array is a subprotocol name. A connection will be established only if the server reports that it has selected one of these sub-protocols . Subprotocol names must be strings that meet the requirements for elements that contain the value of the Sec-WebSocket-Protocol fields, as defined in the WebSocket protocol specification.

Your server responds to a websocket connection request with an empty Sec-WebSocket-Protocol header, as it does not support the Chat-1 sub Chat-1 protocol.

Since you write both on the server side and on the client side (and if you do not write the API that you intend to share), it should not be super important to set a specific sub-protocol name.

This can be fixed by removing the subprotocol name from the javascript connection:

 var socket = new WebSocket(serviceUrl); 

Or changing your server to support the requested protocol.

I could give an example of Ruby, but I cannot give an example of Python, since I do not have enough information.

EDIT (Ruby example)

Since I was asked in the comments, here is an example of Ruby.

In this example, the iodine HTTP / WebSockets server is required because it supports the rack.upgrade specification of the rack.upgrade (the concept is described in detail here ) and adds the pub / sub API.

Server code can be executed either through the terminal or as a Rack application in the 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 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!"); }; 
+17
source share

All Articles