]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: make ServeContent support dates in If-Range headers
authorBrad Fitzpatrick <bradfitz@golang.org>
Mon, 28 Jul 2014 06:30:53 +0000 (23:30 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 28 Jul 2014 06:30:53 +0000 (23:30 -0700)
Fixes #8367

LGTM=adg
R=adg
CC=golang-codereviews
https://golang.org/cl/116300044

src/pkg/net/http/fs.go
src/pkg/net/http/fs_test.go

index 2c7ec53800644b3792fcabfd0fc062faf93321e2..146b0026b9815e147e6a895672ac2d1cb03ffa18 100644 (file)
@@ -139,7 +139,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
        if checkLastModified(w, r, modtime) {
                return
        }
-       rangeReq, done := checkETag(w, r)
+       rangeReq, done := checkETag(w, r, modtime)
        if done {
                return
        }
@@ -275,11 +275,14 @@ func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool {
 }
 
 // checkETag implements If-None-Match and If-Range checks.
-// The ETag must have been previously set in the ResponseWriter's headers.
+//
+// The ETag or modtime must have been previously set in the
+// ResponseWriter's headers.  The modtime is only compared at second
+// granularity and may be the zero value to mean unknown.
 //
 // The return value is the effective request "Range" header to use and
 // whether this request is now considered done.
-func checkETag(w ResponseWriter, r *Request) (rangeReq string, done bool) {
+func checkETag(w ResponseWriter, r *Request, modtime time.Time) (rangeReq string, done bool) {
        etag := w.Header().get("Etag")
        rangeReq = r.Header.get("Range")
 
@@ -290,11 +293,17 @@ func checkETag(w ResponseWriter, r *Request) (rangeReq string, done bool) {
        // We only support ETag versions.
        // The caller must have set the ETag on the response already.
        if ir := r.Header.get("If-Range"); ir != "" && ir != etag {
-               // TODO(bradfitz): handle If-Range requests with Last-Modified
-               // times instead of ETags? I'd rather not, at least for
-               // now. That seems like a bug/compromise in the RFC 2616, and
-               // I've never heard of anybody caring about that (yet).
-               rangeReq = ""
+               // The If-Range value is typically the ETag value, but it may also be
+               // the modtime date. See golang.org/issue/8367.
+               timeMatches := false
+               if !modtime.IsZero() {
+                       if t, err := ParseTime(ir); err == nil && t.Unix() == modtime.Unix() {
+                               timeMatches = true
+                       }
+               }
+               if !timeMatches {
+                       rangeReq = ""
+               }
        }
 
        if inm := r.Header.get("If-None-Match"); inm != "" {
index f968565f9b4db430e5eca95c2ef7063642616971..a6f33cc42d0d828b961995ae13327f5f76b7cdd8 100644 (file)
@@ -721,6 +721,28 @@ func TestServeContent(t *testing.T) {
                        wantStatus:      200,
                        wantContentType: "text/css; charset=utf-8",
                },
+               "range_with_modtime": {
+                       file:    "testdata/style.css",
+                       modtime: time.Date(2014, 6, 25, 17, 12, 18, 0 /* nanos */, time.UTC),
+                       reqHeader: map[string]string{
+                               "Range":    "bytes=0-4",
+                               "If-Range": "Wed, 25 Jun 2014 17:12:18 GMT",
+                       },
+                       wantStatus:      StatusPartialContent,
+                       wantContentType: "text/css; charset=utf-8",
+                       wantLastMod:     "Wed, 25 Jun 2014 17:12:18 GMT",
+               },
+               "range_with_modtime_nanos": {
+                       file:    "testdata/style.css",
+                       modtime: time.Date(2014, 6, 25, 17, 12, 18, 123 /* nanos */, time.UTC),
+                       reqHeader: map[string]string{
+                               "Range":    "bytes=0-4",
+                               "If-Range": "Wed, 25 Jun 2014 17:12:18 GMT",
+                       },
+                       wantStatus:      StatusPartialContent,
+                       wantContentType: "text/css; charset=utf-8",
+                       wantLastMod:     "Wed, 25 Jun 2014 17:12:18 GMT",
+               },
        }
        for testName, tt := range tests {
                var content io.ReadSeeker