如何将HTTP请求升级到Websocket(Autobahn&Twisted Web)

如何将HTTP请求升级到Websocket(Autobahn&Twisted Web)

问题描述:

为了让您了解我正在尝试使用Twisted Web和Autobahn websockets完成什么:我的UI当前发送一个初始HTTP GET请求并升级到websocket在标题中。在Twisted Web中读取时,连接需要从HTTP切换到Websocket协议以来回传递数据。请注意,此websocket升级发生在同一端口上,port 8000如何将HTTP请求升级到Websocket(Autobahn&Twisted Web)

有谁知道我可以如何实现我想要做的?非常感谢。

编辑:工作示例的更新代码。你可以在这里找到它:Payload from POST Request is Cutoff (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() 

编辑2017年7月8日:这里是我的尝试下面的新代码。 websocket消息通过onMessage方法成功接收。但是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() 

因此,在Google上稍微阅读一下后,我发现这个网站介绍了如何通过Autobahn Twisted:Read and Set request headers via Autobahn Twisted将HTTP连接升级到websocket连接。

我能够开始工作的代码如下所示!

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) 

使用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")这样的检查是可靠的。

使用WebSocketResourceSite让你从扭曲的Web和高速公路正确的HTTP解析代码,同时还允许你说话的WebSocket到一个特定的URL和普通的HTTP给他人。

+0

我不认为这段代码展示了如何在与HTTP相同的端口上使用autobahn websocket。我之前已经实现了这个代码,这不是我想要的。我相信我可能有一个使用扭曲网络库的工作示例 – Brian

+0

我已经使用指向正在运行的示例的链接更新了描述,但遇到请求被截断的问题。仍在深入研究这个问题@ jean-paul-calderone – Brian

+0

谢谢!我试过你的例子,我能够接收websocket消息。然而,当我执行一个GET请求时,我得到了一个404,说明没有这样的资源 - 找不到这样的子资源。 – Brian