SWFHttpRequest.hx

From Jimbojw.com

Jump to: navigation, search

// This is the source code for SWFHttpRequest Flash/Ajax Utility

Unless otherwise specified, this haxe code is released under The MIT License. If you choose to install it, you do so at your own risk and discretion.

Download
// SWFHttpRequest.hx
// version: 0.3
// Copyright (c) 2007, Jim R. Wilson (wilson.jim.r at gmail)
// Released under The MIT License (http://www.opensource.org/licenses/mit-license.php)
//
// Purpose:
//    This haxe source file builds a SWF which adds a JavaScript class called
//    SWFHttpRequest to the browser environment in which it's run.  This new
//    class behaves in many ways similar to the native XMLHttpRequest object,
//    except it uses Flash as an intermediary for requesting data.
//
//    The benefit of using Flash for this purpose is its security model which
//    uses an opt-in model controlled by crossdomain.xml files for determining
//    third-party access rather than the much more restrictive same-origin 
//    policy employed by browsers for native XHRs.
//
//    This is NOT a perfect replacement for XHRs, however, since it lacks the
//    following features:
//      * Capacity to read HTTP response headers
//      * Capability to provide credentials or respond to HTTP auth challenges
//
//    The failures mentioned above may be possible to implement by using a lower-
//    level API.  For example, instead of using the haxe.Http class, one could use
//    an appropriately featured ActionScript library, or even (if necessary) drop
//    down to making TCP connections using the flash.net.Socket class.
//
// To build:
//   1. Acquire the latest stable version of haxe from http://haxe.org/download
//      Note: To run haxe, you may also need Neko (http://nekovm.org/download)
//   2. Create a file called SWFHttpRequest.hx with these contents.
//   3. Create a file called compile.hxml with these contents:
//      -swf-version 9
//      -swf swfhttprequest.swf
//      -main SWFHttpRequest
//   4. From the command line, execute:
//      haxe compile.hxml
//
// To activate:
//   1. Take swfhttprequest.swf generated by haxe and deploy it to your webserver.
//   2. Embed it into your HTML file with code like the following:
//      <object
//          id="swfhttprequest" class="hidden"
//          classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
//          codebase="http://fpdownload.adobe.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0">
//          <param name="movie" value="swfhttprequest.swf" />
//          <param name="allowScriptAccess" value="always" />
//          <embed
//              class="hidden"
//              src="swfhttprequest.swf"
//              allowScriptAccess="always"
//              type="application/x-shockwave-flash"
//              pluginspage="http://www.adobe.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash"> 
//          </embed>
//      </object>
//   WARNING: Make sure this is NOT inside a pair of <form> tags.  This will
//   cause EXTREMELY DIFFICULT TO DEBUG issues in Internet Explorer.
//   NOTE: The id attribute of the <object> tag is NECESSARY for IE to function.
//   It can be anything you like, as long as it contains ONLY alphanumeric
//   characters and underscores (so-called "word" characters). Hyphens will kill it.
//
// To hide:
//    Note that you'll probably want to hide the flash object by shifting it
//    way offscreen via position:absolute with negative top and left coordinates.
//    For example, you could add class="hidden" to both the <object> and <embed>
//    tags, then add this to your site-wide CSS:
//       embed.hidden,
//       object.hidden {
//           height: 1px;
//           width: 1px;
//           position: absolute;
//           top: -1000px;
//           left: -1000px;
//       }
// --------------------------------------------------------------------
// Copyright (c) 2007 Jim R. Wilson
// 
// Permission is hereby granted, free of charge, to any person 
// obtaining a copy of this software and associated documentation 
// files (the "Software"), to deal in the Software without 
// restriction, including without limitation the rights to use, 
// copy, modify, merge, publish, distribute, sublicense, and/or 
// sell copies of the Software, and to permit persons to whom the 
// Software is furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be 
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
// OTHER DEALINGS IN THE SOFTWARE.
// --------------------------------------------------------------------
//
 
import flash.external.ExternalInterface;
import flash.Lib;
import haxe.Http;
 
class SWFHttpRequest {
 
    public static var ver:String = '0.3';
 
    public static var transports:Hash<Transport> = new Hash();
 
    public static function abort( instance:Int ) {
        transports.get( instance + '' ).abort();
    }
    
    public static function open( instance:Int, method:String, url:String ) {
        transports.set( instance + '', new Transport( instance, method, url ) );
    }
    
    public static function send( instance:Int, data:String ) {
        transports.get( instance + '' ).send( data );
    }
 
    public static function setRequestHeader( instance:Int, header:String, value:String ) {
        transports.get( instance + '' ).setRequestHeader( header, value );
    }
    
    public static function version() {
        return ver;
    }
 
