Debugging crossdomain issues: Following HTTP 302s

Flash’s crossdomain access policies have always been one of the things developers have always complained against. The specification keeps changing, even with minor updates to the Flash Players (actually I dont really know what counts as a major release in Flash Player land, all releases are point-point-point releases like 9.0.124.0, making them look really minor). I lost more than 2 days on cross domain issues last week before I finally figured it out. So I figured blogging this could help someone else not waste as much time as I did.

If you are reading this post, also check out my previous crossdomain entry as well, which talks about setting up the Flash logging as well as the newly introduced content-type strictness that broke my last app.

In this particular entry I want to talk about how Flash handles HTTP 302 headers while loading images using a Loader, as well as certain tricks to debug crossdomain related issues. While Flash does load images from outside its security sandbox, it does not give you access to the BitmapData of those images which is what I was trying to do.

Whats HTTP 302 ?
HTTP 302 http header tells the browser that the content has been moved temporarily to a different URL. When the browser receives this message it immediately tries the new url. The header is explained in more detail here.

Why is this a problem ?
The problem occurs when you try to load a resource from a url that may redirect to a url outside the security sandbox of the swf. Although Flash automatically calls the new url, it does not use any loaderContext. The LoaderContext object is the optional argument to a URLRequest when making data request calls using a Loader that tells Flash Player to look for a crossdomain.xml file. The first thing that usually causes errors is that the developer forgets to pass this argument. As far as I remember, AS2 used to look for crossdomain files automatically when making cross-domain calls but AS3 does not. But even when you pass this parameter to a Loader’s load function, if the url automatically redirects using 302, the Flash player does not use the LoaderContext in the subsequent call.

The solution:
The way to resolve this (and I only found this after way too long), is to look at the url of LoaderInfo object of the Loader object (Loader.contentLoaderInfo). This data is populated with the new redirected-to URL at the time when the Loader fires the HTTPStatusEvent (and all events following that). Your code needs to check if the final url was different from the original url and if so, load the new URL again, using a LoaderContext that makes the loader look for a crossdomain.xml. The code looks something like this:

[AS]

public function ImageLoadTest(){
l = new Loader()
l.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete)
addChild(l);
load(imageURL);
}
private function onLoadComplete(event:Event):void{
try{
trace( Bitmap(l.content).bitmapData)
}catch(e:Error){
trace(‘Cannot access bitmap of image due to crossdomain issues’);
load(l.contentLoaderInfo.url);
}
}

private function load(url:String):void{
var context:LoaderContext = new LoaderContext(true)
l.load(new URLRequest(url), context);
}

[/AS]

This seems a little idiotic, and makes every load takes twice as long, not to mention consume twice the bandwidth. If anyone knows another way to do this, let me know. However if you do not know the location of the final server up until the time of loading, then this seems to be the only way.

Setting up Flex Builder projects to surface cross domain errors:

The worst part of this bug is that it will never surface when you are developing the code using Flex Builder, since by default, Flex Builder uses the file protocol (file://) to reference the html and swfs, and at this time, all http calls regardless of domains, are allowed. So this bug came up only during the QA phase and seriously jeopardized my project. The way you *should* develop Flex/AS3 projects in Flex Builder is to switch the location of the output folder of your project to the documentRoot of a locally running HTTP server (Macs come with Apache 2 built in, and its an easy install for Windows), and then use the http:// protocol to reference the file. Make sure to change the host file on your machine to reference that page as a subdomain of the project’s final web location. Now all security sandbox errors will come up as you develop your app in the trace console window of Flex Builder

Flex Builder Web Project

Verify crossdomain files with Charles:
I use Charles, an http proxy, to monitor the http behavior of my application. Charles comes with a very nifty feature called local mappings that lets you load local files when the browser makes certain calls. I ended up using this to load crossdomain files sitting on my desktop everytime Flash made certain http requests for them, really a great tool considering the schema has changed a bit in the last few versions of the Flash Player.

Charles Local Files

And of course enabling Flash Player logs is absolutely essential while debugging Flash Player cross domain issues.

Author: Arpit Mathur

Arpit Mathur is a Principal Engineer at Comcast Labs where he is currently working on a variety of topics including Machine Learning, Affective Computing, and Blockchain applications. Arpit has also worked extensively on Android and iOS applications, Virtual Reality apps as well as with web technologies like JavaScript, HTML and Ruby on Rails. He also spent a couple of years in the User Experience team as a Creative Technologist.

5 thoughts on “Debugging crossdomain issues: Following HTTP 302s”

  1. Had a very similar problem. It would be nice if flash would let you see that the url has changed before the complete handler is triggered.

    The help docs say “After the first progress event, the url property reflects the media’s final URL, after any redirects and relative URLs are resolved.”, but if you try to get the url in a progress event listener it throws and error (Error #2099: The loading object is not sufficiently loaded to provide this information).

    So since like you concluded we can not get the url until the complete handler, i am loading images twice like you

    Like

  2. Thank you for this post – very helpful. I’ve applied it and found that sometimes the img_bmp assign will succeed the try, but then is still null. I’ve worked around this by continually loading original URL until bitmap is non-null. Odd…

    private function onLoadComplete(event:Event):void
    var img_bmp:Bitmap;
    try{
    img_bmp = Bitmap(loader.content)// as Bitmap;
    }catch(e:Error){
    trace(‘Cannot access bitmap of image due to crossdomain issues’);
    if (loader.contentLoaderInfo) {
    load(loader.contentLoaderInfo.url);
    }
    return;
    }
    if (img_bmp == null){
    if (loader.contentLoaderInfo != null) {
    if (loader.contentLoaderInfo.url != null){
    load(loader.contentLoaderInfo.url);
    }else{
    load(imgSrc);
    }
    }
    return;
    }
    }

    Like

  3. Even though this post is from 2008 this difficulty is still present (2012). For me there was a further complication that I hope will be useful to someone. The loader.contentLoaderInfo.url that arrived was not the full URL of the image, and only the domain. Don’t know why. So reloading from the new URL would not work for me.

    To solve this, I used Security.loadPolicyFile(newDomain + ‘crossdomain.xml’) and then reloaded from the ORIGINAL url. This works because when the re-direct occurs the second time, the policy file for the new domain is already loaded.

    Further information is, I did not surround the implementation with a try-catch, instead i use the flag loader.contentLoaderInfo.chilcAllowsParent to see if the policy file is ok, and the content is good to go.

    Like

Leave a Reply to standardcombo Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: