[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pyamf-commits
Subject:    [pyamf-commits] r85 - branches/remoting-2/pyamf
From:       pyamf-commits () collab ! com (pyamf-commits () collab ! com)
Date:       2007-10-27 23:24:53
Message-ID: 20071027212445.CA9557BC069 () mail ! collab ! com
[Download RAW message or body]

Author: nick
Date: 2007-10-27 23:24:45 +0200 (Sat, 27 Oct 2007)
New Revision: 85

Modified:
   branches/remoting-2/pyamf/messaging.py
Log:
Added messaging support

Modified: branches/remoting-2/pyamf/messaging.py
===================================================================
--- branches/remoting-2/pyamf/messaging.py	2007-10-27 19:17:55 UTC (rev 84)
+++ branches/remoting-2/pyamf/messaging.py	2007-10-27 21:24:45 UTC (rev 85)
@@ -25,237 +25,187 @@
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-"""AMF Remoting support"""
+"""AMF Messaging support"""
 
-class AMFMessageDecoder(object):
+import pyamf
+from pyamf import util
 
-    def __init__(self, data):
-        self.input = util.BufferedByteStream(data)
-        self.msg = AMFMessage()
+AMF0 = 0
+AMF3 = 3
 
-    def decode(self):
-        # The first byte of the AMF file/stream is the AMF type.
-        self.msg.amfVersion = self.input.read_uchar()
-        if self.msg.amfVersion == GeneralTypes.AMF0:
-            parser_class = amf0.Parser
-        elif self.msg.amfVersion == GeneralTypes.AMF3:
-            from pyamf import amf3
+class ClientTypes:
+    # Specifies aFlash Player 6.0 - 8.0 client.
+    Flash    = 0
+    # Specifies a FlashCom / Flash Media Server client.
+    FlashCom = 1
+    # Specifies a Flash Player 9.0 client.
+    Flash9   = 3
 
-            parser_class = amf3.Parser
-        else:
-            raise Exception("Invalid AMF version: " + str(self.msg.amfVersion))
-        # The second byte is the client type.
-        self.msg.clientType = self.input.read_uchar()
-        # The third and fourth bytes form an integer value that specifies
-        # the number of headers.
-        header_count = self.input.read_short()
-        # Headers are used to request debugging information, send 
-        # authentication info, tag transactions, etc.
-        for i in range(header_count):
-            header = AMFMessageHeader()
-            # UTF string (including length bytes) - header name.
-            name_len = self.input.read_ushort()
-            header.name = self.input.read_utf8_string(name_len)
-            # Specifies if understanding the header is "required".
-            header.required = bool(self.input.read_uchar())
-            # Long - Length in bytes of header.
-            header.length = self.input.read_ulong()
-            # Variable - Actual self.input (including a type code).
-            header.data = parser_class(self.input).readElement()
-            self.msg.headers.append(header)
-        # Between the headers and the start of the bodies is a int 
-        # specifying the number of bodies.
-        bodies_count = self.input.read_short()
-        # A single AMF envelope can contain several requests/bodies; 
-        # AMF supports batching out of the box.
-        for i in range(bodies_count):
-            body = AMFMessageBody()
-            # The target may be one of the following:
-            # - An http or https URL. In that case the gateway should respond 
-            #   by sending a SOAP request to that URL with the specified data. 
-            #   In that case the data will be an Array and the first key 
-            #   (data[0]) contains the parameters to be sent.
-            # - A string with at least one period (.). The value to the right 
-            #   of the right-most period is the method to be called. The value 
-            #   to the left of that is the service to be invoked including package 
-            #   name. In that case data will be an Array of arguments to be sent 
-            #   to the method.
-            target_len = self.input.read_ushort()
-            body.target = self.input.read_utf8_string(target_len)
-            # The response is a string that gives the body an id so it can be 
-            # tracked by the client.
-            response_len = self.input.read_ushort()
-            body.response = self.input.read_utf8_string(response_len)
-            # Body length in bytes.            from pyamf import amf0
+CLIENT_TYPES = set(
+    ClientTypes.__dict__[x] for x in ClientTypes.__dict__
+    if not x.startswith('_'))
 
-            body.length = self.input.read_ulong()
-            # Actual data (including a type code).
-            body.data = parser_class(self.input).readElement()
-            # Bodies contain actual Remoting requests and responses.
-            self.msg.bodies.append(body)
+class Envelope(object):
+    _amf_version = None
+    _client_type = None
+    headers = []
+    bodies = []
 
-        return self.msg
-
-class AMFMessageEncoder:
-
-    def __init__(self, msg):
-        self.output = util.BufferedByteStream()
-        self.msg = msg
-
-    def encode(self):
-
-        encoder_class = amf0.Encoder
-        # Write AMF version.
-        self.output.write_uchar(self.msg.amfVersion)
-        # Client type.
-        self.output.write_uchar(self.msg.clientType)
-        # Header length.
-        header_count = len(self.msg.headers)
-        self.output.write_short(header_count)
-        # Write headers.
-        for header in self.msg.headers:
-            # Write header name.
-            self.output.write_utf8_string(header.name)
-            # Write header requirement.
-            self.output.write_uchar(header.required)
-            # Write length in bytes of header.
-            self.output.write_ulong(-1)
-            # Header data.
-            encoder_class(self.output).writeElement(header.data)
-        # Write bodies length.
-        bodies_count = len(self.msg.bodies)
-        self.output.write_short(bodies_count)
-        # Write bodies.
-        for body in self.msg.bodies:
-            # Target (/1/onResult).
-            self.output.write_utf8_string(body.target)
-            # Response (null).
-            self.output.write_utf8_string(body.response)
-            # Body length in bytes.
-            self.output.write_ulong(-1)
-            # Actual Python result data.
-            encoder_class(self.output).writeElement(body.data)
-
-        return self.output
-
-class AMFMessage:
-
-    def __init__(self):
-        self.amfVersion = GeneralTypes.AMF0
-        self.clientType = GeneralTypes.AC_Flash
-        self.headers = []
-        self.bodies = []
-
     def __repr__(self):
-        r = "<AMFMessage amfVersion=" + str(self.amfVersion) + " clientType=" + \
str(self.clientType) + "\n" +        objs = []
+        r = "<Envelope amfVersion=%s clientType=%s\n" %
+            (self.amfVersion, self.clientType)
+
         for h in self.headers:
             r += "   " + repr(h) + "\n"
         for b in self.bodies:
             r += "   " + repr(b) + "\n"
         r += ">"
+
         return r
 
-class AMFMessageHeader:
+    def _get_amf_version(self):
+        return self._amf_version
 
-    def __init__(self):
-        self.name = None
-        self.required = None
-        self.length = None
-        self.data = None
+    def _set_amf_version(self, version):
+        if not version in (AMF0, AMF3):
+            raise ValueError("Unknown version %s" % str(version))
 
-    def __repr__(self):
-        return "<AMFMessageHeader name=%s data=%r>" % (self.name, self.data)
+        self._amf_version = version
 
-class AMFMessageBody:
+    def _get_client_type(self):
+        return self._client_type
 
-    def __init__(self):
-        self.target = None
-        self.response = None
-        self.length = None
-        self.data = None
+    def _set_client_type(self, type):
+        if not type in CLIENT_TYPES:
+            raise ValueError("Unknown client type %s" % str(version))
 
+    amfVersion = property(_get_amf_version, _set_amf_version)
+
+class Header(object):
+    name = None
+    required = None
+    data = None
+
     def __repr__(self):
-        return "<AMFMessageBody target=%s data=%r>" % (self.target, self.data)
+        return "<Header name=%s data=%r>" % (self.name, self.data)
 
-class Server:
-    def __init__(self, data):
-        if data:
-            self.request = AMFMessageDecoder(data).decode()
-            self.response = AMFMessage()
-        else:
-            raise Exception("Invalid AMF request received")
+class Body(object):
+    data = None
+    target = None
+    response = None
 
     def __repr__(self):
-        return "<Server request=%s response=%d>" % (self.request, self.response)
+        return "<Body target=%s data=%r>" % (self.target, self.data)
 
-    def setResponse(self, target, type, data):
-        """
-        Set a response based on a request you received through getRequests.
-        """
-        if type == GeneralTypes.REMOTING_RESULT:
-            target += '/onResult'
+def _get_amf_decoder(version):
+    if version == AMF0:
+        import pyamf.amf0
 
-        elif type == GeneralTypes.REMOTING_STATUS:
-            target += '/onStatus'
+        return pyamf.amf0.Parser
+    elif version == AMF3:
+        import pyamf.amf3
 
-        elif type == GeneralTypes.REMOTING_DEBUG:
-            target += '/onDebugEvents'
+        return pyamf.amf3.Parser
 
-        body = AMFMessageBody()
-        body.target = target
-        body.response = ''
-        body.data = data
+def _get_amf_encoder(version):
+    if version == AMF0:
+        import pyamf.amf0
 
-        return self.response.bodies.append(body)
+        return pyamf.amf0.Encoder
+    elif version == AMF3:
+        import pyamf.amf3
 
-    def getResponse(self):
-        """
-        Get all responses for the client. Call this after you answered all
-        the requests with setResponse.
-        """
-        self.response.clientType = self.request.clientType
-        data = AMFMessageEncoder(self.response).encode()
-        return data
+        return pyamf.amf3.Encoder
 
-    def addHeader(self, name, required, data):
-            """
-            Add a header to the server response.
-            """
-            self.response.addHeader(name, required, data)
+def _read_header(stream, decoder):
+    header = Header()
 
-    def getRequests(self):
-        """
-        Returns the requests that are made to the gateway.
-        """
-        return self.request.bodies
-    
-    def getHeaders(self):
-        """
-        Returns the request headers.
-        """
-        return self.request.headers
+    name_len = stream.read_ushort()
+    header.name = self.input.read_utf8_string(name_len)
 
-class Client:
-    def __init__(self):
-        self.request = AMFMessage()
-        self.response = AMFMessage()
+    header.required = bool(self.input.read_uchar())
 
-    def __repr__(self):
-        return "<Client endpoint=%s>" % (self.endpoint)
+    # XXX nick: the header length is currently ignored
+    self.input.read_ulong()
+    header.stream = decoder.readElement()
 
-    def setRequest(self, servicePath, data):
-        """
-        setRequest creates the encoded AMF request for the server. It expects  
-        the servicepath and methodname, and the parameters of the methodcall.
-        """
-        body = AMFMessageBody()
-        body.target = servicePath
-        body.response = '/1'
-        body.data = data
-        self.request.bodies.append(body)
-        data = AMFMessageEncoder(self.request).encode()
-        return data.getvalue()
+    return header
 
-    def getResponse(self, data):
-        self.response = AMFMessageDecoder(data).decode()
-        return self.response.bodies
+def _write_header(header, stream, encoder):
+    stream.write_ushort(len(header.name))
+    stream.write_utf8_string(header.name)
+
+    stream.write_uchar(header.required)
+
+    # TODO nick: work out a quick way of getting the header data length    
+    self.output.write_ulong(-1)
+    encoder.writeElement(header.data)
+
+def _read_body(stream, decoder):
+    body = Body()
+
+    target_len = stream.read_ushort()
+    body.target = stream.read_utf8_string(target_len)
+
+    response_len = self.input.read_ushort()
+    body.response = self.input.read_utf8_string(response_len)
+
+    body.length = self.input.read_ulong()
+    body.stream = decoder.readElement()
+
+def _write_body(body, stream, encoder):
+    stream.write_ushort(len(body.target))
+    stream.write_utf8_string(body.target)
+
+    stream.write_ushort(len(body.response))
+    stream.write_utf8_string(body.response)
+
+    # TODO nick: work out a quick way of getting the body data length    
+    stream.write_ulong(-1)
+    encoder.writeElement(body.data)
+
+def decode(stream, context=None):
+    if not isinstance(stream, util.BufferedByteStream):
+        stream = util.BufferedByteStream(stream)
+
+    msg = Envelope()
+
+    try:
+        msg.amfVersion = stream.read_uchar()
+    except ValueError:
+        raise pyamf.ParseError("Unknown AMF version")
+
+    decoder = _get_amf_decoder(msg.amfVersion)(stream, context=context)
+
+    try:
+        msg.clientType = stream.read_uchar()
+    except ValueError:
+        raise pyamf.ParseError("Unknown client type")
+
+    header_count = stream.read_short()
+    msg.headers = [_read_header(stream, decoder) for i in range(header_count)]
+
+    body_count = stream.read_short()
+    msg.bodies = [_read_body(stream, decoder) for i in range(body_count)]
+
+    return msg
+
+def encode(msg, context=None):
+    stream = util.BufferedByteStream()
+
+    encoder = _get_amf_encoder(msg.amfVersion)(stream, context=context)
+
+    stream.write_uchar(msg.amfVersion)
+    stream.write_uchar(msg.clientType)
+    stream.write_short(len(msg.headers))
+
+    for header in msg.headers:
+        _write_header(header, stream, encoder)
+
+    stream.write_short(len(msg.bodies))
+
+    for body in msg.bodies:
+        _write_body(body, stream, encoder)
+
+    return stream


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic