Skip to content

net/http: Broken If-Range handling in ServeFile and friends unless caller sets ETag manually #8367

@gopherbot

Description

@gopherbot
What does 'go version' print?

go version go1.3 linux/amd64

What steps reproduce the problem?

1. You will need an MPEG4 version 2 video file that your browser agrees to play. It
needs to be the specific kind of mp4 that has metadata at the end of the file. I'm sorry
but I don't currently have a publicly available example of one. Put it as video.mp4 in
the directory with the attached files.

2. Download the attached *.go and *.html and "go run buggy-logging.go"

3. Open http://localhost:8000/
tested with Chrome 36.0.1985.97 beta on linux

4. Click play

As the bug depends on browser caching behavior, I find I typically need to "Empty
cache and hard reload", and then perform steps 3 & 4 twice to trigger it.

5. You should see just a black screen, no video playing. Log attached as bug.log. I have
done a similar run with "go run buggy.go", which skips the logging, to prove
that my logging kludge does not change behavior.

Log attached as "bug.log"

6. Control-C the buggy-logging version, go run etag-logging.go

7. "Empty cache and hard reload" and perform steps 3 & 4 (twice?) again.

8. You should see the video play.

Log attached as "etag.log"




The difference in the logs is:

GET /video.mp4
Range: bytes=798054665-798503344
If-Range: Wed, 25 Jun 2014 17:12:18 GMT

200 OK  this causes the bug

vs

GET /video.mp4
Range:bytes=798054665-798503344
If-Range: fake

206 OK  this works
Content-Range: bytes 798054665-798503344/798503345


The above seems to be caused by net/http not sending an Etag, only Last-Modified, but
only accepting ETags for If-Range. I would recommend either

1. Make net/http serveFile provide an ETag (if not already set in ResponseWriter) from
hashing FileInfo.Size(), .ModTime().Unix(), .ModTime().Nanosecond(), and on unix,
syscall.Stat_t.Ctim.Sec, .Ctim.Nsec, .Dev, .Ino
OR
2. checkETag() should fall back to w.Header().get("Last-Modified") if the
response has no ETag

The first one seems preferable, and the stat call is done already, so there's no real
cost to it.

Chrome should also probably know to handle the failure right. It seems like this is
reported as https://code.google.com/p/chromium/issues/detail?id=74852 -- but net/http
should not trigger that logic when no actual file change happened.

Attachments:

  1. index.html (209 bytes)
  2. buggy-logging.go (842 bytes)
  3. buggy.go (484 bytes)
  4. etag-logging.go (875 bytes)
  5. bug.log (1082 bytes)
  6. etag.log (2295 bytes)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions