Skip to content

Adobe Flash Served by Python

The past few versions of Flash have had support for sending and receiving XML documents using the HTTP protocol. The latest version of Flash, MX 2004, has a component that supports communication using the Simple Object Access Protocol. This feature allows a Flash program to call web services that publish their interface using the Web Service Definition Language (WSDL).

I find SOAP to be a bit too rich on features for simple message passing. XML-RPC is a simpler alternative. Flash does not support XML-RPC directly, but the features for sending and receiving XML documents can be used to create a XML-RPC library. This library can be used to send and receive information using remote procedure calls to a service written in another language like Java, Perl, or Python.

In this tutorial, I will explain how to use a XML-RPC library for Flash to communicate with a Python web service. While I’ve chosen Python to implement the service provider, any other language with support for XML-RPC could be used instead.

Prerequistes

First, you will need to have a copy of Flash MX 2004 or higher installed. A demo can be downloaded from Adobe’s website. The full version can be purchased there as well. Make sure you reboot after install if prompted so that the newest Flash Player plugin is initialized properly.

Second, you will need to download and install Python 2.2 or higher to support the Python web service.

Finally, you will need a copy of my Flash XML-RPC library. This library can be placed anywhere on the hard drive, as long as its parent directory is included in the Flash classpath (Edit->Preferences->ActionScript-> ActionScript 2.0 Settings button. Add the parent directory of the XMLRPC folder.)

I want to point out that there is at least one alternative to my XML-RPC library for Flash. Patrick O’Lone has written his own Flash XML-RPC client library. Patrick’s library is available at SourceForge and should work equally well with any XML-RPC service.

Python Service

Creating a simple web service in Python is extremely easy. The SimpleXMLRPC server module that comes with Python can turn any class or function into a web server with a few lines of code.

The following example shows a simple echo web service. Whatever data structure the client passes to the service in the parameter is echoed back to it.

from SimpleXMLRPCServer import SimpleXMLRPCServer

class EchoClass:
  def echo(self, data):
    return data

#create an instance of the class
s = EchoClass()
#create the server on localhost
server = SimpleXMLRPCServer(('', 8000))
#register the instance as a web service
server.register_instance(s)
#start accepting requests
server.serve_forever()

The neat thing about this example is that we’re not limited to simple data types. An array of structs could be passed to our echo function and be used in Python as an array of dictionaries without any problem. A complex data structure like this could also be passed back from Python, and converted to its analog on the client side. In Flash, an array of structs would be converted to an array of objects with properties with the struct names and values.

Flash Client

Constructing the Flash client is just about as simple. There is only one significant design consideration to note when working with remote procedure calls in Flash: a call to a remote procedure is asynchronous. This means that calling a remote function will not cause the client to block until a value is returned from the service. Instead, the client receives an event at a later time if and when the return value is ready.

The following example shows the code for a simple Flash client to the echo server created above. If the code is placed in the first frame of the timeline, it will execute immediately when the program is started and print the response from the server when it is ready.

/*
Place all of this code into the first frame of the timeline to get it to
execute when the movie starts.
*/

//make a listener object
listener = new Object();
listener.Onecho = function(event) {
  if(event.target.IsFault()) {
    trace(event.target.Fault.code);
    trace(event.target.Fault.description);
  } else {
   trace(event.target.Data);
  }
}

//point to the XMLRPC server
server = new XMLRPC.ProxyServer("http://localhost:8000")

//build a complex array of data to pass to the echo function
var data = [{property1: 'This is a struct value', foo: 1}, 'This is a
  string', [1,2,3], new Date(), true]

//call the function Echo with an array of params; in this case the data array
//is the only parameter; register the listener as the object that holds the
//callback with the name OnEcho
server.Call(”echo”, [data], listener)

Of course, calling remote procedures and receiving the results can and should be wrapped in a class to encapsulate the dirty details. The class making the call can be registered as the listener for the callback to hide some of the asynchronous behavior.

Issues

The above examples will work as long as the client and server are running within the same domain. Any Flash client outside the domain of the service will fail. The reason for this failure is security. Flash prevents a movie from accessing a web service unless that service permits Flash to do so.

Allowing clients from additional domains access to a service is pretty straightfoward. A Flash client outside the domain of the service will request a file named crossdomain.xml before attempting to access the service. If the crossdomain file states that the domain of the client should be allowed access, then the client continues to access the service. If it states otherwise, or the file does not exist, the client will refuse to access the service.

The most open (and arguably insecure) crossdomain file is shown below. It’s fine for our echo service. It should be placed in the same folder as the Python echo server.

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
  "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">

  <cross-domain-policy>
  <allow-access-from domain="*" />
</cross-domain-policy>

The missing piece to this puzzle is a method for giving a Flash client access to the crossdomain file through our XMLRPC server. Flash requests the XML file using the standard HTTP GET method with the path /crossdomain.xml. Unfortunately, the Python SimpleXMLRPCServer class does not support the GET method by default. Thankfully, remedying this problem is made simple by overriding the SimpleXMLRPCHandler class to support the GET method without sacrificing any existing XML-RPC functions.

The Python service should be changed to mimic the code below. The only major changes are the additional import of the SimpleXMLRPCHandler class, the definition of the ExtendedXMLRPCHandler class, and the use of the ExtendedXMLRPCHandler class in the costructor of the SimpleXMLRPCServer.

from SimpleXMLRPCServer import SimpleXMLRPCServer,SimpleXMLRPCRequestHandler

class ExtendedXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
  def do_GET(self):
    #only allow a request for the crossdomain file
    #this can be changed to support GETs of any file if needed
    if self.path != '/crossdomain.xml':
      self.send_response(403)
      self.log_request(403)
      return

    #open the crossdomain file and read its contents
    f = file('crossdomain.xml', 'r')
    msg = f.read()
    f.close()

    #write the data to the socket along with valid HTTP headers
    res = 'HTTP/1.0 200 OK\r\nDate: %s\r\n\r\n%s' % \
      (self.date_time_string(),msg)
    self.wfile.write(res)
    self.log_request(200)

class EchoClass:
  def echo(self, data):
    return data

#create an instance of the class
s = EchoClass()
#create the server on localhost
server = SimpleXMLRPCServer(('', 8000), ExtendedXMLRPCRequestHandler)
#register the instance as a web service
server.register_instance(s)
#start accepting requests
server.serve_forever()

References