AIR 2.0 Web Server using the New Server Socket API

AIR 2.0 Web Server using the New Server Socket API

After exploring Java integration using the new Native Process API (here and here), Excel integration using the new file.openWithDefaultApplication(), and the new Microphone API, here is another application I wrote, this time to explore the new Adobe AIR 2.0 Server Socket API.

The “Mini AIR Web Server” is a simplistic implementation of an HTTPServer. Needless to say that it is far from being a production quality Web Server. The goal here is simply to use the well understood mechanics of a web server to learn how to use server sockets in AIR 2.0.

Installation Instructions

  1. Download the AIR 2.0 beta runtime here.
  2. Download MiniAIRWebServer.air here.
  3. Double-click MiniAIRWebServer.air in Explorer or Finder to start the installation process.

To test the application, open a browser and test the sample HTML pages provided with the application:

NOTE: You may have to change the port number in these URLs depending on the port number you enter in the application.

You can add your own content in the webroot folder in the applicationStorageDirectory.

View Source is enabled, but if you just want to take a quick peak at the code, here is MiniAIRWebServer.mxml:

<?xml version="1.0" encoding="utf-8"?>
<!-- Author: Christophe Coenraets http://coenraets.org -->
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
					   xmlns:s="library://ns.adobe.com/flex/spark"
					   xmlns:mx="library://ns.adobe.com/flex/halo"
					   title="Mini AIR Web Server"
					   applicationComplete="init()" viewSourceURL="srcview/index.html">
	<s:layout>
		<s:VerticalLayout paddingTop="8" paddingLeft="8" paddingRight="8" paddingBottom="8"/>
	</s:layout>

	<fx:Script>
		<![CDATA[
			import flash.events.Event;
			import flash.events.ProgressEvent;
			import flash.events.ServerSocketConnectEvent;
			import flash.net.ServerSocket;
			import flash.net.Socket;
			import flash.utils.ByteArray;
			import mx.controls.Alert;

			private var serverSocket:ServerSocket;

			private var mimeTypes:Object = new Object();

			private function init():void
			{
				// The mime types supported by this mini web server
				mimeTypes[".css"] 	= "text/css";
				mimeTypes[".gif"] 	= "image/gif";
				mimeTypes[".htm"] 	= "text/html";
				mimeTypes[".html"] 	= "text/html";
				mimeTypes[".ico"] 	= "image/x-icon";
				mimeTypes[".jpg"] 	= "image/jpeg";
				mimeTypes[".js"] 	= "application/x-javascript";
				mimeTypes[".png"] 	= "image/png";

				// Initialize the web server directory (in applicationStorageDirectory) with sample files
				var webroot:File = File.applicationStorageDirectory.resolvePath("webroot");
				if (!webroot.exists)
				{
					File.applicationDirectory.resolvePath("webroot").copyTo(webroot);
				}
			}

			private function listen():void
			{
				try
				{
					serverSocket = new ServerSocket();
					serverSocket.addEventListener(Event.CONNECT, socketConnectHandler);
					serverSocket.bind(Number(port.text));
					serverSocket.listen();
					log.text += "Listening on port " + port.text + "...\n";
				}
				catch (error:Error)
				{
					Alert.show("Port " + port.text +
						" may be in use. Enter another port number and try again.\n(" +
						error.message +")", "Error");
				}
			}

			private function socketConnectHandler(event:ServerSocketConnectEvent):void
			{
				var socket:Socket = event.socket;
				socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
			}

			private function socketDataHandler(event:ProgressEvent):void
			{
				try
				{
					var socket:Socket = event.target as Socket;
					var bytes:ByteArray = new ByteArray();
					socket.readBytes(bytes);
					var request:String = "" + bytes;
					log.text += request;
					var filePath:String = request.substring(4, request.indexOf("HTTP/") - 1);
					var file:File = File.applicationStorageDirectory.resolvePath("webroot" + filePath);
					if (file.exists && !file.isDirectory)
					{
						var stream:FileStream = new FileStream();
						stream.open( file, FileMode.READ );
						var content:ByteArray = new ByteArray();
						stream.readBytes(content);
						stream.close();
						socket.writeUTFBytes("HTTP/1.1 200 OK\n");
						socket.writeUTFBytes("Content-Type: " + getMimeType(filePath) + "\n\n");
						socket.writeBytes(content);
					}
					else
					{
						socket.writeUTFBytes("HTTP/1.1 404 Not Found\n");
						socket.writeUTFBytes("Content-Type: text/html\n\n");
						socket.writeUTFBytes("<html><body><h2>Page Not Found</h2></body></html>");
					}
					socket.flush();
					socket.close();
				}
				catch (error:Error)
				{
					Alert.show(error.message, "Error");
				}
			}

			private function getMimeType(path:String):String
			{
				var mimeType:String;
				var index:int = path.lastIndexOf(".");
				if (index > -1)
				{
					mimeType = mimeTypes[path.substring(index)];
				}
				return mimeType == null ? "text/html" : mimeType; // default to text/html for unknown mime types
			}

		]]>
	</fx:Script>

	<s:HGroup verticalAlign="middle">
		<s:Label text="Port:"/>
		<s:TextInput id="port" text="8888" width="50"/>
		<s:Button label="Listen" click="listen()"/>
	</s:HGroup>

	<s:TextArea id="log" width="100%" height="100%" />

