如何将 HTTP 请求升级到 Websocket (Autobahn & Twisted Web)
Posted
技术标签:
【中文标题】如何将 HTTP 请求升级到 Websocket (Autobahn & Twisted Web)【英文标题】:How to Upgrade HTTP Request to Websocket (Autobahn & Twisted Web ) 【发布时间】:2017-07-02 21:12:17 【问题描述】:为了让您了解我正在尝试使用 Twisted Web 和 Autobahn websockets 完成什么:我的 UI 当前发送初始 HTTP GET 请求,并在标头中升级到 websocket。在 Twisted Web 中阅读后,连接需要从 HTTP 切换到 websocket 协议来来回传递数据。请注意,此 websocket 升级发生在同一端口 port 8000
。
有谁知道我可以如何实现我正在尝试做的事情?非常感谢。
编辑:更新了工作示例的代码。你可以在这里找到它:Payload from POST Request is Cutoff (Twisted Web)
这是我使用 Twisted Web 的代码:
class HttpResource(resource.Resource):
isLeaf = 1
def __init__(self):
self.children =
self.ws_port = None
print 'resource invoked'
def render_GET(self, request):
print 'render invoked'
if request.getHeader('Sec-WebSocket-Key'):
# Processing the Key as per RFC 6455
key = request.getHeader('Sec-WebSocket-Key')
h = hashlib.sha1(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
request.setHeader('Sec-WebSocket-Accept', base64.b64encode(h.digest()))
# setting response headers
request.setHeader('Upgrade', 'websocket')
request.setHeader('Connection', 'Upgrade')
request.setResponseCode(101)
return ''
else:
log("Regular HTTP GET request.")
return "<html><body style='margin: 0; overflow: hidden;'><iframe style='width: 100%; height: 100%; border: none;' src='http://tsa-graphiql.herokuapp.com/'></iframe></body></html>"
def render_POST(self,request):
log("POST request")
request.setResponseCode(200)
def handle_single_query(self, queryData):
log("Handle single query data.")
return
class HttpWsChannel(http.HTTPChannel):
def dataReceived(self, data):
log('Data received:\n'.format(data))
if data.startswith('GET'):
# This will invoke the render method of resource provided
http.HTTPChannel.dataReceived(self, data)
if data.startswith('POST'):
http.HTTPChannel.dataReceived(self, data)
else:
"""
Pass binary data to websocket class.
"""
ws_protocol = self.site.ws_factory.protocol(self.site.ws_factory.connection_subscriptions)
log(ws_protocol)
#just echo for now
# self.transport.write(data)
class HttpFactory(Site):
"""
Factory which takes care of tracking which protocol
instances or request instances are responsible for which
named response channels, so incoming messages can be
routed appropriately.
"""
def __init__(self, resource):
http.HTTPFactory.__init__(self)
self.resource = resource
self.ws_factory = WsProtocolFactory("ws://127.0.0.1:8000")
self.ws_factory.protocol = WsProtocol
def buildProtocol(self, addr):
try:
channel = HttpWsChannel()
channel.requestFactory = self.requestFactory
channel.site = self
return channel
except Exception as e:
log("Could not build protocol: ".format(e))
site = HttpFactory(HttpResource())
if __name__ == '__main__':
reactor.listenTCP(8000, site)
reactor.run()
编辑 7/8/2017:这是我在下面尝试的新代码。通过onMessage
方法成功接收到websocket 消息。但是 HTTP 请求不起作用。我在 GET 请求中遇到的当前错误是:
<html>
<head><title>404 - No Such Resource</title></head>
<body>
<h1>No Such Resource</h1>
<p>No such child resource.</p>
</body>
</html>
Python 代码
from twisted.web.server import (
Site,
)
from twisted.internet import reactor
from twisted.web.resource import (
Resource,
)
from autobahn.twisted.websocket import (
WebSocketServerProtocol,
WebSocketServerFactory,
)
from autobahn.twisted.resource import (
WebSocketResource,
)
class WebSocketProtocol(WebSocketServerProtocol):
def onConnect(self, request):
print("WebSocket connection request: ".format(request))
def onMessage(self, payload, isBinary):
print("onMessage: ".format(payload))
if __name__ == '__main__':
factory = WebSocketServerFactory()
factory.protocol = WebSocketProtocol
resource = WebSocketResource(factory)
root = Resource()
root.putChild(b"ws", resource)
site = Site(root)
reactor.listenTCP(8000, site)
reactor.run()
【问题讨论】:
【参考方案1】:使用WebSocketResource
将WebSocketServerFactory
作为Site
的一部分公开。
from twisted.web.server import (
Site,
)
from twisted.web.resource import (
Resource,
)
from autobahn.twisted.websocket import (
WebSocketServerProtocol,
WebSocketServerFactory,
)
from autobahn.twisted.resource import (
WebSocketResource,
)
class YourAppProtocol(WebSocketServerProtocol):
def onConnect(self, request):
...
...
def main():
factory = WebSocketRendezvousFactory()
factory.protocol = YourAppProtocol
resource = WebSocketResource(factory)
root = Resource()
root.putChild(b"some-path-segment", resource)
root.putChild(...)
site = Site(root)
reactor.listenTCP(8080, site)
reactor.run()
请求正文被截断的问题可能是因为您的升级协议实现存在错误。 dataReceived
级别没有应用框架,因此您不能指望像 startswith("GET")
这样的检查是可靠的。
使用 WebSocketResource
和 Site
可以为您提供来自 Twisted Web 和 Autobahn 的正确 HTTP 解析代码,同时还允许您将 WebSocket 与特定 URL 对话,并与其他人对话。
【讨论】:
我不认为这段代码显示了如何在与 HTTP 相同的端口上使用高速公路 websocket。我之前已经实现了这段代码,这不是我想要的。我相信我可能有一个使用 Twisted 网络库的工作示例 我已经用一个工作示例的链接更新了描述,但遇到了请求被切断的问题。仍在进一步研究这个问题@jean-paul-calderone 谢谢!我尝试了您的示例,并且能够接收到 websocket 消息。但是,在执行 GET 请求时我得到一个 404,说明没有这样的资源 - 没有找到这样的子资源。 我是否必须使用 render_GET 方法显式定义资源类?另外,我是否必须指定 getChild 方法如何正确路由 websocket 请求?顺便说一句,我正在使用 REST 客户端进行测试。再次感谢! 获取什么时出现 404?【参考方案2】:所以在谷歌上读了一点之后,我发现这个网站解释了如何通过 Autobahn Twisted 将 HTTP 连接升级到 websocket 连接:Read and Set request headers via Autobahn Twisted。
我能够开始工作的代码如下所示!
from twisted.web.server import (
Site,
)
from twisted.internet import reactor
from twisted.web.resource import (
Resource,
)
from autobahn.twisted.websocket import (
WebSocketServerProtocol,
WebSocketServerFactory,
)
from autobahn.twisted.resource import (
WebSocketResource,
)
class HttpResource(Resource):
isLeaf = True
def render_GET(self, request):
return "<html><body style='margin: 0; overflow: hidden;'><iframe style='width: 100%; height: 100%; border: none;' src='http://tsa-graphiql.herokuapp.com/'></iframe></body></html>"
class WebSocketProtocol(WebSocketServerProtocol):
def onConnect(self, request):
custom_header =
if request.headers['sec-websocket-key']:
custom_header['sec-websocket-protocol'] = 'graphql-ws'
return (None, custom_header)
def onMessage(self, payload, isBinary):
print("onMessage: ".format(payload))
if __name__ == '__main__':
factory = WebSocketServerFactory()
factory.protocol = WebSocketProtocol
resource = WebSocketResource(factory)
root = Resource()
root.putChild("", HttpResource())
root.putChild(b"ws", ws_resource)
site = Site(root)
reactor.listenTCP(8000, site)
【讨论】:
以上是关于如何将 HTTP 请求升级到 Websocket (Autobahn & Twisted Web)的主要内容,如果未能解决你的问题,请参考以下文章
已解决 - 如何在 Spring 中自定义 WebSocket 升级请求的 HTTP 响应?
websocket 升级是不是仍然允许 http ajax 请求?
EasyRTC编译中Golang 将 http 请求升级为 websocket实现过程分享
.NET 库,可以将套接字从 HTTP 切换到 WebSocket 协议