From f5037ee127ed3b92b46f96ba6d033fa11683fee2 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 27 Jul 2014 23:30:53 -0700 Subject: [PATCH] net/http: make ServeContent support dates in If-Range headers Fixes #8367 LGTM=adg R=adg CC=golang-codereviews https://golang.org/cl/116300044 --- src/pkg/net/http/fs.go | 25 +++++++++++++++++-------- src/pkg/net/http/fs_test.go | 22 ++++++++++++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/pkg/net/http/fs.go b/src/pkg/net/http/fs.go index 2c7ec53800..146b0026b9 100644 --- a/src/pkg/net/http/fs.go +++ b/src/pkg/net/http/fs.go @@ -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 != "" { diff --git a/src/pkg/net/http/fs_test.go b/src/pkg/net/http/fs_test.go index f968565f9b..a6f33cc42d 100644 --- a/src/pkg/net/http/fs_test.go +++ b/src/pkg/net/http/fs_test.go @@ -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 -- 2.48.1