</s:WindowedApplication>
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • LinkedIn
  • StumbleUpon
  • Twitter
This entry was posted in Flex. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

18 Comments

  1. Posted December 7, 2009 at 4:23 pm | Permalink

    Holy shit very very nice… Thx for this little snippet!

  2. Posted December 7, 2009 at 5:10 pm | Permalink

    This is awesome, I just love it!

  3. Posted December 7, 2009 at 5:17 pm | Permalink

    nice solution.

    But the pink code hurt my eyes!!

  4. Gary Mc
    Posted December 7, 2009 at 8:22 pm | Permalink

    Awesome! Now if there was a way for AIR to know when a file changes (I guess it could just poll) then an AIR app could be built for providing a live preview when developing in HTML/CSS/Javascript. Given a folder and HTML file, it could serve the page etc with the above, show the page with the AIR browser, and as soon as a file changes in the folder, refresh the browser!

  5. Posted December 9, 2009 at 2:54 am | Permalink

    Just Great..

  6. Posted December 17, 2009 at 7:49 am | Permalink

    I produced “airhttpd” is a HTTP server by AIR 2.0 too.

  7. Gaurav
    Posted December 22, 2009 at 11:56 pm | Permalink

    Great Work Christophe. However, this comment is mostly a request. Since, you work at Adobe, I would like you to please convince them to create a robust FTP class in AIR2 so that we don’t have to fiddle with sockets. I am an AIR+AJAX developer and sockets are a pain. I am not able to successfully implement an FTP functionality in my AIR app as I have to go through the sockets in detail. It would be a great help to all.

    Thanks, Gaurav

  8. Posted December 28, 2009 at 3:54 am | Permalink

    Thank for the snippets!

  9. James Burke Hubbard
    Posted January 2, 2010 at 6:20 pm | Permalink

    Christophe,

    When I try to compile your source in Flash Builder Beta 2, I get a unspecified error at this socket function.

    private function socketConnectHandler(event:ServerSocketConnectEvent):void

    I would appreciate any insight you might have.

    Are you able to publish your working project file so that we could start from a known working point.

    Outstanding tutorial, thanks for the fantastic work.

    Cheers,

    James

  10. Nathan
    Posted January 3, 2010 at 12:40 am | Permalink

    @James,

    Yeah, same problem with me too… the problem could be found in line 65:

    private function socketConnectHandler(event:ServerSocketConnectEvent):void
    {
    var socket:Socket = event.socket;
    socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
    }

    with the following error message:

    “Type was not found or was not a compile-time constant: ServerSocketConnectEvent.”

    This would be very useful if you can provide us with a solution for this, so we can compile it in our end.

    Thanks a lot Christophe for another great AIR application!

    Nathan

  11. Posted January 14, 2010 at 2:47 pm | Permalink

    Any performance improvements in the overall socket library with 2.0?

    With the 1.5 runtime, socket sends of large buffers (1MB) seems to be limited to 100KBytes/second regardless of what the link and endpoint can do. Equivalent code in other interpreted run time environments can go 10-15 times faster.

  12. Posted January 21, 2010 at 11:40 pm | Permalink

    thank you so much for showing the source code

  13. Posted January 25, 2010 at 11:12 am | Permalink

    Christophe,
    This is pretty awesome! If AS3 had some sort of dynamic AS3 evaluation, someone could write a web server which could execute server side AS3! There are a libraries out there, but it would be pretty slow, since they interpret/run the script.

    I hope that AIR gets some scripting engine support in the future, with JIT compilation, etc!

    - Derrick

  14. Posted January 25, 2010 at 1:35 pm | Permalink

    @James, @Nathan

    Same problem with me too

    private function socketConnectHandler(event:ServerSocketConnectEvent):void
    {
    var socket:Socket = event.socket;
    socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
    }

    Any body got the solutions?

    Christophe Could you provide me the published project code?

    Thanks in advance

  15. who
    Posted January 29, 2010 at 4:38 am | Permalink

    I have solved the problem:
    “Type was not found or was not a compile-time constant: ServerSocketConnectEvent.”

    I download the AIR 2.0 SDK

    and follow this document:
    http://labs.adobe.com/wiki/index.php/AIR_2:Release_Notes#How_to_overlay_the_Adobe_AIR_SDK_for_use_with_the_Flex_SDK

    It is work.

    (sorry, my english is not good, if you dont know what I say, I’m so sorry….)

  16. Sreejith
    Posted March 5, 2010 at 10:23 am | Permalink

    Solution

    Use AIR2Beta sdk. Air2Beat2 sdk doesnt work.

  17. GOD
    Posted March 5, 2010 at 10:27 am | Permalink

    To solve the issue (ServerSocketConnectEvent )
    use Air2Beta1 instead of Air2Beat2.

  18. Posted June 18, 2010 at 11:20 am | Permalink

    Congratulations for the excellent sample =)

