[Twisted-Python] AMP WebSockets bridging
Laurens Van Houtven
_ at lvh.cc
Fri Jun 15 11:13:22 MDT 2012
Hi,
I'm trying to get browsers to communicate with an AMP API. Right now I'm trying to do that using WebSockets with Corbin SImpson's txWS. Eventually I'll try to write a Twisted implementation of SockJS, which is basically portable websockets all the way down to IE6.
The issue is that while AMP is inherently a binary protocol, WebSockets transfer text (well, that's a lie: there's a binary version, but it's even less widely supported than WebSockets themselves -- and SockJS doesn't support it at all, although it's on the roadmap). So, I need an alternative, preferably 7-bit, serialization for AMP boxes.
My first attempt at that serialization is JSON -- widely supported in browsers, and should map pretty closely to what AMP can do:
- AMPLIsts are objects
- ListOfs are arrays
- numeric types and strings are more or less natively supported (except JSON is always text, never bytes)
- booleans are booleans
Right now I have something that almost works, but is really a crude, cheap, untested hack. I'm wondering if there's a better integration point.
"""
AMP over WebSockets support.
"""
import json
import txws
from twisted.internet import defer, protocol
from twisted.python import log
class Bridge(protocol.Protocol):
"""
Two-way AMP over WebSockets bridge.
"""
def __init__(self, amp):
self._amp = amp
amp.startReceivingBoxes(self)
amp.boxSender = self
def sendBox(self, box):
"""
Sends a box over the WebSocket as JSON.
"""
log.msg("Sending box: {}".format(box))
self.transport.write(json.dumps(box))
def jsonObjectReceived(self, obj):
"""
Hands the JSON object (dict) over to ampBoxReceived.
"""
log.msg("JSON object received: {}".format(obj))
self._amp.boxReceiver.ampBoxReceived(obj)
def dataReceived(self, data):
"""
Calls jsonObjectReceived.
This assumes that JSON objects will always arrive as 1 chunk.
"""
self.jsonObjectReceived(json.loads(data))
class BridgeFactory(protocol.Factory):
"""
A factory for AMP over WebSockets bridges.
"""
def __init__(self, ampFactory):
self._ampFactory = ampFactory
def buildProtocol(self, addr):
"""
Builds a bridge and associates it with an AMP protocol instance.
"""
return Bridge(self._ampFactory.buildProtocol(addr))
def makeFactory(ampFactory):
"""
Makes a WebSocket factory that bridges AMP messages.
"""
return txws.WebSocketFactory(BridgeFactory(ampFactory))
An issue I'm running into is an AMP ListOf. With the above code, ListOf still gets translated to a string, and obviously I want it to be a list. When that ListOf has actual data in it, I get the binary representation.
The other issues that I predict will happen but haven't ran into yet (because I don't use that functionality) are:
- booleans -- expects "True" or "False" but gets True or False (true and false in JSON) -- can be fixed both on the client and the server side, not sure where the optimal place would be
- AMPList -- same reason as ListOf
ints, floats… will magically work, but it's a bit ugly: the only reason it works is that the factory for them is int and float, and, well, int(5) == 5 == int("5"), so it doesn't actually matter if it's already an int or a str.
In my Command implementation, I'm returning {"k": anAxiomQuery} -- so it's iterable but not a list. I can write a custom JSON encoder that can take arbitrary iterables, of course.
cheers
lvh
More information about the Twisted-Python
mailing list