NYCPHP Meetup

[nycphp-talk] Double Loading PHP as image - Firefox or PHP?

Mark Armendariz enolists at gmail.com
Wed Jun 7 21:21:01 EDT 2006


This is a follow up to an old discussion I started months ago, on which the
last post I found in my own archives was on 12/2.  I can't seem to find the
original post in the archive, but here's a link to one of them:

http://lists.nyphp.org/pipermail/talk/2005-December/017145.html

Strangely long after I'd forgotten the problem due to lack of time to find a
real answer, I got 2 emails from unrelated people working on unrelated
projects with the same issue.  As I'm still as backlogged as I was when I
originally had the issue I haven't progressed much and I couldn't be much
help.  But they seem to have solved the issue, and as I promised then - when
I figure out the answer I'll post it.

I haven't had the opportunity to give this a shot on my end, so I can
confirm nothing - but it seems sound.  Also, should you need to email Jason,
let me know and I'll get you in contact.  I don't want to post his address
here sans permission.

First Conversation with Jason Wieland...

===================================================================
===================================================================
From: Jason Wieland
Sent: Wednesday, May 24, 2006 10:27 AM
To: nyphp at enobrev.com
Subject: Firefox double loading images

I was wondering if you found a solution to this.  I saw your post a couple
months ago on the nyphp-talk mailing list.  I have exactly the same issues.

Thanks,

Jason

-------------

Hey Jason,

--- some gibberish summation of the discussion here with the air of 'I got
nothin' ---

Mark

-------------

From: Jason Wieland
Sent: Monday, June 05, 2006 10:58 AM
To: Enobrev
Subject: Re: Firefox double loading images


Hey thanks for your response.  how I solved it was sending Etag and
Last-Modified headers.  Something like this:

$file_hash = md5_file($post_abs);

header("Etag: \"$file_hash\"");
header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($post_abs)).'
GMT');

That seemed to make firefox happy.  I then check for those headers in the
future request to possibly issue a 304 if the image is the same.  Something
like this:

  #Possibly we could the file in the browsers cache
  $headers = apache_request_headers();
  $file_hash = md5_file($post_abs);

  if (isset($headers['If-None-Match']) && ereg($file_hash,
$headers['If-None-Match'])) {
    logger( DEBUG, "Etag match sending 304");
    header("HTTP/1.1 304 Not Modified");
    exit;
  }


  if (isset($headers['If-Modified-Since']) &&
(strtotime($headers['If-Modified-Since']) == filemtime($post_abs))) {
    #Etag should always hit first... but just in case
    logger( DEBUG, "If Modified since looks good sending 304");
    header("HTTP/1.1 304 Not Modified");
    exit;
  }


Maybe you'll find some of this useful,

Jason Wieland

-------------

Enobrev wrote:
Jason, that's fantastic.  I'm going to have to give it a shot when I find a
few minutes.  I really hated the 3-second thing but never caught a moment to
figure it out.  The worst part about researching it was that both requests
'looked' exactly the same even though they were in fact different types of
requests.  I do recall looking everywhere for info, though.  How'd you
figure it out?

If it works for me, I'll probably post it to the list for the archives (with
reference to you, of course), if you don't mind.

Thanks!!

Mark

-------------

No problem, glad I could help.  I stumbled upon the solution in two parts.
First I loaded up a static image on firefox and used Live Headers (a great
developer extension http://livehttpheaders.mozdev.org/) to view the
different headers sent for a static image and php dynamic image.  I noticed
that apache automagically stuck in Etag and Last-Modified when requesting
the static image.  Firefox would double request on php dynamic images but
not on static images so I knew it had to be something to do with those
headers.

I also wanted to create a browser caching solution.  The default way php
sends out dynamic images the users browser will request a new image from the
server every time the page is reloaded even though it may have a perfectly
good image sitting in its local cache.  The browsers uses Etag (Entity Tag,
basically just a unique string the represents an id for that image) and
Last-Modified (the time of image creation or modification).  You really only
need to send one.  But apache sends both for static images so I just though
I would send both as well.  After I added those headers the browser stop
doing the double requests and on future request of the image it would return
the Etag and last-modified (If-Modified-Since) http headers which I can use
to issue a 304 (use local cache) to the browser instead of creating the
image all over again and sending it out.


Good Luck,

Jason

===================================================================
And the second discussion from this week on the same subject...
===================================================================

From: Jason Collins
Sent: Monday, June 05, 2006 8:39 AM
To: nyphp at enobrev.com
Subject: Double Firefox Image Requests

http://lists.nyphp.org/pipermail/talk/2005-December/017153.html

Hey Mark, did you ever find any resolution on this? I am hitting a dynamic
(.aspx) resource in an <img> request, but Firefox is making 2 identical
requests one after the other.

It is very bizarre.

There is a slight difference in the Accept header; I'm wondering if some
form of content negotiation is occurring?

J

http://localhost/Report/dv.ashx?id=ff37

GET /Report/dv.ashx?id=ff37 HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.4)
Gecko/20060508 Firefox/1.5.0.4
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9
,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: vid=0466af9b-127a-4550-9327-94d92cbae524

