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

feat(roundtripper): add roundtripper implementation #9

Merged
merged 1 commit into from
Apr 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 0 additions & 23 deletions goche.go

This file was deleted.

63 changes: 63 additions & 0 deletions hache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package hache

import (
"errors"
"net/http"
"time"
)

var (
// ErrInvalidCachedResponse will throw if the cached response is invalid
ErrInvalidCachedResponse = errors.New("Cached Response is Invalid")
)

// New ...
func New(client *http.Client, cacheInteractor CacheInteractor) (err error) {
roundtrip := &RoundTrip{
DefaultRoundTripper: client.Transport,
CacheInteractor: cacheInteractor,
}
client.Transport = roundtrip
return
}

// NewWithInmemoryCache will create a complete cache-support of HTTP client with using inmemory cache.
// If the duration not set, the cache will use LFU algorithm
func NewWithInmemoryCache(client *http.Client, duration ...time.Duration) (err error) {
panic("TODO: (bxcodec)")
return
}

// CachedResponse represent the cacher struct item
type CachedResponse struct {
StatusCode int `json:"statusCode"`
DumpedResponse []byte `json:"body"`
RequestURI string `json:"requestUri"`
RequestMethod string `json:"requestMethod"`
CachedTime time.Time `json:"cachedTime"`
}

// Validate will validate the cached response
func (c *CachedResponse) Validate() (err error) {
if c.StatusCode == 0 {
return ErrInvalidCachedResponse
}

if c.RequestMethod == "" {
return ErrInvalidCachedResponse
}

if c.RequestURI == "" {
return ErrInvalidCachedResponse
}

if len(c.DumpedResponse) == 0 {
return ErrInvalidCachedResponse
}

if c.CachedTime.IsZero() {
return ErrInvalidCachedResponse
}

return
}
90 changes: 84 additions & 6 deletions roundtriper.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package hache

import "net/http"
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"net/http"
"time"
)

// RoundTrip custom plugable' struct of implementation of the http.RoundTripper
type RoundTrip struct {
Expand All @@ -10,14 +17,85 @@ type RoundTrip struct {

// RoundTrip the implementation of http.RoundTripper
func (r *RoundTrip) RoundTrip(req *http.Request) (resp *http.Response, err error) {
panic("TODO: (bxcodec)")
if !isTheHeaderAllowCachedResponse(req) {
return r.DefaultRoundTripper.RoundTrip(req)
}

if !isHTTPMethodValid(req) {
return r.DefaultRoundTripper.RoundTrip(req)
}

resp, err = getCachedResponse(r.CacheInteractor, req)
if resp != nil && err == nil {
buildTheCachedResponseHeader(resp)
return
}

resp, err = r.DefaultRoundTripper.RoundTrip(req)
if err != nil {
return
}
storeRespToCache(r.CacheInteractor, req, resp)
return
}

func storeRespToCache(cacheInteractor CacheInteractor, req *http.Request, resp *http.Response) (err error) {
cachedResp := CachedResponse{
StatusCode: resp.StatusCode,
RequestMethod: req.Method,
RequestURI: req.RequestURI,
CachedTime: time.Now(),
}

bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}

resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
cachedResp.DumpedResponse = bodyBytes
err = cacheInteractor.Set(getCacheKey(req), cachedResp)
return
}

func getCachedResponse(cacheInteractor CacheInteractor, req *http.Request) (resp *http.Response, err error) {
item, err := cacheInteractor.Get(getCacheKey(req))
if err != nil {
return
}

cachedResp, ok := item.(CachedResponse)
if !ok {
return
}
if err = cachedResp.Validate(); err != nil {
return
}

cachedResponse := bytes.NewBuffer(cachedResp.DumpedResponse)
resp, err = http.ReadResponse(bufio.NewReader(cachedResponse), req)
if err != nil {
return
}
resp.StatusCode = cachedResp.StatusCode
return
}

func validateCacheHeader() {
panic("TODO: (bxcodec)")
func getCacheKey(req *http.Request) (key string) {
key = fmt.Sprintf("%s %s", req.Method, req.RequestURI)
return
}

// buildTheCachedResponse will finalize the response header
func buildTheCachedResponseHeader(resp *http.Response) {
panic("TODO: (bxcodec) Add the header based on RFC 7234")
}

// check the header if the response will cached or not
func isTheHeaderAllowCachedResponse(req *http.Request) bool {
panic("TODO: (bxcodec) check the header based on RFC 7234")
}

func validateHTTPMethod() {
panic("TODO: (bxcodec)")
func isHTTPMethodValid(req *http.Request) bool {
panic("TODO: (bxcodec) check the method verb based on RFC 7234")
}