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

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


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: trueclass 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}"  endendmodule 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 selfend# config.ruif __FILE__.end_with? ".ru"  run APP else# terminal?  require 'iodine'  Iodine.threads = 1  Iodine.listen2http app: APP, log: true  Iodine.startend

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!"); };


For those who use cloudformation templates, AWS has a nice example here.

UPDATE

The key thing is the response in the connection function. On the abovementioned AWS shows how this can be done:

exports.handler = async (event) => {    if (event.headers != undefined) {        const headers = toLowerCaseProperties(event.headers);                if (headers['sec-websocket-protocol'] != undefined) {            const subprotocolHeader = headers['sec-websocket-protocol'];            const subprotocols = subprotocolHeader.split(',');                        if (subprotocols.indexOf('myprotocol') >= 0) {                const response = {                    statusCode: 200,                    headers: {                        "Sec-WebSocket-Protocol" : "myprotocol"                    }                };                return response;            }        }    }        const response = {        statusCode: 400    };            return response;};function toLowerCaseProperties(obj) {    var wrapper = {};    for (var key in obj) {        wrapper[key.toLowerCase()] = obj[key];    }    return wrapper;}       

Please note the header settings in the response. Also this response must be delivered to the requester, for this response integration must be configured.

In the AWS example consider the code:

MyIntegration:Type: AWS::ApiGatewayV2::IntegrationProperties:  ApiId: !Ref MyAPI  IntegrationType: AWS_PROXY  IntegrationUri: !GetAtt MyLambdaFunction.Arn  IntegrationMethod: POST  ConnectionType: INTERNET 

The most important are the last two lines.