Skip to content

Commit 9009472

Browse files
committed
include checkIfModifiedSince check
1 parent fc80fae commit 9009472

File tree

1 file changed

+78
-0
lines changed

1 file changed

+78
-0
lines changed

catalogd/internal/storage/localdir.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,16 @@ func (s *LocalDirV1) handleV1Query(w http.ResponseWriter, r *http.Request) {
217217
}
218218
defer catalogFile.Close()
219219

220+
w.Header().Set("Last-Modified", catalogStat.ModTime().UTC().Format(TimeFormat))
221+
switch checkIfModifiedSince(r, w, catalogStat.ModTime()) {
222+
case condNone:
223+
return
224+
case condFalse:
225+
w.WriteHeader(http.StatusNotModified)
226+
return
227+
case condTrue:
228+
}
229+
220230
schema := r.URL.Query().Get("schema")
221231
pkg := r.URL.Query().Get("package")
222232
name := r.URL.Query().Get("name")
@@ -298,3 +308,71 @@ func (s *LocalDirV1) getIndex(catalog string) (*index, error) {
298308
}
299309
return idx.(*index), nil
300310
}
311+
312+
type condResult int
313+
314+
const (
315+
condNone condResult = iota
316+
condTrue
317+
condFalse
318+
)
319+
320+
// Copyright 2009 The Go Authors. All rights reserved.
321+
// Use of this source code is governed by a BSD-style
322+
// license that can be found in the LICENSE file.
323+
//
324+
// Source: Originally from Go's net/http/fs.go
325+
// https://cs.opensource.google/go/go/+/master:src/net/http/fs.go
326+
func checkIfModifiedSince(r *http.Request, w http.ResponseWriter, modtime time.Time) condResult {
327+
ims := r.Header.Get("If-Modified-Since")
328+
if ims == "" || isZeroTime(modtime) {
329+
return condTrue
330+
}
331+
t, err := ParseTime(ims)
332+
if err != nil {
333+
httpError(w, err)
334+
return condNone
335+
}
336+
// The Last-Modified header truncates sub-second precision so
337+
// the modtime needs to be truncated too.
338+
modtime = modtime.Truncate(time.Second)
339+
if modtime.Compare(t) <= 0 {
340+
return condFalse
341+
}
342+
return condTrue
343+
}
344+
345+
// TimeFormat is the time format to use when generating times in HTTP
346+
// headers. It is like [time.RFC1123] but hard-codes GMT as the time
347+
// zone. The time being formatted must be in UTC for Format to
348+
// generate the correct format.
349+
//
350+
// For parsing this time format, see [ParseTime].
351+
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
352+
353+
var (
354+
unixEpochTime = time.Unix(0, 0)
355+
timeFormats = []string{
356+
TimeFormat,
357+
time.RFC850,
358+
time.ANSIC,
359+
}
360+
)
361+
362+
// isZeroTime reports whether t is obviously unspecified (either zero or Unix()=0).
363+
func isZeroTime(t time.Time) bool {
364+
return t.IsZero() || t.Equal(unixEpochTime)
365+
}
366+
367+
// ParseTime parses a time header (such as the Date: header),
368+
// trying each of the three formats allowed by HTTP/1.1:
369+
// [TimeFormat], [time.RFC850], and [time.ANSIC].
370+
func ParseTime(text string) (t time.Time, err error) {
371+
for _, layout := range timeFormats {
372+
t, err = time.Parse(layout, text)
373+
if err == nil {
374+
return
375+
}
376+
}
377+
return
378+
}

0 commit comments

Comments
 (0)