HTTP/1.x 200 OK
Server: Microsoft-IIS/5.1
Date: Mon, 05 Jun 2006 15:31:54 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: image/gif
Content-Length: 43
----------------------------------------------------------

http://localhost/Report/dv.ashx?id=ff37
GET /Report/dv.ashx?id=ff37 HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.4)
Gecko/20060508 Firefox/1.5.0.4
Accept: image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: vid=0466af9b-127a-4550-9327-94d92cbae524

HTTP/1.x 200 OK
Server: Microsoft-IIS/5.1
Date: Mon, 05 Jun 2006 15:31:54 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: image/gif
Content-Length: 43

----------------------------------------------------------

From: Enobrev
Sent: Tuesday, June 06, 2006 1:48 AM
To: Jason Collins
Subject: RE: Double Firefox Image Requests

funny you should ask right now.  I'd long forgotten about that post and
ended up using a workaround that did the job but felt so dirty.  Another
person by the name of Jason Wieland came out of the blue a couple days ago
and asked.  I told him my workaround, which was basically to put a timestamp
on requests, using the user agent and _possibly_ the ip(it's been a while)
and a cookie to discern the visitor.  Then set it to ignore any other
requests by the same user for the next 3 seconds.

It's worked all this time (to my knowledge), but it has that terrible kludgy
feel to it.

Jason hit me back just yesterday with his solution:

--- (refer to last message) ---

I haven't had the chance to test it yet and probably won't for at least a
couple weeks, but it's an interesting approach.  I just replied to him right
before writing this to ask how he came up with it.

Anyways, if it works for me, I'm going to add it to the nyphp post for the
archives.  If you get a chance to try it out, let me know how it goes.

Have a great evening!

Mark

----------------------------------------------------------

Jason Collins:
As I did more experimenting yesterday, I found that Firefox behaved much
differently when the request came from an <img> tag embedded in an HTML
page.

That is, if I requested the resource directly, I received double requests
from Firefox for many response header combinations (basically any header
combination that made the resource non-cachable).

If I embedded that request in an <img> tag inside of an HTML document, many
of those double requests went away.

My solution was similar to below – I set a Last-Modified in the past, and
set the Expires header to Now. Combining this with only requesting from an
embedded <img> tag, I got 200 and 304 activity as I would have expected. My
C# source:

              // Case 1
              //context.Response.Cache.SetCacheability(
HttpCacheability.NoCache);
              // IE good
              // firefox makes double requests

              // Case 2
              //context.Response.Cache.SetETag("123"); // ETag, on its own,
doesn't emit!!!
              // ETag is not emitted at all!!! Asp.Net weirdness? Unknown.
Moving on.

              // Case 3
              //context.Response.Cache.SetCacheability(
HttpCacheability.NoCache);
              //context.Response.Cache.SetLastModified(new DateTime(2000, 1,
1));
              // Last-Modified is not emitted at all (likely due to NoCache)
              // case 3 is therefore like case 1 above.

              // Case 4
              //context.Response.Cache.SetLastModified(new DateTime(2000, 1,
1));
              // IE makes one request, but caches
              // Firefox makes one request, but caches

              // Case 5
              //context.Response.Cache.SetLastModified(new DateTime(2000, 1,
1));
              //context.Response.AddHeader("Expires", "-1");
              // Custom "Expires" header is not emitted therefore like case
4 above

              // Case 6
              //context.Response.Cache.SetLastModified(new DateTime(2000, 1,
1));
              //context.Response.Cache.SetExpires(DateTime.Now);
              // IE makes correct requests, including If-Modified-Since (for
304)
              // Firefox makes double requests, but does include
If-Modified-Since
              // Update: Firefox only makes double requests if the image is
requested directly!!!

              // Case 7
              // final solution: using Case 6, assuming that <img> is
embedded in web page, and supporting
              // 304 revalidation headers
              context.Response.Cache.SetExpires(DateTime.Now);
              if (context.Request.Headers["If-Modified-Since"] != null)
              {
                     context.Response.StatusCode = 304;
              }
              else
              {
                     context.Response.Cache.SetLastModified(new
DateTime(2000, 1, 1));
                     context.Response.ContentType = "image/gif";
                     context.Response.OutputStream.Write(ImageBytes, 0,
ImageBytes.Length);
              }

Thanks,
j

===================================================================
===================================================================
Apparently two very smart gentlemen.  Hopefully this can help anyone else
who's had the same issue.

Have a great evening!!

Mar
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.nyphp.org/pipermail/talk/attachments/20060607/15b03d95/attachment.html>


More information about the talk mailing list