Skip to content

Commit 86a9b05

Browse files
authored
Update c.Fresh(#311) (#317)
* update: ctx.BodyParser * add #311 * Update utils.go
1 parent 1cf0d25 commit 86a9b05

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

ctx.go

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import (
1414
"log"
1515
"mime"
1616
"mime/multipart"
17+
"net/http"
1718
"net/url"
1819
"path/filepath"
20+
"regexp"
1921
"strconv"
2022
"strings"
2123
"sync"
@@ -62,6 +64,7 @@ type Cookie struct {
6264
// Global variables
6365
var schemaDecoderForm = schema.NewDecoder()
6466
var schemaDecoderQuery = schema.NewDecoder()
67+
var cacheControlNoCacheRegexp, _ = regexp.Compile(`/(?:^|,)\s*?no-cache\s*?(?:,|$)/`)
6568

6669
// Ctx pool
6770
var poolCtx = sync.Pool{
@@ -407,8 +410,59 @@ func (ctx *Ctx) FormValue(key string) (value string) {
407410
}
408411

409412
// Fresh is not implemented yet, pull requests are welcome!
413+
// https://github.com/jshttp/fresh/blob/10e0471669dbbfbfd8de65bc6efac2ddd0bfa057/index.js#L33
410414
func (ctx *Ctx) Fresh() bool {
411-
return false
415+
// fields
416+
var modifiedSince = ctx.Get(HeaderIfModifiedSince)
417+
var noneMatch = ctx.Get(HeaderIfNoneMatch)
418+
419+
// unconditional request
420+
if modifiedSince == "" && noneMatch == "" {
421+
return false
422+
}
423+
424+
// Always return stale when Cache-Control: no-cache
425+
// to support end-to-end reload requests
426+
// https://tools.ietf.org/html/rfc2616#section-14.9.4
427+
var cacheControl = ctx.Get(HeaderCacheControl)
428+
if cacheControl != "" && cacheControlNoCacheRegexp.MatchString(cacheControl) {
429+
return false
430+
}
431+
432+
// if-none-match
433+
if noneMatch != "" && noneMatch != "*" {
434+
var etag = getString(ctx.Fasthttp.Response.Header.Peek(HeaderETag))
435+
if etag == "" {
436+
return false
437+
}
438+
var etagStal = true
439+
var matches = parseTokenList(getBytes(noneMatch))
440+
for _, match := range matches {
441+
if match == etag || match == "W/"+etag || "W/"+match == etag {
442+
etagStal = false
443+
break
444+
}
445+
}
446+
if etagStal {
447+
return false
448+
}
449+
450+
if modifiedSince != "" {
451+
var lastModified = getString(ctx.Fasthttp.Response.Header.Peek(HeaderLastModified))
452+
if lastModified != "" {
453+
lastModifiedTime, err := http.ParseTime(lastModified)
454+
if err != nil {
455+
return false
456+
}
457+
modifiedSinceTime, err := http.ParseTime(modifiedSince)
458+
if err != nil {
459+
return false
460+
}
461+
return lastModifiedTime.Before(modifiedSinceTime)
462+
}
463+
}
464+
}
465+
return true
412466
}
413467

414468
// Get returns the HTTP request header specified by field.

utils.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,34 @@ func (c *testConn) SetDeadline(t time.Time) error { return nil }
156156
func (c *testConn) SetReadDeadline(t time.Time) error { return nil }
157157
func (c *testConn) SetWriteDeadline(t time.Time) error { return nil }
158158

159+
// Adapted from:
160+
// https://github.com/jshttp/fresh/blob/10e0471669dbbfbfd8de65bc6efac2ddd0bfa057/index.js#L110
161+
func parseTokenList(noneMatchBytes []byte) []string {
162+
var (
163+
start int
164+
end int
165+
list []string
166+
)
167+
for i, v := range noneMatchBytes {
168+
switch v {
169+
case 0x20:
170+
if start == end {
171+
start = i + 1
172+
end = i + 1
173+
}
174+
case 0x2c:
175+
list = append(list, getString(noneMatchBytes[start:end]))
176+
start = i + 1
177+
end = i + 1
178+
default:
179+
end = i + 1
180+
}
181+
}
182+
183+
list = append(list, getString(noneMatchBytes[start:end]))
184+
return list
185+
}
186+
159187
// HTTP status codes were copied from net/http.
160188
var statusMessages = map[int]string{
161189
100: "Continue",

0 commit comments

Comments
 (0)