5 Trackbacks

  1. By AIR 2.0 Experimente » GELB der Powerflasher Blog on December 9, 2009 at 12:03 pm

    [...] -AIR 2.0 Web Server using the New Server Socket API -Screenrecording app with AIR 2.0 Beta -Embedding Tomcat and BlazeDS in an AIR 2.0 Application -Tomcat Launcher: Sample Application using the AIR 2.0 Native Process API – “Open in Excel”: Another AIR 2 Mini Sample -Voice Notes: Record Voice Notes and Persist them in SQLite with AIR 2 [...]

  2. [...] AIR 2.0 Web Server using the New Server Socket API (from Christophe Coenraets) [...]

  3. By AIR 2.0 HTTP Web Server | Promethe's Blog on December 17, 2009 at 5:46 am

    [...] of this new and incredible ability, Christophe Coenraets posted a small but yet very powerful code snippet to build an HTTP web server using AIR 2.0! Categories: AIR, Flash Tags: ActionScript 3.0, AIR, AIR 2.0, experiment, Flash, RIA [...]

  4. [...] Christophe Coenraets – AIR 2 Web Server using SocketServer API [...]

  5. By Air 2.0 - Socket.bind() - Flashforum on January 25, 2010 at 6:03 am

    [...] so server zeugs auszutesten. Creating a socket server in Adobe AIR 2 | Adobe Developer Connection AIR 2.0 Web Server using the New Server Socket API httpeek – Project Hosting on Google Code Dummerweise versagen bei mir aber sowohl Klassen( [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>