New Upstream Snapshot - golang-github-pquerna-cachecontrol

Ready changes

Summary

Merged new upstream version: 0.1.0+git20210401.1.348dae4+ds (was: 0.0~git20171018.0dec1b3).

Resulting package

Built on 2023-01-19T15:58 (took 4m25s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-snapshots golang-github-pquerna-cachecontrol-dev

Lintian Result

Diff

diff --git a/.travis.yml b/.travis.yml
index f140b49..0d966bb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,8 @@
+arch:
+  - amd64
+  - ppc64le
 language: go
 
-install:
-  - go get -d -v ./...
-  - go get -u github.com/stretchr/testify/require
-
 go:
-  - 1.7
-  - 1.8
-  - tip
+  - "1.15"
+  - "1.16"
diff --git a/README.md b/README.md
index da0b428..6be4317 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,6 @@
 # cachecontrol: HTTP Caching Parser and Interpretation
 
-[![GoDoc](https://godoc.org/github.com/pquerna/cachecontrol?status.svg)](https://godoc.org/github.com/pquerna/cachecontrol)[![Build Status](https://travis-ci.org/pquerna/cachecontrol.svg?branch=master)](https://travis-ci.org/pquerna/cachecontrol)
-
- 
+[![PkgGoDev](https://pkg.go.dev/badge/github.com/pquerna/cachecontrol?tab=doc)](https://pkg.go.dev/github.com/pquerna/cachecontrol?tab=doc)[![Build Status](https://travis-ci.org/pquerna/cachecontrol.svg?branch=main)](https://travis-ci.org/pquerna/cachecontrol) 
 
 `cachecontrol` implements [RFC 7234](http://tools.ietf.org/html/rfc7234) __Hypertext Transfer Protocol (HTTP/1.1): Caching__.  It does this by parsing the `Cache-Control` and other headers, providing information about requests and responses -- but `cachecontrol` does not implement an actual cache backend, just the control plane to make decisions about if a particular response is cachable.
 
diff --git a/api.go b/api.go
index f6f2858..5759a4c 100644
--- a/api.go
+++ b/api.go
@@ -25,7 +25,7 @@ import (
 )
 
 type Options struct {
-	// Set to True for a prviate cache, which is not shared amoung users (eg, in a browser)
+	// Set to True for a private cache, which is not shared among users (eg, in a browser)
 	// Set to False for a "shared" cache, which is more common in a server context.
 	PrivateCache bool
 }
diff --git a/cacheobject/directive.go b/cacheobject/directive.go
index 1e2a6c2..afc63dc 100644
--- a/cacheobject/directive.go
+++ b/cacheobject/directive.go
@@ -32,7 +32,7 @@ var (
 	ErrQuoteMismatch         = errors.New("Missing closing quote")
 	ErrMaxAgeDeltaSeconds    = errors.New("Failed to parse delta-seconds in `max-age`")
 	ErrSMaxAgeDeltaSeconds   = errors.New("Failed to parse delta-seconds in `s-maxage`")
-	ErrMaxStaleDeltaSeconds  = errors.New("Failed to parse delta-seconds in `min-fresh`")
+	ErrMaxStaleDeltaSeconds  = errors.New("Failed to parse delta-seconds in `max-stale`")
 	ErrMinFreshDeltaSeconds  = errors.New("Failed to parse delta-seconds in `min-fresh`")
 	ErrNoCacheNoArgs         = errors.New("Unexpected argument to `no-cache`")
 	ErrNoStoreNoArgs         = errors.New("Unexpected argument to `no-store`")
@@ -41,6 +41,10 @@ var (
 	ErrMustRevalidateNoArgs  = errors.New("Unexpected argument to `must-revalidate`")
 	ErrPublicNoArgs          = errors.New("Unexpected argument to `public`")
 	ErrProxyRevalidateNoArgs = errors.New("Unexpected argument to `proxy-revalidate`")
+	// Experimental
+	ErrImmutableNoArgs                  = errors.New("Unexpected argument to `immutable`")
+	ErrStaleIfErrorDeltaSeconds         = errors.New("Failed to parse delta-seconds in `stale-if-error`")
+	ErrStaleWhileRevalidateDeltaSeconds = errors.New("Failed to parse delta-seconds in `stale-while-revalidate`")
 )
 
 func whitespace(b byte) bool {
@@ -160,7 +164,7 @@ type cacheDirective interface {
 	addPair(s string, v string) error
 }
 
-// LOW LEVEL API: Repersentation of possible request directives in a `Cache-Control` header: http://tools.ietf.org/html/rfc7234#section-5.2.1
+// LOW LEVEL API: Representation of possible request directives in a `Cache-Control` header: http://tools.ietf.org/html/rfc7234#section-5.2.1
 //
 // Note: Many fields will be `nil` in practice.
 //
@@ -185,6 +189,7 @@ type RequestCacheDirectives struct {
 	// assigned to max-stale, then the client is willing to accept a stale
 	// response of any age.
 	MaxStale DeltaSeconds
+	MaxStaleSet bool
 
 	// min-fresh(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.1.3
 	//
@@ -236,10 +241,10 @@ func (cd *RequestCacheDirectives) addToken(token string) error {
 	switch token {
 	case "max-age":
 		err = ErrMaxAgeDeltaSeconds
-	case "max-stale":
-		err = ErrMaxStaleDeltaSeconds
 	case "min-fresh":
 		err = ErrMinFreshDeltaSeconds
+	case "max-stale":
+		cd.MaxStaleSet = true
 	case "no-cache":
 		cd.NoCache = true
 	case "no-store":
@@ -403,6 +408,21 @@ type ResponseCacheDirectives struct {
 	// proxy-revalidate response directive.
 	SMaxAge DeltaSeconds
 
+	////
+	// Experimental features
+	// - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Extension_Cache-Control_directives
+	// - https://www.fastly.com/blog/stale-while-revalidate-stale-if-error-available-today
+	////
+
+	// immutable(cast-to-bool): experimental feature
+	Immutable bool
+
+	// stale-if-error(delta seconds): experimental feature
+	StaleIfError DeltaSeconds
+
+	// stale-while-revalidate(delta seconds): experimental feature
+	StaleWhileRevalidate DeltaSeconds
+
 	// Extensions: http://tools.ietf.org/html/rfc7234#section-5.2.3
 	//
 	// The Cache-Control header field can be extended through the use of one
@@ -416,6 +436,9 @@ func ParseResponseCacheControl(value string) (*ResponseCacheDirectives, error) {
 	cd := &ResponseCacheDirectives{
 		MaxAge:  -1,
 		SMaxAge: -1,
+		// Exerimantal stale timeouts
+		StaleIfError:         -1,
+		StaleWhileRevalidate: -1,
 	}
 
 	err := parse(value, cd)
@@ -446,6 +469,13 @@ func (cd *ResponseCacheDirectives) addToken(token string) error {
 		err = ErrMaxAgeDeltaSeconds
 	case "s-maxage":
 		err = ErrSMaxAgeDeltaSeconds
+	// Experimental
+	case "immutable":
+		cd.Immutable = true
+	case "stale-if-error":
+		err = ErrMaxAgeDeltaSeconds
+	case "stale-while-revalidate":
+		err = ErrMaxAgeDeltaSeconds
 	default:
 		cd.Extensions = append(cd.Extensions, token)
 	}
@@ -500,6 +530,13 @@ func (cd *ResponseCacheDirectives) addPair(token string, v string) error {
 		cd.MaxAge, err = parseDeltaSeconds(v)
 	case "s-maxage":
 		cd.SMaxAge, err = parseDeltaSeconds(v)
+	// Experimental
+	case "immutable":
+		err = ErrImmutableNoArgs
+	case "stale-if-error":
+		cd.StaleIfError, err = parseDeltaSeconds(v)
+	case "stale-while-revalidate":
+		cd.StaleWhileRevalidate, err = parseDeltaSeconds(v)
 	default:
 		// TODO(pquerna): this sucks, making user re-parse, and its technically not 'quoted' like the original,
 		// but this is still easier, just a SplitN on "="
diff --git a/cacheobject/directive_test.go b/cacheobject/directive_test.go
index 69d47b5..979ced5 100644
--- a/cacheobject/directive_test.go
+++ b/cacheobject/directive_test.go
@@ -30,7 +30,7 @@ func TestMaxAge(t *testing.T) {
 	require.NoError(t, err)
 	require.Equal(t, cd.MaxAge, DeltaSeconds(-1))
 
-	cd, err = ParseResponseCacheControl("max-age")
+	_, err = ParseResponseCacheControl("max-age")
 	require.Error(t, err)
 
 	cd, err = ParseResponseCacheControl("max-age=20")
@@ -41,7 +41,7 @@ func TestMaxAge(t *testing.T) {
 	require.NoError(t, err)
 	require.Equal(t, cd.MaxAge, DeltaSeconds(0))
 
-	cd, err = ParseResponseCacheControl("max-age=-1")
+	_, err = ParseResponseCacheControl("max-age=-1")
 	require.Error(t, err)
 }
 
@@ -50,7 +50,7 @@ func TestSMaxAge(t *testing.T) {
 	require.NoError(t, err)
 	require.Equal(t, cd.SMaxAge, DeltaSeconds(-1))
 
-	cd, err = ParseResponseCacheControl("s-maxage")
+	_, err = ParseResponseCacheControl("s-maxage")
 	require.Error(t, err)
 
 	cd, err = ParseResponseCacheControl("s-maxage=20")
@@ -61,7 +61,7 @@ func TestSMaxAge(t *testing.T) {
 	require.NoError(t, err)
 	require.Equal(t, cd.SMaxAge, DeltaSeconds(0))
 
-	cd, err = ParseResponseCacheControl("s-maxage=-1")
+	_, err = ParseResponseCacheControl("s-maxage=-1")
 	require.Error(t, err)
 }
 
@@ -246,6 +246,27 @@ func TestResPrivate(t *testing.T) {
 	require.Equal(t, cd.PrivatePresent, true)
 }
 
+func TestResImmutable(t *testing.T) {
+	cd, err := ParseResponseCacheControl(`immutable`)
+	require.NoError(t, err)
+	require.NotNil(t, cd)
+	require.Equal(t, cd.Immutable, true)
+}
+
+func TestResStaleIfError(t *testing.T) {
+	cd, err := ParseResponseCacheControl(`stale-if-error=99999`)
+	require.NoError(t, err)
+	require.NotNil(t, cd)
+	require.Equal(t, cd.StaleIfError, DeltaSeconds(99999))
+}
+
+func TestResStaleWhileRevalidate(t *testing.T) {
+	cd, err := ParseResponseCacheControl(`stale-while-revalidate=99999`)
+	require.NoError(t, err)
+	require.NotNil(t, cd)
+	require.Equal(t, cd.StaleWhileRevalidate, DeltaSeconds(99999))
+}
+
 func TestParseDeltaSecondsZero(t *testing.T) {
 	ds, err := parseDeltaSeconds("0")
 	require.NoError(t, err)
@@ -259,7 +280,7 @@ func TestParseDeltaSecondsLarge(t *testing.T) {
 }
 
 func TestParseDeltaSecondsVeryLarge(t *testing.T) {
-	ds, err := parseDeltaSeconds(fmt.Sprintf("%d", math.MaxInt64))
+	ds, err := parseDeltaSeconds(fmt.Sprintf("%d", int64(math.MaxInt64)))
 	require.NoError(t, err)
 	require.Equal(t, ds, DeltaSeconds(math.MaxInt32))
 }
@@ -307,7 +328,15 @@ func TestReqMaxAge(t *testing.T) {
 }
 
 func TestReqMaxStale(t *testing.T) {
-	cd, err := ParseRequestCacheControl(`max-stale=99999`)
+	cd, err := ParseRequestCacheControl(`max-stale`)
+	require.NoError(t, err)
+	require.NotNil(t, cd)
+	require.True(t, cd.MaxStaleSet)
+	require.Equal(t, cd.MaxStale, DeltaSeconds(-1))
+	require.Equal(t, cd.MaxAge, DeltaSeconds(-1))
+	require.Equal(t, cd.MinFresh, DeltaSeconds(-1))
+
+	cd, err = ParseRequestCacheControl(`max-stale=99999`)
 	require.NoError(t, err)
 	require.NotNil(t, cd)
 	require.Equal(t, cd.MaxStale, DeltaSeconds(99999))
@@ -322,13 +351,6 @@ func TestReqMaxAgeBroken(t *testing.T) {
 	require.Nil(t, cd)
 }
 
-func TestReqMaxStaleBroken(t *testing.T) {
-	cd, err := ParseRequestCacheControl(`max-stale`)
-	require.Error(t, err)
-	require.Equal(t, ErrMaxStaleDeltaSeconds, err)
-	require.Nil(t, cd)
-}
-
 func TestReqMinFresh(t *testing.T) {
 	cd, err := ParseRequestCacheControl(`min-fresh=99999`)
 	require.NoError(t, err)
diff --git a/cacheobject/object.go b/cacheobject/object.go
index 35f88b9..ae38a31 100644
--- a/cacheobject/object.go
+++ b/cacheobject/object.go
@@ -22,7 +22,7 @@ import (
 	"time"
 )
 
-// LOW LEVEL API: Repersents a potentially cachable HTTP object.
+// LOW LEVEL API: Represents a potentially cachable HTTP object.
 //
 // This struct is designed to be serialized efficiently, so in a high
 // performance caching server, things like Date-Strings don't need to be
@@ -44,7 +44,7 @@ type Object struct {
 	NowUTC time.Time
 }
 
-// LOW LEVEL API: Repersents the results of examinig an Object with
+// LOW LEVEL API: Represents the results of examining an Object with
 // CachableObject and ExpirationObject.
 //
 // TODO(pquerna): decide if this is a good idea or bad
@@ -55,33 +55,17 @@ type ObjectResults struct {
 	OutErr            error
 }
 
-// LOW LEVEL API: Check if a object is cachable.
-func CachableObject(obj *Object, rv *ObjectResults) {
-	rv.OutReasons = nil
-	rv.OutWarnings = nil
-	rv.OutErr = nil
-
+// LOW LEVEL API: Check if a request is cacheable.
+// This function doesn't reset the passed ObjectResults.
+func CachableRequestObject(obj *Object, rv *ObjectResults) {
 	switch obj.ReqMethod {
 	case "GET":
 		break
 	case "HEAD":
 		break
 	case "POST":
-		/**
-		  POST: http://tools.ietf.org/html/rfc7231#section-4.3.3
-
-		  Responses to POST requests are only cacheable when they include
-		  explicit freshness information (see Section 4.2.1 of [RFC7234]).
-		  However, POST caching is not widely implemented.  For cases where an
-		  origin server wishes the client to be able to cache the result of a
-		  POST in a way that can be reused by a later GET, the origin server
-		  MAY send a 200 (OK) response containing the result and a
-		  Content-Location header field that has the same value as the POST's
-		  effective request URI (Section 3.1.4.2).
-		*/
-		if !hasFreshness(obj.ReqDirectives, obj.RespDirectives, obj.RespHeaders, obj.RespExpiresHeader, obj.CacheIsPrivate) {
-			rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodPOST)
-		}
+		// Responses to POST requests can be cacheable if they include explicit freshness information
+		break
 
 	case "PUT":
 		rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodPUT)
@@ -103,16 +87,35 @@ func CachableObject(obj *Object, rv *ObjectResults) {
 	// To my knowledge, none of them are cachable. Please open a ticket if this is not the case!
 	//
 	default:
-		rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodUnkown)
+		rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodUnknown)
 	}
 
-	if obj.ReqDirectives.NoStore {
+	if obj.ReqDirectives != nil && obj.ReqDirectives.NoStore {
 		rv.OutReasons = append(rv.OutReasons, ReasonRequestNoStore)
 	}
+}
+
+// LOW LEVEL API: Check if a response is cacheable.
+// This function doesn't reset the passed ObjectResults.
+func CachableResponseObject(obj *Object, rv *ObjectResults) {
+	/**
+	  POST: http://tools.ietf.org/html/rfc7231#section-4.3.3
+
+	  Responses to POST requests are only cacheable when they include
+	  explicit freshness information (see Section 4.2.1 of [RFC7234]).
+	  However, POST caching is not widely implemented.  For cases where an
+	  origin server wishes the client to be able to cache the result of a
+	  POST in a way that can be reused by a later GET, the origin server
+	  MAY send a 200 (OK) response containing the result and a
+	  Content-Location header field that has the same value as the POST's
+	  effective request URI (Section 3.1.4.2).
+	*/
+	if obj.ReqMethod == http.MethodPost && !hasFreshness(obj.RespDirectives, obj.RespHeaders, obj.RespExpiresHeader, obj.CacheIsPrivate) {
+		rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodPOST)
+	}
 
 	// Storing Responses to Authenticated Requests: http://tools.ietf.org/html/rfc7234#section-3.2
-	authz := obj.ReqHeaders.Get("Authorization")
-	if authz != "" {
+	if obj.ReqHeaders.Get("Authorization") != "" {
 		if obj.RespDirectives.MustRevalidate ||
 			obj.RespDirectives.Public ||
 			obj.RespDirectives.SMaxAge != -1 {
@@ -149,18 +152,26 @@ func CachableObject(obj *Object, rv *ObjectResults) {
 	     *  contains a public response directive (see Section 5.2.2.5).
 	*/
 
-	expires := obj.RespHeaders.Get("Expires") != ""
-	statusCachable := cachableStatusCode(obj.RespStatusCode)
-
-	if expires ||
+	if obj.RespHeaders.Get("Expires") != "" ||
 		obj.RespDirectives.MaxAge != -1 ||
 		(obj.RespDirectives.SMaxAge != -1 && !obj.CacheIsPrivate) ||
-		statusCachable ||
+		cachableStatusCode(obj.RespStatusCode) ||
 		obj.RespDirectives.Public {
 		/* cachable by default, at least one of the above conditions was true */
-	} else {
-		rv.OutReasons = append(rv.OutReasons, ReasonResponseUncachableByDefault)
+		return
 	}
+
+	rv.OutReasons = append(rv.OutReasons, ReasonResponseUncachableByDefault)
+}
+
+// LOW LEVEL API: Check if a object is cachable.
+func CachableObject(obj *Object, rv *ObjectResults) {
+	rv.OutReasons = nil
+	rv.OutWarnings = nil
+	rv.OutErr = nil
+
+	CachableRequestObject(obj, rv)
+	CachableResponseObject(obj, rv)
 }
 
 var twentyFourHours = time.Duration(24 * time.Hour)
@@ -232,7 +243,7 @@ func ExpirationObject(obj *Object, rv *ObjectResults) {
 			println("Expiration: ", expiresTime.String())
 		}
 	} else {
-		// TODO(pquerna): what should the default behavoir be for expiration time?
+		// TODO(pquerna): what should the default behavior be for expiration time?
 	}
 
 	rv.OutExpirationTime = expiresTime
@@ -243,20 +254,29 @@ func UsingRequestResponse(req *http.Request,
 	statusCode int,
 	respHeaders http.Header,
 	privateCache bool) ([]Reason, time.Time, error) {
+	reasons, time, _, _, err := UsingRequestResponseWithObject(req, statusCode, respHeaders, privateCache)
+	return reasons, time, err
+}
 
+// Evaluate cachability based on an HTTP request, and parts of the response.
+// Returns the parsed Object as well.
+func UsingRequestResponseWithObject(req *http.Request,
+	statusCode int,
+	respHeaders http.Header,
+	privateCache bool) ([]Reason, time.Time, []Warning, *Object, error) {
 	var reqHeaders http.Header
 	var reqMethod string
 
 	var reqDir *RequestCacheDirectives = nil
 	respDir, err := ParseResponseCacheControl(respHeaders.Get("Cache-Control"))
 	if err != nil {
-		return nil, time.Time{}, err
+		return nil, time.Time{}, nil, nil, err
 	}
 
 	if req != nil {
 		reqDir, err = ParseRequestCacheControl(req.Header.Get("Cache-Control"))
 		if err != nil {
-			return nil, time.Time{}, err
+			return nil, time.Time{}, nil, nil, err
 		}
 		reqHeaders = req.Header
 		reqMethod = req.Method
@@ -279,7 +299,7 @@ func UsingRequestResponse(req *http.Request,
 	if respHeaders.Get("Date") != "" {
 		dateHeader, err = http.ParseTime(respHeaders.Get("Date"))
 		if err != nil {
-			return nil, time.Time{}, err
+			return nil, time.Time{}, nil, nil, err
 		}
 		dateHeader = dateHeader.UTC()
 	}
@@ -287,7 +307,7 @@ func UsingRequestResponse(req *http.Request,
 	if respHeaders.Get("Last-Modified") != "" {
 		lastModifiedHeader, err = http.ParseTime(respHeaders.Get("Last-Modified"))
 		if err != nil {
-			return nil, time.Time{}, err
+			return nil, time.Time{}, nil, nil, err
 		}
 		lastModifiedHeader = lastModifiedHeader.UTC()
 	}
@@ -312,19 +332,19 @@ func UsingRequestResponse(req *http.Request,
 
 	CachableObject(&obj, &rv)
 	if rv.OutErr != nil {
-		return nil, time.Time{}, rv.OutErr
+		return nil, time.Time{}, nil, nil, rv.OutErr
 	}
 
 	ExpirationObject(&obj, &rv)
 	if rv.OutErr != nil {
-		return nil, time.Time{}, rv.OutErr
+		return nil, time.Time{}, nil, nil, rv.OutErr
 	}
 
-	return rv.OutReasons, rv.OutExpirationTime, nil
+	return rv.OutReasons, rv.OutExpirationTime, rv.OutWarnings, &obj, nil
 }
 
 // calculate if a freshness directive is present: http://tools.ietf.org/html/rfc7234#section-4.2.1
-func hasFreshness(reqDir *RequestCacheDirectives, respDir *ResponseCacheDirectives, respHeaders http.Header, respExpires time.Time, privateCache bool) bool {
+func hasFreshness(respDir *ResponseCacheDirectives, respHeaders http.Header, respExpires time.Time, privateCache bool) bool {
 	if !privateCache && respDir.SMaxAge != -1 {
 		return true
 	}
diff --git a/cacheobject/object_test.go b/cacheobject/object_test.go
index 6986a7e..638886d 100644
--- a/cacheobject/object_test.go
+++ b/cacheobject/object_test.go
@@ -104,7 +104,7 @@ func TestUncachableMethods(t *testing.T) {
 		{"OPTIONS", ReasonRequestMethodOPTIONS},
 		{"CONNECT", ReasonRequestMethodCONNECT},
 		{"TRACE", ReasonRequestMethodTRACE},
-		{"MADEUP", ReasonRequestMethodUnkown},
+		{"MADEUP", ReasonRequestMethodUnknown},
 	}
 
 	for _, mp := range tc {
@@ -392,3 +392,37 @@ func TestExpirationExpiresNoServerDate(t *testing.T) {
 	require.Len(t, rv.OutWarnings, 0)
 	require.WithinDuration(t, now.Add(time.Second*1500), rv.OutExpirationTime, time.Second*1)
 }
+
+func TestCachableRequestObject(t *testing.T) {
+	ReqDirectives, err := ParseRequestCacheControl("")
+	require.NoError(t, err)
+
+	obj := Object{
+		ReqDirectives: ReqDirectives,
+		ReqHeaders:    http.Header{},
+		ReqMethod:     "GET",
+
+		NowUTC: time.Now().UTC(),
+	}
+
+	rv := ObjectResults{}
+	CachableRequestObject(&obj, &rv)
+	require.Len(t, rv.OutReasons, 0)
+
+	obj.ReqMethod = "PUT"
+	rv.OutReasons = nil
+	CachableRequestObject(&obj, &rv)
+	require.Len(t, rv.OutReasons, 1)
+}
+
+func TestCachableResponseObject(t *testing.T) {
+	obj := fill(t, time.Now().UTC())
+	obj.ReqMethod = "DELETE"
+	obj.RespDirectives.NoStore = true
+
+	rv := ObjectResults{}
+	CachableRequestObject(&obj, &rv)
+	require.Len(t, rv.OutReasons, 1)
+	CachableResponseObject(&obj, &rv)
+	require.Len(t, rv.OutReasons, 2)
+}
diff --git a/cacheobject/reasons.go b/cacheobject/reasons.go
index f53d1ad..2e75ae7 100644
--- a/cacheobject/reasons.go
+++ b/cacheobject/reasons.go
@@ -45,7 +45,7 @@ const (
 	ReasonRequestMethodTRACE
 
 	// The request method was not recognized by cachecontrol, and should not be cached.
-	ReasonRequestMethodUnkown
+	ReasonRequestMethodUnknown
 
 	// The request included an Cache-Control: no-store header
 	ReasonRequestNoStore
@@ -77,7 +77,7 @@ func (r Reason) String() string {
 		return "ReasonRequestMethodOPTIONS"
 	case ReasonRequestMethodTRACE:
 		return "ReasonRequestMethodTRACE"
-	case ReasonRequestMethodUnkown:
+	case ReasonRequestMethodUnknown:
 		return "ReasonRequestMethodUnkown"
 	case ReasonRequestNoStore:
 		return "ReasonRequestNoStore"
diff --git a/debian/changelog b/debian/changelog
index dfea382..c938d70 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+golang-github-pquerna-cachecontrol (0.1.0+git20210401.1.348dae4+ds-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+  * Drop patch FTBFS_988328.patch, present upstream.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Thu, 19 Jan 2023 15:55:22 -0000
+
 golang-github-pquerna-cachecontrol (0.0~git20171018.0dec1b3-2) unstable; urgency=medium
 
   * Team upload.
diff --git a/debian/patches/FTBFS_988328.patch b/debian/patches/FTBFS_988328.patch
deleted file mode 100644
index a660cc5..0000000
--- a/debian/patches/FTBFS_988328.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From 0923c70de240c513ff98174aa10a783bdcf0560e Mon Sep 17 00:00:00 2001
-From: Paul Querna <pquerna@apache.org>
-Date: Thu, 17 May 2018 09:25:54 -0700
-Subject: [PATCH] Explicit cast of MaxInt64 to int64.  Fixes #12
-
----
- cacheobject/directive_test.go | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/cacheobject/directive_test.go b/cacheobject/directive_test.go
-index 4076a98..f420fec 100644
---- a/cacheobject/directive_test.go
-+++ b/cacheobject/directive_test.go
-@@ -280,7 +280,7 @@ func TestParseDeltaSecondsLarge(t *testing.T) {
- }
- 
- func TestParseDeltaSecondsVeryLarge(t *testing.T) {
--	ds, err := parseDeltaSeconds(fmt.Sprintf("%d", math.MaxInt64))
-+	ds, err := parseDeltaSeconds(fmt.Sprintf("%d", int64(math.MaxInt64)))
- 	require.NoError(t, err)
- 	require.Equal(t, ds, DeltaSeconds(math.MaxInt32))
- }
diff --git a/debian/patches/series b/debian/patches/series
index d882cd5..e69de29 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1 +0,0 @@
-FTBFS_988328.patch
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..368205a
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+module github.com/pquerna/cachecontrol
+
+go 1.16
+
+require github.com/stretchr/testify v1.6.1
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..afe7890
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,11 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/gocode/src/github.com/pquerna/cachecontrol/go.mod
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/pquerna/cachecontrol/go.sum

No differences were encountered in the control files

More details

Full run details