Skip to content

Commit ee14624

Browse files
committed
include checkIfModifiedSince check
1 parent fc80fae commit ee14624

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

catalogd/internal/storage/localdir.go

Lines changed: 72 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,65 @@ 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+
func checkIfModifiedSince(r *http.Request, w http.ResponseWriter, modtime time.Time) condResult {
321+
ims := r.Header.Get("If-Modified-Since")
322+
if ims == "" || isZeroTime(modtime) {
323+
return condTrue
324+
}
325+
t, err := ParseTime(ims)
326+
if err != nil {
327+
httpError(w, err)
328+
return condNone
329+
}
330+
// The Last-Modified header truncates sub-second precision so
331+
// the modtime needs to be truncated too.
332+
modtime = modtime.Truncate(time.Second)
333+
if modtime.Compare(t) <= 0 {
334+
return condFalse
335+
}
336+
return condTrue
337+
}
338+
339+
// TimeFormat is the time format to use when generating times in HTTP
340+
// headers. It is like [time.RFC1123] but hard-codes GMT as the time
341+
// zone. The time being formatted must be in UTC for Format to
342+
// generate the correct format.
343+
//
344+
// For parsing this time format, see [ParseTime].
345+
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
346+
347+
var (
348+
unixEpochTime = time.Unix(0, 0)
349+
timeFormats = []string{
350+
TimeFormat,
351+
time.RFC850,
352+
time.ANSIC,
353+
}
354+
)
355+
356+
// isZeroTime reports whether t is obviously unspecified (either zero or Unix()=0).
357+
func isZeroTime(t time.Time) bool {
358+
return t.IsZero() || t.Equal(unixEpochTime)
359+
}
360+
361+
// ParseTime parses a time header (such as the Date: header),
362+
// trying each of the three formats allowed by HTTP/1.1:
363+
// [TimeFormat], [time.RFC850], and [time.ANSIC].
364+
func ParseTime(text string) (t time.Time, err error) {
365+
for _, layout := range timeFormats {
366+
t, err = time.Parse(layout, text)
367+
if err == nil {
368+
return
369+
}
370+
}
371+
return
372+
}

0 commit comments

Comments
 (0)