Skip to content
This repository was archived by the owner on Mar 8, 2024. It is now read-only.

Commit 468ac0d

Browse files
authored
refactor(in-memory): resolve the general function (#11)
* resolve condition allowedto cache * test the functionality * feat(inmemcache): add a few configurations * chore(roundtripper): remove print * chore(go.mod): tidy-up
1 parent 64bcf90 commit 468ac0d

8 files changed

Lines changed: 63 additions & 93 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@
1010

1111
# Output of the go coverage tool, specifically when used with LiteIDE
1212
*.out
13-
.vscode
13+
.vscode
14+
sample
15+
bin/

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mockery-prepare:
2+
@echo "Installing mockery"
3+
@go get -u github.com/vektra/mockery

cache/cache.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,16 @@ type Interactor interface {
2323

2424
// CachedResponse represent the cacher struct item
2525
type CachedResponse struct {
26-
StatusCode int `json:"statusCode"`
27-
DumpedResponse []byte `json:"response"`
28-
DumpedBody []byte `json:"body"`
29-
RequestURI string `json:"requestUri"`
30-
RequestMethod string `json:"requestMethod"`
31-
CachedTime time.Time `json:"cachedTime"`
26+
// StatusCode int `json:"statusCode"`
27+
DumpedResponse []byte `json:"response"`
28+
// DumpedBody []byte `json:"body"`
29+
RequestURI string `json:"requestUri"`
30+
RequestMethod string `json:"requestMethod"`
31+
CachedTime time.Time `json:"cachedTime"`
3232
}
3333

3434
// Validate will validate the cached response
3535
func (c *CachedResponse) Validate() (err error) {
36-
if c.StatusCode == 0 {
37-
return ErrInvalidCachedResponse
38-
}
39-
4036
if c.RequestMethod == "" {
4137
return ErrInvalidCachedResponse
4238
}

go.mod

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
module github.com/bxcodec/hache
22

3-
require (
4-
github.com/bxcodec/gotcha v1.0.0-beta.2
5-
github.com/hashicorp/golang-lru v0.5.1
6-
)
3+
go 1.12
4+
5+
require github.com/bxcodec/gotcha v1.0.0-beta.2

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
11
github.com/bxcodec/gotcha v1.0.0-beta.2 h1:0jY/Mx6O5jzM2fkcz84zzyy67hLu/bKGJEFTtcRmw5I=
22
github.com/bxcodec/gotcha v1.0.0-beta.2/go.mod h1:MEL9PRYL9Squu1zxreMIzJU6xtMouPmQybWEtXrL1nk=
3-
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
4-
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=

hache.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,10 @@ func New(client *http.Client, cacheInteractor cache.Interactor) (err error) {
1717
}
1818

1919
func newClient(client *http.Client, cacheInteractor cache.Interactor) (err error) {
20-
roundtrip := &RoundTrip{
21-
DefaultRoundTripper: client.Transport,
22-
CacheInteractor: cacheInteractor,
20+
if client.Transport == nil {
21+
client.Transport = http.DefaultTransport
2322
}
24-
client.Transport = roundtrip
23+
client.Transport = NewRoundtrip(client.Transport, cacheInteractor)
2524
return
2625
}
2726

@@ -37,6 +36,5 @@ func NewWithInmemoryCache(client *http.Client, duration ...time.Duration) (err e
3736
SetExpiryTime(expiryTime).SetMaxSizeItem(100),
3837
)
3938

40-
newClient(client, inmem.NewCache(c))
41-
return
39+
return newClient(client, inmem.NewCache(c))
4240
}

roundtriper.go

Lines changed: 31 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"bufio"
55
"bytes"
66
"fmt"
7-
"io/ioutil"
7+
"log"
88
"net/http"
99
"net/http/httputil"
1010
"strings"
@@ -15,57 +15,13 @@ import (
1515

1616
// Headers
1717
const (
18-
HeaderAccept = "Accept"
19-
HeaderAcceptEncoding = "Accept-Encoding"
20-
HeaderAllow = "Allow"
21-
HeaderAuthorization = "Authorization"
22-
HeaderContentDisposition = "Content-Disposition"
23-
HeaderContentEncoding = "Content-Encoding"
24-
HeaderContentLength = "Content-Length"
25-
HeaderContentType = "Content-Type"
26-
HeaderCookie = "Cookie"
27-
HeaderCacheControl = "Cache-Control"
28-
HeaderSetCookie = "Set-Cookie"
29-
HeaderIfModifiedSince = "If-Modified-Since"
30-
HeaderLastModified = "Last-Modified"
31-
HeaderLocation = "Location"
32-
HeaderUpgrade = "Upgrade"
33-
HeaderVary = "Vary"
34-
HeaderWWWAuthenticate = "WWW-Authenticate"
35-
HeaderXForwardedFor = "X-Forwarded-For"
36-
HeaderXForwardedProto = "X-Forwarded-Proto"
37-
HeaderXForwardedProtocol = "X-Forwarded-Protocol"
38-
HeaderXForwardedSsl = "X-Forwarded-Ssl"
39-
HeaderXUrlScheme = "X-Url-Scheme"
40-
HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
41-
HeaderXRealIP = "X-Real-IP"
42-
HeaderXRequestID = "X-Request-ID"
43-
HeaderXRequestedWith = "X-Requested-With"
44-
HeaderServer = "Server"
45-
HeaderOrigin = "Origin"
46-
47-
// Access control
48-
HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
49-
HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers"
50-
HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
51-
HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
52-
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
53-
HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
54-
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
55-
HeaderAccessControlMaxAge = "Access-Control-Max-Age"
56-
57-
// Security
58-
HeaderStrictTransportSecurity = "Strict-Transport-Security"
59-
HeaderXContentTypeOptions = "X-Content-Type-Options"
60-
HeaderXXSSProtection = "X-XSS-Protection"
61-
HeaderXFrameOptions = "X-Frame-Options"
62-
HeaderContentSecurityPolicy = "Content-Security-Policy"
63-
HeaderXCSRFToken = "X-CSRF-Token"
18+
HeaderAuthorization = "Authorization"
19+
HeaderCacheControl = "Cache-Control"
6420
)
6521

6622
var (
67-
// CachedAuthorizedRequest used for determine that a request with Authorization header should be cached or not
68-
CachedAuthorizedRequest = false // TODO(bxcodec): Need to revised about this feature
23+
// CacheAuthorizedRequest used for determine that a request with Authorization header should be cached or not
24+
CacheAuthorizedRequest = false // TODO(bxcodec): Need to revised about this feature
6925
)
7026

7127
// RoundTrip custom plugable' struct of implementation of the http.RoundTripper
@@ -74,16 +30,24 @@ type RoundTrip struct {
7430
CacheInteractor cache.Interactor
7531
}
7632

33+
// NewRoundtrip will create an implementations of cache http roundtripper
34+
func NewRoundtrip(defaultRoundTripper http.RoundTripper, cacheActor cache.Interactor) http.RoundTripper {
35+
return &RoundTrip{
36+
DefaultRoundTripper: defaultRoundTripper,
37+
CacheInteractor: cacheActor,
38+
}
39+
}
40+
7741
// RoundTrip the implementation of http.RoundTripper
7842
func (r *RoundTrip) RoundTrip(req *http.Request) (resp *http.Response, err error) {
7943
if allowedFromCache(req) {
80-
resp, err = getCachedResponse(r.CacheInteractor, req)
44+
resp, cachedItem, err := getCachedResponse(r.CacheInteractor, req)
8145
if resp != nil && err == nil {
82-
buildTheCachedResponseHeader(resp)
83-
return
46+
buildTheCachedResponseHeader(resp, cachedItem)
47+
return resp, err
8448
}
8549
}
86-
50+
err = nil
8751
resp, err = r.DefaultRoundTripper.RoundTrip(req)
8852
if err != nil {
8953
return
@@ -93,25 +57,21 @@ func (r *RoundTrip) RoundTrip(req *http.Request) (resp *http.Response, err error
9357
return
9458
}
9559

96-
storeRespToCache(r.CacheInteractor, req, resp)
60+
err = storeRespToCache(r.CacheInteractor, req, resp)
61+
if err != nil {
62+
log.Println(err)
63+
}
64+
9765
return
9866
}
9967

10068
func storeRespToCache(cacheInteractor cache.Interactor, req *http.Request, resp *http.Response) (err error) {
10169
cachedResp := cache.CachedResponse{
102-
StatusCode: resp.StatusCode,
10370
RequestMethod: req.Method,
10471
RequestURI: req.RequestURI,
10572
CachedTime: time.Now(),
10673
}
10774

108-
bodyBytes, err := ioutil.ReadAll(resp.Body)
109-
if err != nil {
110-
return
111-
}
112-
113-
resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
114-
cachedResp.DumpedBody = bodyBytes
11575
dumpedResponse, err := httputil.DumpResponse(resp, true)
11676
if err != nil {
11777
return
@@ -121,8 +81,8 @@ func storeRespToCache(cacheInteractor cache.Interactor, req *http.Request, resp
12181
return
12282
}
12383

124-
func getCachedResponse(cacheInteractor cache.Interactor, req *http.Request) (resp *http.Response, err error) {
125-
cachedResp, err := cacheInteractor.Get(getCacheKey(req))
84+
func getCachedResponse(cacheInteractor cache.Interactor, req *http.Request) (resp *http.Response, cachedResp cache.CachedResponse, err error) {
85+
cachedResp, err = cacheInteractor.Get(getCacheKey(req))
12686
if err != nil {
12787
return
12888
}
@@ -132,12 +92,13 @@ func getCachedResponse(cacheInteractor cache.Interactor, req *http.Request) (res
13292
if err != nil {
13393
return
13494
}
95+
13596
return
13697
}
13798

13899
func getCacheKey(req *http.Request) (key string) {
139100
key = fmt.Sprintf("%s %s", req.Method, req.RequestURI)
140-
if (CachedAuthorizedRequest ||
101+
if (CacheAuthorizedRequest ||
141102
(strings.ToLower(req.Header.Get(HeaderCacheControl)) == "private")) &&
142103
req.Header.Get(HeaderAuthorization) != "" {
143104
key = fmt.Sprintf("%s %s", key, req.Header.Get(HeaderAuthorization))
@@ -146,21 +107,22 @@ func getCacheKey(req *http.Request) (key string) {
146107
}
147108

148109
// buildTheCachedResponse will finalize the response header
149-
func buildTheCachedResponseHeader(resp *http.Response) {
150-
panic("TODO: (bxcodec) Add the header based on RFC 7234")
110+
func buildTheCachedResponseHeader(resp *http.Response, cachedResp cache.CachedResponse) {
111+
resp.Header.Add("Expires", cachedResp.CachedTime.String())
112+
// TODO: (bxcodec) add more headers related to cache
151113
}
152114

153115
// check the header if the response will cached or not
154116
func allowedToCache(req *http.Request, resp *http.Response) (ok bool) {
155117
// A request with authorization header must not be cached
156118
// https://tools.ietf.org/html/rfc7234#section-3.2
157119
// Unless configured by user to cache request by authorization
158-
if ok = !CachedAuthorizedRequest && req.Header.Get(HeaderAuthorization) != ""; !ok {
120+
if ok = (!CacheAuthorizedRequest && req.Header.Get(HeaderAuthorization) == ""); !ok {
159121
return
160122
}
161123

162124
// check if the request method allowed to be cached
163-
if ok = !requestMethodValid(req); !ok {
125+
if ok = requestMethodValid(req); !ok {
164126
return
165127
}
166128

@@ -176,7 +138,6 @@ func allowedToCache(req *http.Request, resp *http.Response) (ok bool) {
176138
if ok = resp.StatusCode == http.StatusOK; !ok {
177139
return
178140
}
179-
180141
return
181142
}
182143

roundtripper_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package hache_test
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
7+
"github.com/bxcodec/hache"
8+
)
9+
10+
func TestRoundtrip(t *testing.T) {
11+
client := &http.Client{}
12+
client.Transport = hache.NewRoundtrip(client.Transport, nil)
13+
}

0 commit comments

Comments
 (0)