[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