Friday, March 6, 2009

Flash preloader mayhem. bytesTotal equals 0 or bytesLoaded and the misterious missing Content-Length header

There are times in programming when when the simplest of things turn out to be the most difficult to achieve.

I experienced this some days ago when I wanted to implement a preloader for a game I'd been working on.

After an unsatisfactory attempt at a first-frame preloader (the combination with PureMVC was to be fatal to me :), I went for the external one. Namely, there is a very small swf, which is the 'host', and which loads the actual game while displaying the loading progress. With no more fuss from PureMVC, and everything working fine, the progress bar appeared at 0%, as expected. It was good, simple and quick, like a preloader should. I happily deployed the game and test it in the browsers.

None of them worked! All of them displayed an empty progress bar, and after some seconds the game suddenly appeared. My gosh, what could it be, I wondered. I placed some strategic traces, only to learn that the bytesTotal property of the Loader.contentLoaderInfo object was either equal to the bytesLoaded property (only in Firefox), or simply 0. This was the strangest thing I'd seen in quite a bit of time...


After some extensive Googling around, I found some hints of what might be wrong. The Adobe documentation for the URLLoader class (which I had not used) mentioned that "a missing Content-Length header will result in bytesTotal being indeterminate" (nothing of the sort was mentioned for the Loader class, which I had used). This prompted me to open Charles and spy on the headers returned by the server for the loader and for the game itself. Indeed, there was a single difference between them: the game's response was missing a "Content-Length" header. Eurika!

This, in turn, called for an .htaccess file to set things straight (I was using Apache 2). I dug some more and I discovered mod_proxy_http in the Apache documentation. They mention that the variables proxy-sendcl and proxy-sendchunks are responsible for the Content-Length header, which apparently works in conjunction with an operation called 'chunking', performed by Apache when serving larger files, whereby it starts sending the file to the requester before buffering it (which would allow it to know its size), making the whole process quicker. I presently set out to alter them, by using the SetEnv command: setenv proxy-sendcl 1. I put this in the .htaccess, and I made sure the variables were set by using a phpinfo() call in a test php script. Nothing changed! Then I added the other one: setenv proxy-sendchunks 0, hoping to finally force Apache to cut it out with the chunking. Didn't work this time etiher, everything looked pretty much the same. It dawned on me at this point that I didn't know the correct values for these variables (maybe it wasn't numbers, but rather some predefined enums? the documentation didn't say). But I had already wasted too much time on a preloader and decided to call for help from our hosting company. They suggested, quite strangely, that I turn gzipping off, by using this line in the .htaccess: SetEnv no-gzip dont-vary. I was quite sceptical of the solution, because the loader swf was being gzipped by Apache anyway and it did have its "Content-Length" header where it needed it. However, surprise surprise, the preloader started working! It was smooth and silky, as I was hoping.

Now, I still don't understand what gzipping had to do with proxy-sendcl. I have sent them an email back for clarification, I will be forever grateful if they can illuminate me. And I shall undoubtedly share my findings, so keep posted - or, if you know and are willing to help, drop a comment!

I hope this saves someone some precious time.

3 comments:

George Profenza said...

Interesant articol. Poate ne auzim la LFPUG

Unknown said...

Wow, thanks!!

I was experiencing the same problem and was about to give up on it, when I ran into this post.

After comparing the server response to my local server response (which did work), I noticed that the remote server encoded its response.

You're post saved the day, thanks a million!!!!

evolver said...

@George: Nu am mai fost demult, voi avea timp doar prin iunie, cand scap de examene. A, si frumoasa albina ai desenat. Spor si cu altele.

@Bob: thanks a lot for letting me know. I'm really glad to know it made a difference in someone's development day. I'll come back with some more when I find a bit of time. Cheers