URLLoader produces unexpected results in HTML5 target

Greetings. Been tasked with porting a legacy Haxe+Flash product to HTML5. Hoping OpenFL will make this much easier. Hitting some speedbumps with the network code though. I re-implemented a typical async server call using openfl.net.URLLoader. When I target Flash, communication is re-established with our server no problem. If I target HTML5, the server returns an error. When I inspect the traffic in Fiddler, I see very different outgoing results between the two targets. Short story is that our server code expects some JSON in the “body” and I can’t see any evidence that this data is being sent when I compile to HTML5.

I’m using Haxe 3.2.1 and OpenFL 3.6.1.

Relevant code:

callServerHTTP(headers, { test:"hello world" }, handler);

    public static function callServerHTTP(header:HeaderParams, body:Dynamic, ?handler:StandardResponse->Void = null):Void
    {
        var req:URLRequest = new URLRequest(header.server + header.game + "/" + header.endpoint);
        req.method = URLRequestMethod.POST;
        req.contentType = "application/json";
        var requestHeaders:Array<URLRequestHeader> = [
            new URLRequestHeader("accept-type", "application/json"),
            new URLRequestHeader("Accept", "application/json"),
            new URLRequestHeader("sessionid", header.session)
        ];
        if (header.playerOverride.length > 1)
        {
            requestHeaders.push(new URLRequestHeader("X-Player-Override", header.playerOverride));
        }
        req.requestHeaders = requestHeaders;
        req.data = Json.stringify(body);
        var loader:URLLoader = new URLLoader();
        loader.dataFormat = URLLoaderDataFormat.TEXT;
        loader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
        loader.addEventListener(Event.COMPLETE, defaultHandler);
        if (handler != null)
        {
            pending.set(loader, handler);
        }
        loader.load(req);
    }

I’m also attempting to attach some screenshots of the traffic from Fiddler.

The correct (Flash) one is marked POST and has body JSON data. Another red flag to note is that the content of x-player-override appears to be missing from the HTML5 version.

Any advice would be appreciated. Thanks!

  • Justin

We should be sending POST data, but perhaps it isn’t coming through properly. Could you try adding a trace and see if it looks correct? https://github.com/openfl/openfl/blob/develop/openfl/net/URLLoader.hx#L294

Thanks for your help.
If I trace uri there I get:
URLLoader.hx:294: {“test”:“hello world”}
(when running debug HTML5 target in Firefox)
So that looks good?

Curiously though, if I F4 that xmlHttpRequest.send in FlashDevelop I see this:
function send( data : Dynamic/MISSING InputStream/ ) : Void;

I wonder what that MISSING bit is about? Maybe this implementation is incomplete in Haxe itself?

Using OpenFL 4, I tried the following:

package;


import haxe.Json;
import openfl.display.Sprite;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
import openfl.net.URLLoader;
import openfl.net.URLLoaderDataFormat;
import openfl.net.URLRequest;
import openfl.net.URLRequestHeader;
import openfl.net.URLRequestMethod;


class Main extends Sprite {
	
	
	public function new () {
		
		super ();
		
		callServerHTTP(/*headers,*/ { test:"hello world" }/*, handler*/);
		
	}
	
	
	private static function handleIOError (_) {}
	private static function defaultHandler (event:Event) {
		
		var loader:URLLoader = cast event.currentTarget;
		trace (loader.data);
		
	}
	
	
	public static function callServerHTTP(/*header:HeaderParams, */body:Dynamic/*, ?handler:StandardResponse->Void = null*/):Void
	{
		//var req:URLRequest = new URLRequest(header.server + header.game + "/" + header.endpoint);
		var req:URLRequest = new URLRequest("http://posttestserver.com/post.php");
		req.method = URLRequestMethod.POST;
		req.contentType = "application/json";
		var requestHeaders:Array<URLRequestHeader> = [
			//new URLRequestHeader("accept-type", "application/json"),
			new URLRequestHeader("Accept", "application/json"),
			//new URLRequestHeader("sessionid", header.session)
		];
		//if (header.playerOverride.length > 1)
		//{
			//requestHeaders.push(new URLRequestHeader("X-Player-Override", header.playerOverride));
		//}
		req.requestHeaders = requestHeaders;
		req.data = Json.stringify(body);
		var loader:URLLoader = new URLLoader();
		loader.dataFormat = URLLoaderDataFormat.TEXT;
		loader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
		loader.addEventListener(Event.COMPLETE, defaultHandler);
		//if (handler != null)
		//{
			//pending.set(loader, handler);
		//}
		loader.load(req);
	}
	
	
}

Here were the results:

http://www.posttestserver.com/data/2016/07/13/20.36.19339237221

It looks like it worked, however, I got an HTML5 error when “accept-type” was defined in a header:

XMLHttpRequest cannot load http://posttestserver.com/post.php. Request header field accept-type is not allowed by Access-Control-Allow-Headers in preflight response.

It appears to be working, perhaps it is something with the server you are using, or maybe it is somewhat related to the “accept-type” header? I’m testing using Opera

Thank you for your help. The outgoing request appears as expected (POST, with JSON body) only if I add no custom headers. However, I need those headers (such as x-player-override).

When I add them the request format completely changes. I did some more reading and as far as I can tell the browser is trying to do a “preflight” request for CORS permissions. This is why the method mysteriously changes to “OPTIONS”.

I can replicate this error bypassing OpenFL and targeting the JavaScript API directly:

        ajax = new XMLHttpRequest();
        ajax.open("POST", url, true);
        ajax.setRequestHeader("Accept", "application/json");
        //ajax.setRequestHeader("X-Player-Override", headers.playerOverride);
        ajax.send(Json.stringify( { test:"hello world" } ));

… without x-player-override the JSON is sent immediately, but with it additional security steps become necessary (and we’ll probably be forced to change our server handle modern CORS-style requests).

So I don’t think there’s anything wrong with OpenFL, this behavior is due to the XMLHttpRequest implementation in the browser. Sorry to take so much time!

Well this is very interesting, if you find any more general solution, let me know, or if we can/should do anything to make this better in OpenFL (strip header and warn?)