    public static function main() {
        ExternalInterface.addCallback("abort",abort);
        ExternalInterface.addCallback("open",open);
        ExternalInterface.addCallback("send",send);
        ExternalInterface.addCallback("setRequestHeader",setRequestHeader);
        ExternalInterface.addCallback("SWFHttpRequestVersion",version);
        ExternalInterface.call( [ "(function(){",
            "if (window.SWFHttpRequest) return;",
            "var Class = function(properties){",
                "var klass = function(){ return this.abort.apply(this); };",
                "klass.prototype = properties;",
                "klass.constructor = arguments.callee;",
                "return klass;",
            "};",
            "window.SWFHttpRequest = new Class({",
                "abort: function(){",
                    "if (typeof this.instance != 'undefined') {",
                        "window.SWFHttpRequest.instances[this.instance] = null;",
                        "window.SWFHttpRequest.engine.abort( this.instance );",
                    "}",
                    "this.readyState = 0;",
                    "this.responseText = '';",
                    "this.responseXML = null;",
                    "this.status = 0;",
                    "this.statusText = '';",
                    "this.instance = window.SWFHttpRequest.instances.length;",
                    "window.SWFHttpRequest.instances.push( this );",
                "},",
                "getAllResponseHeaders: function(){ return null; },",
                "getResponseHeader: function(){ return null; },",
                "onreadystatechange: null,",
                "open: function(method, url, async, user, password){",
                    "this.status = 0;",
                    "this.readyState = 1;",
                    "this.statusText = this.responseText = '';",
                    "this.responseXML = null;",
                    "window.SWFHttpRequest.engine.open( this.instance, method, url );",
                "},",
                "send: function(data){",
                    // TODO: Once haxe.Http supports setPostData() for flash9 targets, this function
                    // should be updated to allow for a Document as the data.  When that's the case,
                    // it should be serialized as XML prior to sending over to the Flash executor.
                    "window.SWFHttpRequest.engine.send( this.instance, data );",
                "},",
                "setRequestHeader: function(header, value){",
                    "if (this.readyState != 1 || !header || !value) return;",
                    "window.SWFHttpRequest.engine.setRequestHeader( this.instance, header, value );",
                "}",
            "});",
            "window.SWFHttpRequest.instances = [];",
            "window.SWFHttpRequest.version = '" + ver + "';",
            "var f = function(tag){",
                "var elems = document.getElementsByTagName(tag);",
                "for (var i=0; i<elems.length; i++) if (elems[i].SWFHttpRequestVersion) return elems[i];",
            "};",
            "window.SWFHttpRequest.engine = f('embed') || f('object');",
        "})" ].join('') );
        var params = Lib.current.loaderInfo.parameters;
        if (Reflect.hasField(params,'onload')) ExternalInterface.call( Reflect.field(params,'onload') );
    }
    
}
 
class Transport {
 
    var active:Bool;
    var instance:Int;
    var method:String;
    var url:String;
    var http:Http;
 
    public function new( instance:Int, method:String, url:String ) {
        this.active = true;
        this.instance = instance;
        this.method = method.toUpperCase();
        this.url = url;
        this.http = new Http( this.url );
        this.http.onData = this.onData;
        this.http.onError = this.onError;
        this.http.onStatus = this.onStatus;
    }
    
    public function send( ?data:String ) {
        if ( data==null ) return this.http.request( this.method=='POST' );
        // NOTE: Once haxe.Http supports the setPostData() method for flash9 targets,
        // all the following should be replaced by: this.http.setPostData( data );
        var re = ~/^(.*?)=(.*)$/;
        var pairs:Array<String> = data.split('&');
        var ud = function(s){
            try { return StringTools.urlDecode(s); }
            catch ( e:Dynamic ) { }
            return s;
        };
        for( i in 0...pairs.length ) {
            if (re.match(pairs[i])) this.http.setParameter( ud(re.matched(1)), ud(re.matched(2)) );
            else this.http.setParameter( ud(pairs[i]), '' );
        }
        return this.http.request( this.method=='POST' );
    }
    
    public function setRequestHeader( header:String, value:String ) {
        this.http.setHeader( header, value );
    }
    
    public function onData( data:String ) {
        if (!this.active) return;
        ExternalInterface.call( [ "(function(instance, data){",
            "var shr = window.SWFHttpRequest.instances[instance];",
            "if (!shr) return;",
            "shr.status = 200;",
            "shr.statusText = 'OK';",
            "shr.readyState = 4;",
            "shr.responseText = data;",
            "try {",
                "if (window.DOMParser) {",
                    "var dp = new DOMParser();",
                    "shr.responseXML = dp.parseFromString( data, 'text/xml' );",
                "} else {",
                    "shr.responseXML = new ActiveXObject('Microsoft.XMLDOM');",
                    "shr.responseXML.async = 'false';",
                    "shr.responseXML.loadXML(data);",
                "}",
            "} catch(error) { shr.responseXML = null; }",
            "if (shr.onreadystatechange && typeof shr.onreadystatechange=='function') shr.onreadystatechange();",
        "})" ].join(''), this.instance, data);
    }
    
    public function onError( msg:String ) {
        if (!this.active) return;
        ExternalInterface.call( [ "(function(instance){",
            "var shr = window.SWFHttpRequest.instances[instance];",
            "if (!shr) return;",
            "shr.status = 404;",
            "shr.statusText = 'Not Found';",
            "shr.readyState = 4;",
            "shr.responseText = null;",
            "shr.responseXML = null;",
            "if (shr.onreadystatechange && typeof shr.onreadystatechange=='function') shr.onreadystatechange();",
        "})" ].join(''), this.instance );
    }
    
    public function onStatus( status:Int ) {
        if (!this.active || status==200) return;
        ExternalInterface.call( [ "(function(instance, status){",
            "var shr = window.SWFHttpRequest.instances[instance];",
            "if (!shr) return;",
            "shr.status = status;",
            "if (shr.onreadystatechange && typeof shr.onreadystatechange=='function') shr.onreadystatechange();",
        "})" ].join(''), this.instance, status );
    }
    
    public function abort() {
        this.active = false;
    }
 
}
 
//