mikeboers / Flask-Images

On-demand resizing of images for Flask applications.
https://mikeboers.github.io/Flask-Images/
BSD 3-Clause "New" or "Revised" License
81 stars 43 forks source link

if_modified_since is not respected #37

Closed macrojames closed 9 years ago

macrojames commented 9 years ago
[:debug] last_modified: datetime.datetime(2015, 11, 10, 23, 20, 1, 80586)
[:debug] if_modified_since: datetime.datetime(2015, 11, 10, 23, 20, 1)

That makes last_modified bigger than if_modified_since because of the microseconds Impact: no 304 (Not modified) is sent, but the picture itself.

My ugly patch was:

  mtime = datetime.datetime.utcfromtimestamp(raw_mtime) - datetime.timedelta(seconds=1)

That works for me, but doesn't solve the real problem. Root cause is unknown for me, but the header will never have parts of seconds set.

mikeboers commented 9 years ago

So, the issue is multiple requests within the remaining part of a second since an image was cached will result in the image being sent again?

macrojames commented 9 years ago

It happens on single requests, even after some time. The mtime is more accurate and is always some microseconds "newer"  than the header. Happens for all of my files but only in wsgi mode, not in the flask integrate dev server.

mikeboers commented 9 years ago

But, how? If-Modified-Since generally contains the value of Last-Modified as originally sent by the server (unless the client assumes the responsibility of fully understanding the server's concept of time). Last-Modified will be set to the same value as mtime (without microseconds). So the only time (in which I can conceive of) this happens is when requested again within the remaining fraction of a second as another request, which is also the same second that the file was modified.

macrojames commented 9 years ago

No, because the browser or the Apache just drops the fraction. The browser never knows about the fraction. The next request will consider if-modified-since as datetime without fractional seconds. Like a floor() operation.

mikeboers commented 9 years ago

Indeed.

Given that, the only time that request.if_modified_since >= mtime gives an unintended result should be within the remaining fraction of a second as another request, which is also the same second that the file was modified.

So sure, it can happen, but only in that tiny slice of time in a use pattern way outside normal. And even then, it will likely catch against the etag before sending the payload.

That is my understanding, at least. I'd love to know how I'm wrong.

macrojames commented 9 years ago

If-modified-since is independent from the request time. It is the saved timestamp from the browser cache. It will stay the last received mtime until a newer file is received.

mikeboers commented 9 years ago

:bulb:!

The Last-Modified is always set from the file's mtime, and then compared to it again, at which point the microseconds have been truncated and it fails. The reason why mine works is because my platforms do not have microseconds returned from os.path.getmtime.

I propose truncating the microseconds off of the timestamp. Either with a .replace or math.floor (.replace seems better).

Thanks for sticking with this. :grinning: