|
@ -21,6 +21,64 @@ import ( |
|
|
"github.com/chrislusf/seaweedfs/weed/util" |
|
|
"github.com/chrislusf/seaweedfs/weed/util" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Validates the preconditions. Returns true if GET/HEAD operation should not proceed.
|
|
|
|
|
|
// Preconditions supported are:
|
|
|
|
|
|
// If-Modified-Since
|
|
|
|
|
|
// If-Unmodified-Since
|
|
|
|
|
|
// If-Match
|
|
|
|
|
|
// If-None-Match
|
|
|
|
|
|
func checkPreconditions(w http.ResponseWriter, r *http.Request, entry *filer.Entry) bool { |
|
|
|
|
|
|
|
|
|
|
|
etag := filer.ETagEntry(entry) |
|
|
|
|
|
/// When more than one conditional request header field is present in a
|
|
|
|
|
|
/// request, the order in which the fields are evaluated becomes
|
|
|
|
|
|
/// important. In practice, the fields defined in this document are
|
|
|
|
|
|
/// consistently implemented in a single, logical order, since "lost
|
|
|
|
|
|
/// update" preconditions have more strict requirements than cache
|
|
|
|
|
|
/// validation, a validated cache is more efficient than a partial
|
|
|
|
|
|
/// response, and entity tags are presumed to be more accurate than date
|
|
|
|
|
|
/// validators. https://tools.ietf.org/html/rfc7232#section-5
|
|
|
|
|
|
if entry.Attr.Mtime.IsZero() { |
|
|
|
|
|
return false |
|
|
|
|
|
} |
|
|
|
|
|
w.Header().Set("Last-Modified", entry.Attr.Mtime.UTC().Format(http.TimeFormat)) |
|
|
|
|
|
|
|
|
|
|
|
ifMatchETagHeader := r.Header.Get("If-Match") |
|
|
|
|
|
ifUnmodifiedSinceHeader := r.Header.Get("If-Unmodified-Since") |
|
|
|
|
|
if ifMatchETagHeader != "" { |
|
|
|
|
|
if util.CanonicalizeETag(etag) != util.CanonicalizeETag(ifMatchETagHeader) { |
|
|
|
|
|
w.WriteHeader(http.StatusPreconditionFailed) |
|
|
|
|
|
return true |
|
|
|
|
|
} |
|
|
|
|
|
} else if ifUnmodifiedSinceHeader != "" { |
|
|
|
|
|
if t, parseError := time.Parse(http.TimeFormat, ifUnmodifiedSinceHeader); parseError == nil { |
|
|
|
|
|
if t.Before(entry.Attr.Mtime) { |
|
|
|
|
|
w.WriteHeader(http.StatusPreconditionFailed) |
|
|
|
|
|
return true |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ifNoneMatchETagHeader := r.Header.Get("If-None-Match") |
|
|
|
|
|
ifModifiedSinceHeader := r.Header.Get("If-Modified-Since") |
|
|
|
|
|
if ifNoneMatchETagHeader != "" { |
|
|
|
|
|
if util.CanonicalizeETag(etag) == util.CanonicalizeETag(ifNoneMatchETagHeader) { |
|
|
|
|
|
w.WriteHeader(http.StatusNotModified) |
|
|
|
|
|
return true |
|
|
|
|
|
} |
|
|
|
|
|
} else if ifModifiedSinceHeader != "" { |
|
|
|
|
|
if t, parseError := time.Parse(http.TimeFormat, ifModifiedSinceHeader); parseError == nil { |
|
|
|
|
|
if t.After(entry.Attr.Mtime) { |
|
|
|
|
|
w.WriteHeader(http.StatusNotModified) |
|
|
|
|
|
return true |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) { |
|
|
func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
|
|
|
|
path := r.URL.Path |
|
|
path := r.URL.Path |
|
@ -61,10 +119,8 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// set etag
|
|
|
|
|
|
etag := filer.ETagEntry(entry) |
|
|
etag := filer.ETagEntry(entry) |
|
|
if ifm := r.Header.Get("If-Match"); ifm != "" && (ifm != "\""+etag+"\"" && ifm != etag) { |
|
|
|
|
|
w.WriteHeader(http.StatusPreconditionFailed) |
|
|
|
|
|
|
|
|
if checkPreconditions(w, r, entry) { |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -81,19 +137,6 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) |
|
|
w.Header().Set("Content-Type", mimeType) |
|
|
w.Header().Set("Content-Type", mimeType) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// if modified since
|
|
|
|
|
|
if !entry.Attr.Mtime.IsZero() { |
|
|
|
|
|
w.Header().Set("Last-Modified", entry.Attr.Mtime.UTC().Format(http.TimeFormat)) |
|
|
|
|
|
if r.Header.Get("If-Modified-Since") != "" { |
|
|
|
|
|
if t, parseError := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); parseError == nil { |
|
|
|
|
|
if !t.Before(entry.Attr.Mtime) { |
|
|
|
|
|
w.WriteHeader(http.StatusNotModified) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// print out the header from extended properties
|
|
|
// print out the header from extended properties
|
|
|
for k, v := range entry.Extended { |
|
|
for k, v := range entry.Extended { |
|
|
if !strings.HasPrefix(k, "xattr-") { |
|
|
if !strings.HasPrefix(k, "xattr-") { |
|
@ -123,10 +166,6 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) |
|
|
w.Header().Set(xhttp.AmzTagCount, strconv.Itoa(tagCount)) |
|
|
w.Header().Set(xhttp.AmzTagCount, strconv.Itoa(tagCount)) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if inm := r.Header.Get("If-None-Match"); inm == "\""+etag+"\"" { |
|
|
|
|
|
w.WriteHeader(http.StatusNotModified) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
setEtag(w, etag) |
|
|
setEtag(w, etag) |
|
|
|
|
|
|
|
|
filename := entry.Name() |
|
|
filename := entry.Name() |
|
|