Asked  7 Months ago    Answers:  5   Viewed   27 times

I have a PHP script which is called with an ?img= parameter.

The value for that parameter is an (urlencoded) URL of an image.

My script checks, if that image is already stored at my server.

If not - it downloads it. After that it optionally resizes the image and sends it to STDOUT, i.e. back to the requesting browser, prepended with Content-Type and Last-modified headers:

Connection:close
Content-Type:image/jpeg
Date:Fri, 01 Jun 2012 08:28:30 GMT
Last-Modified:Fri, 01 Jun 2012 08:02:44 GMT
Server:Apache/2.2.15 (CentOS)
Transfer-Encoding:chunked
X-Powered-By:PHP/5.3.3

This is needed to workaround some crossdomain issues and works well for me since over a year:

screenshot

However I'd like to add functionality to handle the incoming If-Modified-since header - to send a Not Modified 304 response.

My questions are:

1) Is that even possible in PHP, when run in Apache?

2) How to handle (i.e. parse and produce) the dates best in PHP here?

Bonus question) How to add a Content-Length header for the resized images?

My code is below (I've omitted the CURL-downloading part):

<?php

define('CACHE_DIR', '/var/www/cached_avatars/');

$img    = urldecode($_GET['img']);
$cached = CACHE_DIR . md5($img);

# omitted downloading part for brevity

$readfh = fopen($cached, 'rb');
if ($readfh) {
        flock($readfh, LOCK_SH);

        $size = getimagesize($cached);
        $w    = $size[0];
        $h    = $size[1];
        $type = $size[2];
        $mime = $size['mime'];

        # find the downscale factor to fit image into $maxw x $maxh
        $scale = max($w / $maxw, $h / $maxh);

        header('Content-Type: ' . $size['mime']);
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s T', filemtime($cached)));

        $length = filesize($cached);
        $buf = fread($readfh, $length);
        fclose($readfh);

        # the image is smaller than $maxw x $maxh, do not scale up
        if ($scale <= 1) {
                header('Content-Length: ' . $length);
                print($buf);
                return;
        }

        $tw = $w / $scale;
        $th = $h / $scale;
        $image = imagecreatefromstring($buf);
        $thumb = imagecreatetruecolor($tw, $th);
        imagecopyresampled($thumb, $image, 0, 0, 0, 0, $tw, $th, $w, $h);
        imagedestroy($image);

        # How to add Content-Length here, after image resizing?

        if (IMAGETYPE_JPEG == $type)
                imagejpeg($thumb, null, 75);
        else if (IMAGETYPE_PNG == $type)
                imagepng($thumb, null, 9);
        else if (IMAGETYPE_GIF == $type)
                imagegif($thumb, null);

        imagedestroy($thumb);
}

?>

 Answers

19

This is definitely possible in PHP!

When the browser checks if there were modifications, it sends an If-Modified-Since header; in PHP this value would be set inside $_SERVER['HTTP_IF_MODIFIED_SINCE'].

To decode the date/time value (encoded using rfc822 I believe), you can just use strtotime(), so:

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && 
    strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= filemtime($localFileName))
{
    header('HTTP/1.0 304 Not Modified');
    exit;
}

Explanation: if the If-Modified-Since header is sent by the browser AND the date/time is at least the modified date of the file you're serving, you write the "304 Not Modified" header and stop.

Otherwise, the script continues as per normal.

Wednesday, March 31, 2021
 
LoicTheAztec
answered 7 Months ago
95

gcc returns 0 exit code for warnings as well. So you are going to differentiate that yourself with something like this:

  for F in *.c; do
      out=$(gcc -c -Wall -o ${F%.c} $F 2>&1)
      if [ $? -ne 0 ]; then
        echo $F Errors
      else
         if grep "warning:" <<<"${out}" >/dev/null ; then
          echo $F Warnings
        else
          echo $F OK
        fi
     fi
  done
Sunday, August 15, 2021
 
CodeCaster
answered 2 Months ago
81

I believe it should be

if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) >= $file_time)) {

Checking if the modified time is greater than or equal rather than just equal. Although I do understand the two values should be the same.

Tuesday, August 17, 2021
 
user542584
answered 2 Months ago
59

I've been chasing this issue for some time, thought I'd share what I found.

"The rule is actually quite simple: any error with the certificate means the page will not be cached."

https://code.google.com/p/chromium/issues/detail?id=110649

If you are using a self-signed certificate, even if you tell Chrome to add an exception for it so that the page loads, no resources from that page will be cached, and subsequent requests will not have an If-Modified-Since header.

Wednesday, September 29, 2021
 
ps-aux
answered 3 Weeks ago
79

This is not the correct use of the header. The If-Modified-Since header is one which an HTTP client (browser or code) may optionally supply to the server when requesting a resource. If supplied the meaning is "I want resource X, but only if it's changed since time T." Its purpose is to allow client-side caching of resources.

The semantics of your proposed usage are "I want updates for collection X that happened since time T." It's a request for a subset of X. It does not seem like your motivation is to enable caching. Your client-side cached representation seemingly contains all of X, even though the typical request will only return you a small set of changes to X; that is, the response is not what you are directly caching, so the caching needs to happen in custom user logic client-side.

A query string parameter is a much more appropriate solution. Below {seq} would be something like a sequence number or timestamp.

GET /ws/schools/7/students/updates?since={seq}

Server-side I imagine you have a sequence of updates since the beginning of your system and a request of the above form would grab the first N updates that had a sequence value greater than {seq}. In this way, if a client ever got very far behind and needed to catch up, the results would be paged.

Monday, October 11, 2021
 
mattltm
answered 2 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :