Rails 5 ActionCable WebSockets 未返回升级 headers,状态为 101 升级响应
Rails 5 ActionCable WebSockets is not returning upgrade headers with status 101 Upgrade response
编辑:最后显示,发现实际上创建了升级 header。
我正在使用 action-cable-example 代码库,尝试构建 WebSocket 应用程序。 "Chatty" 应用程序依赖于应用程序中提供的浏览器客户端,可以正常工作。但是,我不会使用该客户端,因为我需要外部物联网连接。因此,我正在尝试将 ws/wss WebSocket 协议实现到外部 non-browser 设备,我在 route.rb 中的连接是:
mount ActionCable.server => '/cable'
我尝试了几个外部客户端,例如 Chrome Simple WebSocket Client extension 和 gem websocket-client-simple using sample/client.rb。在这两种情况下,ActionCable returns 都没有升级 headers。 Chrome 扩展抱怨如下:
WebSocket connection to 'ws://127.0.0.1:3000/cable' failed: Error during WebSocket handshake: 'Upgrade' header is missing
实际的握手表明这是真的,如:
**General**
Request URL:ws://127.0.0.1:3000/cable
Request Method:GET
Status Code:101 Switching Protocols
**Response Headers**
view source
Connection:keep-alive
Server:thin
**Request Headers**
view source
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:Upgrade
Cookie:PPA_ID=<redacted>
DNT:1
Host:127.0.0.1:3000
Origin:chrome-extension://pfdhoblngboilpfeibdedpjgfnlcodoo
Pragma:no-cache
Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
Sec-WebSocket-Key:1vokmzewcWf9e2RwMth0Lw==
Sec-WebSocket-Version:13
Upgrade:websocket
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36
根据标准,响应 header 应为:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
中的Sec-WebSocket-Accept尤为重要,因为它是根据请求header的Sec-WebSocket-Key进行的计算,以确认ws/wss被理解并且切换协议应该发生。
在此过程中,服务器更开心,直到客户端被勾选并关闭连接:
Started GET "/cable" for 127.0.0.1 at 2016-06-16 19:19:17 -0400
ActiveRecord::SchemaMigration Load (1.0ms) SELECT "schema_migrations".* FROM "schema_migrations"
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2016-06-16 19:19:17 -0400
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
Finished "/cable/" [WebSocket] for 127.0.0.1 at 2016-06-16 19:19:18 -0400
看websocket-client-simple,我把返回的WebSocket分解到client.rb,也显示空header。我正在显示代码,然后是 WebSocket:
url = ARGV.shift || 'ws://localhost:3000/cable'
ws = WebSocket::Client::Simple.connect url
#<WebSocket::Client::Simple::Client:0x2cdaf68
@url="ws://localhost:3000/cable",
@socket=#<TCPSocket:fd 3>,
@handshake=<WebSocket::Handshake::Client:0x013231c8
@url="ws://localhost:3000/cable",
@headers={},
@state=:new,
@handler=#<WebSocket::Handshake::Handler::Client11:0x2e88400
@handshake=<WebSocket::Handshake::Client:0x013231c8
@url="ws://localhost:3000/cable",
@headers={},
@state=:new,
@handler=#<WebSocket::Handshake::Handler::Client11:0x2e88400 ...>,
@data="",
@secure=false,
@host="localhost",
@port=3000,
@path="/cable",
@query=nil,
@version=13>,
@key="KUJ0/C0rvoCMruW8STp0Sw==">,
@data="",
@secure=false,
@host="localhost",
@port=3000,
@path="/cable",
@query=nil,
@version=13>,
@handshaked=false,
@pipe_broken=false,
@closed=false,
@__events=[{:type=>:__close, :listener=>#<Proc:0x2d10ae8@D:/Bitnami/rubystack-2.2.5-3/projects/websocket-client-simple/lib/websocket-client-simple/client.rb:37>, :params=>{:once=>true}, :id=>0}],
@thread=#<Thread:0x2d10a70@D:/Bitnami/rubystack-2.2.5-3/projects/websocket-client-simple/lib/websocket-client-simple/client.rb:42 sleep>
>;
在此响应中,我注意到实例变量“@handshaked”返回为 false。这可能是相关的,但到目前为止我还没有找到在代码中设置或引用它的位置。
更新:
发现 WebSocket::Driver.start 实际上创建了升级 headers。并且,@socket.write(response) 应该 通过 EventMachine 将它们发送出去。
代码:
def start
return false unless @ready_state == 0
response = handshake_response
return false unless response
@socket.write(response)
open unless @stage == -1
true
end
handshake_response 是:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: iJVnsG1ApNMFzABXGDSHN1V0i/s=
问题是我试图在开发中使用瘦服务器。它会运作。然而,它实际上在处理过程中传输响应 headers,例如:
Response Headers
Connection:keep-alive
Server:thin
ActionCable 实际上发送了适当的升级 headers,但它只是在 Thin 发送了自己的 headers 之后才这样做,因此客户端无法识别它们。
转换回 Puma 后,我按预期收到了这些:
Response Headers
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: XJOmp1e2IwQIMk5n0JV/RZZSIhs=
编辑:最后显示,发现实际上创建了升级 header。
我正在使用 action-cable-example 代码库,尝试构建 WebSocket 应用程序。 "Chatty" 应用程序依赖于应用程序中提供的浏览器客户端,可以正常工作。但是,我不会使用该客户端,因为我需要外部物联网连接。因此,我正在尝试将 ws/wss WebSocket 协议实现到外部 non-browser 设备,我在 route.rb 中的连接是:
mount ActionCable.server => '/cable'
我尝试了几个外部客户端,例如 Chrome Simple WebSocket Client extension 和 gem websocket-client-simple using sample/client.rb。在这两种情况下,ActionCable returns 都没有升级 headers。 Chrome 扩展抱怨如下:
WebSocket connection to 'ws://127.0.0.1:3000/cable' failed: Error during WebSocket handshake: 'Upgrade' header is missing
实际的握手表明这是真的,如:
**General**
Request URL:ws://127.0.0.1:3000/cable
Request Method:GET
Status Code:101 Switching Protocols
**Response Headers**
view source
Connection:keep-alive
Server:thin
**Request Headers**
view source
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:Upgrade
Cookie:PPA_ID=<redacted>
DNT:1
Host:127.0.0.1:3000
Origin:chrome-extension://pfdhoblngboilpfeibdedpjgfnlcodoo
Pragma:no-cache
Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
Sec-WebSocket-Key:1vokmzewcWf9e2RwMth0Lw==
Sec-WebSocket-Version:13
Upgrade:websocket
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36
根据标准,响应 header 应为:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
中的Sec-WebSocket-Accept尤为重要,因为它是根据请求header的Sec-WebSocket-Key进行的计算,以确认ws/wss被理解并且切换协议应该发生。
在此过程中,服务器更开心,直到客户端被勾选并关闭连接:
Started GET "/cable" for 127.0.0.1 at 2016-06-16 19:19:17 -0400
ActiveRecord::SchemaMigration Load (1.0ms) SELECT "schema_migrations".* FROM "schema_migrations"
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2016-06-16 19:19:17 -0400
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
Finished "/cable/" [WebSocket] for 127.0.0.1 at 2016-06-16 19:19:18 -0400
看websocket-client-simple,我把返回的WebSocket分解到client.rb,也显示空header。我正在显示代码,然后是 WebSocket:
url = ARGV.shift || 'ws://localhost:3000/cable'
ws = WebSocket::Client::Simple.connect url
#<WebSocket::Client::Simple::Client:0x2cdaf68
@url="ws://localhost:3000/cable",
@socket=#<TCPSocket:fd 3>,
@handshake=<WebSocket::Handshake::Client:0x013231c8
@url="ws://localhost:3000/cable",
@headers={},
@state=:new,
@handler=#<WebSocket::Handshake::Handler::Client11:0x2e88400
@handshake=<WebSocket::Handshake::Client:0x013231c8
@url="ws://localhost:3000/cable",
@headers={},
@state=:new,
@handler=#<WebSocket::Handshake::Handler::Client11:0x2e88400 ...>,
@data="",
@secure=false,
@host="localhost",
@port=3000,
@path="/cable",
@query=nil,
@version=13>,
@key="KUJ0/C0rvoCMruW8STp0Sw==">,
@data="",
@secure=false,
@host="localhost",
@port=3000,
@path="/cable",
@query=nil,
@version=13>,
@handshaked=false,
@pipe_broken=false,
@closed=false,
@__events=[{:type=>:__close, :listener=>#<Proc:0x2d10ae8@D:/Bitnami/rubystack-2.2.5-3/projects/websocket-client-simple/lib/websocket-client-simple/client.rb:37>, :params=>{:once=>true}, :id=>0}],
@thread=#<Thread:0x2d10a70@D:/Bitnami/rubystack-2.2.5-3/projects/websocket-client-simple/lib/websocket-client-simple/client.rb:42 sleep>
>;
在此响应中,我注意到实例变量“@handshaked”返回为 false。这可能是相关的,但到目前为止我还没有找到在代码中设置或引用它的位置。
更新: 发现 WebSocket::Driver.start 实际上创建了升级 headers。并且,@socket.write(response) 应该 通过 EventMachine 将它们发送出去。 代码:
def start
return false unless @ready_state == 0
response = handshake_response
return false unless response
@socket.write(response)
open unless @stage == -1
true
end
handshake_response 是:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: iJVnsG1ApNMFzABXGDSHN1V0i/s=
问题是我试图在开发中使用瘦服务器。它会运作。然而,它实际上在处理过程中传输响应 headers,例如:
Response Headers
Connection:keep-alive
Server:thin
ActionCable 实际上发送了适当的升级 headers,但它只是在 Thin 发送了自己的 headers 之后才这样做,因此客户端无法识别它们。
转换回 Puma 后,我按预期收到了这些:
Response Headers
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: XJOmp1e2IwQIMk5n0JV/RZZSIhs=