Skip to content

Commit

Permalink
fix: fixing to achieve good score on BetterCodeHub - #40
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiocicerchia committed Dec 4, 2020
1 parent 1584e9d commit f48f53d
Show file tree
Hide file tree
Showing 38 changed files with 894 additions and 832 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ jobs:
./go-proxy-cache -debug -config=config.sample.yml &
make test
- name: Coverage
run: make cover

- name: Codecov
run: |
redis-server &
./bin/gen-selfsigned-cert.sh
./go-proxy-cache -debug -config=config.sample.yml &
make cover
make codecov
run: make codecov

- uses: paambaati/codeclimate-action@latest

sca:
runs-on: ubuntu-20.04
Expand Down
14 changes: 10 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,16 @@ test-functional: ## test functional
test-endtoend: ## test endtoend
go test -race -count=1 --tags=endtoend ./...

cover: ## coverage
go test -race -count=1 --tags=all -coverprofile cover.out ./...
go tool cover -func=cover.out
go tool cover -html=cover.out
.codeclimate:
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
chmod +x ./cc-test-reporter

cover: .codeclimate ## coverage
# ./cc-test-reporter before-build
go test -race -count=1 --tags=all -coverprofile c.out ./...
go tool cover -func=c.out
go tool cover -html=c.out
# ./cc-test-reporter after-build

codecov: ## codecov
curl -s https://codecov.io/bash | bash
Expand Down
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,30 @@
Simple Reverse Proxy with Caching, written in Go, using Redis.
>>> **(semi) production-ready** <<<

[![MIT License](https://img.shields.io/badge/License-MIT-lightgrey.svg?longCache=true)](LICENSE)
[![MIT License](https://img.shields.io/badge/License-MIT-brightgreen.svg?longCache=true)](LICENSE)
[![Pull Requests](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?longCache=true)](https://github.com/fabiocicerchia/go-proxy-cache/pulls)
![Maintenance](https://img.shields.io/maintenance/yes/2020)
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)

![Last Commit](https://img.shields.io/github/last-commit/fabiocicerchia/go-proxy-cache)
![Release Date](https://img.shields.io/github/release-date/fabiocicerchia/go-proxy-cache)
![GitHub all releases](https://img.shields.io/github/downloads/fabiocicerchia/go-proxy-cache/total)

![Docker pulls](https://img.shields.io/docker/pulls/fabiocicerchia/go-proxy-cache.svg "Docker pulls")
![Docker stars](https://img.shields.io/docker/stars/fabiocicerchia/go-proxy-cache.svg "Docker stars")
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/fabiocicerchia/go-proxy-cache)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/fabiocicerchia/go-proxy-cache)

![Docker pulls](https://img.shields.io/docker/pulls/fabiocicerchia/go-proxy-cache "Docker pulls")
![Docker stars](https://img.shields.io/docker/stars/fabiocicerchia/go-proxy-cache "Docker stars")

![GitHub Workflow Status](https://img.shields.io/github/workflow/status/fabiocicerchia/go-proxy-cache/Builds)

[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4469/badge)](https://bestpractices.coreinfrastructure.org/projects/4469)
[![Go Report Card](https://goreportcard.com/badge/github.com/fabiocicerchia/go-proxy-cache)](https://goreportcard.com/report/github.com/fabiocicerchia/go-proxy-cache)
[![BCH compliance](https://bettercodehub.com/edge/badge/fabiocicerchia/go-proxy-cache?branch=main)](https://bettercodehub.com/results/fabiocicerchia/go-proxy-cache)
[![Go Report Card](https://goreportcard.com/badge/github.com/fabiocicerchia/go-proxy-cache)](https://goreportcard.com/report/github.com/fabiocicerchia/go-proxy-cache)
[![codecov](https://codecov.io/gh/fabiocicerchia/go-proxy-cache/branch/main/graph/badge.svg)](https://codecov.io/gh/fabiocicerchia/go-proxy-cache)
[![Maintainability](https://api.codeclimate.com/v1/badges/6cf8c9ea02b75fccf8b5/maintainability)](https://codeclimate.com/github/fabiocicerchia/go-proxy-cache/maintainability)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/fabiocicerchia/go-proxy-cache.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/fabiocicerchia/go-proxy-cache/alerts/)
![Builds](https://github.com/fabiocicerchia/go-proxy-cache/workflows/Builds/badge.svg)
[![Maintainability](https://img.shields.io/codeclimate/maintainability/fabiocicerchia/go-proxy-cache)](https://codeclimate.com/github/fabiocicerchia/go-proxy-cache/maintainability)
[![Technical Debt](https://img.shields.io/codeclimate/tech-debt/fabiocicerchia/go-proxy-cache)](https://codeclimate.com/github/fabiocicerchia/go-proxy-cache/maintainability)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/fabiocicerchia/go-proxy-cache.svg)](https://lgtm.com/projects/g/fabiocicerchia/go-proxy-cache/alerts/)

</center>

Expand Down
85 changes: 62 additions & 23 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@ import (
"time"

"github.com/fabiocicerchia/go-proxy-cache/cache/engine"
"github.com/fabiocicerchia/go-proxy-cache/config"
"github.com/fabiocicerchia/go-proxy-cache/utils"
"github.com/fabiocicerchia/go-proxy-cache/utils/slice"
log "github.com/sirupsen/logrus"
)

// CacheObj - Contains cache settings and current cached/cacheable object.
type CacheObj struct {
AllowedStatuses []int
AllowedMethods []string
CurrentObj URIObj
}

// URIObj - Holds details about the response
type URIObj struct {
URL url.URL
Expand All @@ -35,72 +42,96 @@ type URIObj struct {
}

// IsStatusAllowed - Checks if a status code is allowed to be cached.
func IsStatusAllowed(statusCode int) bool {
return utils.ContainsInt(config.Config.Cache.AllowedStatuses, statusCode)
func (c CacheObj) IsStatusAllowed() bool {
return slice.ContainsInt(c.AllowedStatuses, c.CurrentObj.StatusCode)
}

// IsMethodAllowed - Checks if a HTTP method is allowed to be cached.
func IsMethodAllowed(method string) bool {
return utils.ContainsString(config.Config.Cache.AllowedMethods, method)
func (c CacheObj) IsMethodAllowed() bool {
return slice.ContainsString(c.AllowedMethods, c.CurrentObj.Method)
}

// StoreFullPage - Stores the whole page response in cache.
func StoreFullPage(obj URIObj, expiration time.Duration) (bool, error) {
if !IsStatusAllowed(obj.StatusCode) || !IsMethodAllowed(obj.Method) || expiration < 1 {
return false, nil
// IsValid - Verifies the validity of a cacheable object.
func (c CacheObj) IsValid() (bool, error) {
if !c.IsStatusAllowed() || slice.LenSliceBytes(c.CurrentObj.Content) == 0 {
return false, fmt.Errorf(
"not allowed. status %d - content length %d",
c.CurrentObj.StatusCode,
slice.LenSliceBytes(c.CurrentObj.Content),
)
}

targetURL := obj.URL
targetURL.Host = obj.Host
return true, nil
}

meta, err := GetVary(obj.ResponseHeaders)
func (c CacheObj) handleMetadata(targetURL url.URL, expiration time.Duration) ([]string, error) {
meta, err := GetVary(c.CurrentObj.ResponseHeaders)
if err != nil {
return false, err
return []string{}, err
}

_, err = StoreMetadata(obj.Method, targetURL, meta, expiration)
_, err = StoreMetadata(c.CurrentObj.Method, targetURL, meta, expiration)
if err != nil {
return []string{}, err
}

return meta, nil
}

// StoreFullPage - Stores the whole page response in cache.
func (c CacheObj) StoreFullPage(expiration time.Duration) (bool, error) {
if !c.IsStatusAllowed() || !c.IsMethodAllowed() || expiration < 1 {
return false, nil
}

targetURL := c.CurrentObj.URL
targetURL.Host = c.CurrentObj.Host

meta, err := c.handleMetadata(targetURL, expiration)
if err != nil {
return false, err
}

encoded, err := engine.GetConn(targetURL.Host).Encode(obj)
encoded, err := engine.GetConn(targetURL.Host).Encode(c.CurrentObj)
if err != nil {
return false, err
}

key := StorageKey(obj.Method, targetURL, meta, obj.RequestHeaders)
key := StorageKey(c.CurrentObj.Method, targetURL, meta, c.CurrentObj.RequestHeaders)

return engine.GetConn(targetURL.Host).Set(key, encoded, expiration)
}

// RetrieveFullPage - Retrieves the whole page response from cache.
func RetrieveFullPage(method string, url url.URL, reqHeaders http.Header) (URIObj, error) {
func (c *CacheObj) RetrieveFullPage(method string, url url.URL, reqHeaders http.Header) error {
obj := &URIObj{}

meta, err := FetchMetadata(method, url)
if err != nil {
return *obj, fmt.Errorf("cannot fetch metadata: %s", err)
return fmt.Errorf("cannot fetch metadata: %s", err)
}

key := StorageKey(method, url, meta, reqHeaders)
log.Debugf("StorageKey: %s", key)

encoded, err := engine.GetConn(url.Host).Get(key)
if err != nil {
return *obj, fmt.Errorf("cannot get key: %s", err)
return fmt.Errorf("cannot get key: %s", err)
}

err = engine.GetConn(url.Host).Decode(encoded, obj)
if err != nil {
return *obj, fmt.Errorf("cannot decode: %s", err)
return fmt.Errorf("cannot decode: %s", err)
}

return *obj, nil
c.CurrentObj = *obj

return nil
}

// PurgeFullPage - Deletes the whole page response from cache.
func PurgeFullPage(method string, url url.URL) (bool, error) {
err := DeleteMetadata(method, url)
func (c CacheObj) PurgeFullPage(method string, url url.URL) (bool, error) {
err := PurgeMetadata(url)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -144,6 +175,14 @@ func FetchMetadata(method string, url url.URL) ([]string, error) {
return engine.GetConn(url.Host).List(key)
}

// PurgeMetadata - Purges the cache metadata for the requested URL.
func PurgeMetadata(url url.URL) error {
keyPattern := "META" + utils.StringSeparatorOne + "*" + utils.StringSeparatorOne + url.String()

_, err := engine.GetConn(url.Host).DelWildcard(keyPattern)
return err
}

// DeleteMetadata - Removes the cache metadata for the requested URL.
func DeleteMetadata(method string, url url.URL) error {
key := "META" + utils.StringSeparatorOne + method + utils.StringSeparatorOne + url.String()
Expand Down
32 changes: 17 additions & 15 deletions cache/engine/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import (
"time"

"github.com/fabiocicerchia/go-proxy-cache/config"
"github.com/fabiocicerchia/go-proxy-cache/utils"
"github.com/fabiocicerchia/go-proxy-cache/utils/base64"
circuitbreaker "github.com/fabiocicerchia/go-proxy-cache/utils/circuit-breaker"
"github.com/fabiocicerchia/go-proxy-cache/utils/msgpack"
"github.com/go-redis/redis/v8"
)

Expand Down Expand Up @@ -47,7 +49,7 @@ func (rdb *RedisClient) Close() error {

// PurgeAll - Purges all the existing keys on a DB.
func (rdb *RedisClient) PurgeAll() (bool, error) {
_, err := config.CB(rdb.Name).Execute(func() (interface{}, error) {
_, err := circuitbreaker.CB(rdb.Name).Execute(func() (interface{}, error) {
err := rdb.Client.FlushDB(ctx).Err()
return nil, err
})
Expand All @@ -61,7 +63,7 @@ func (rdb *RedisClient) PurgeAll() (bool, error) {

// Ping - Tests the connection.
func (rdb *RedisClient) Ping() bool {
_, err := config.CB(rdb.Name).Execute(func() (interface{}, error) {
_, err := circuitbreaker.CB(rdb.Name).Execute(func() (interface{}, error) {
err := rdb.Client.Ping(ctx).Err()
return nil, err
})
Expand All @@ -71,7 +73,7 @@ func (rdb *RedisClient) Ping() bool {

// Set - Sets a key, with certain value, with TTL for expiring.
func (rdb *RedisClient) Set(key string, value string, expiration time.Duration) (bool, error) {
_, err := config.CB(rdb.Name).Execute(func() (interface{}, error) {
_, err := circuitbreaker.CB(rdb.Name).Execute(func() (interface{}, error) {
err := rdb.Client.Set(ctx, key, value, expiration).Err()
return nil, err
})
Expand All @@ -85,7 +87,7 @@ func (rdb *RedisClient) Set(key string, value string, expiration time.Duration)

// Get - Gets a key.
func (rdb *RedisClient) Get(key string) (string, error) {
value, err := config.CB(rdb.Name).Execute(func() (interface{}, error) {
value, err := circuitbreaker.CB(rdb.Name).Execute(func() (interface{}, error) {
value, err := rdb.Client.Get(ctx, key).Result()
if value == "" && err != nil && err.Error() == "redis: nil" {
return "", nil
Expand All @@ -103,7 +105,7 @@ func (rdb *RedisClient) Get(key string) (string, error) {

// Del - Removes a key.
func (rdb *RedisClient) Del(key string) error {
_, err := config.CB(rdb.Name).Execute(func() (interface{}, error) {
_, err := circuitbreaker.CB(rdb.Name).Execute(func() (interface{}, error) {
err := rdb.Client.Del(ctx, key).Err()
return nil, err
})
Expand All @@ -113,7 +115,7 @@ func (rdb *RedisClient) Del(key string) error {

// DelWildcard - Removes the matching keys based on a pattern.
func (rdb *RedisClient) DelWildcard(key string) (int, error) {
k, err := config.CB(rdb.Name).Execute(func() (interface{}, error) {
k, err := circuitbreaker.CB(rdb.Name).Execute(func() (interface{}, error) {
keys, err := rdb.Client.Keys(ctx, key).Result()
return keys, err
})
Expand All @@ -125,7 +127,7 @@ func (rdb *RedisClient) DelWildcard(key string) (int, error) {
return l, nil
}

_, errDel := config.CB(rdb.Name).Execute(func() (interface{}, error) {
_, errDel := circuitbreaker.CB(rdb.Name).Execute(func() (interface{}, error) {
err := rdb.Client.Del(ctx, keys...).Err()
return nil, err
})
Expand All @@ -135,7 +137,7 @@ func (rdb *RedisClient) DelWildcard(key string) (int, error) {

// List - Returns the values in a list.
func (rdb *RedisClient) List(key string) ([]string, error) {
value, err := config.CB(rdb.Name).Execute(func() (interface{}, error) {
value, err := circuitbreaker.CB(rdb.Name).Execute(func() (interface{}, error) {
value, err := rdb.Client.LRange(ctx, key, 0, -1).Result()
return value, err
})
Expand All @@ -149,7 +151,7 @@ func (rdb *RedisClient) List(key string) ([]string, error) {

// Push - Append values to a list.
func (rdb *RedisClient) Push(key string, values []string) error {
_, err := config.CB(rdb.Name).Execute(func() (interface{}, error) {
_, err := circuitbreaker.CB(rdb.Name).Execute(func() (interface{}, error) {
err := rdb.Client.RPush(ctx, key, values).Err()
return nil, err
})
Expand All @@ -159,7 +161,7 @@ func (rdb *RedisClient) Push(key string, values []string) error {

// Expire - Sets a TTL on a key.
func (rdb *RedisClient) Expire(key string, expiration time.Duration) error {
_, err := config.CB(rdb.Name).Execute(func() (interface{}, error) {
_, err := circuitbreaker.CB(rdb.Name).Execute(func() (interface{}, error) {
err := rdb.Client.Expire(ctx, key, expiration).Err()
return nil, err
})
Expand All @@ -169,23 +171,23 @@ func (rdb *RedisClient) Expire(key string, expiration time.Duration) error {

// Encode - Encodes an object with msgpack.
func (rdb *RedisClient) Encode(obj interface{}) (string, error) {
value, err := utils.MsgpackEncode(obj)
value, err := msgpack.Encode(obj)
if err != nil {
return "", err
}

encoded := utils.Base64Encode(value)
encoded := base64.Encode(value)

return encoded, nil
}

// Decode - Decodes an object with msgpack.
func (rdb *RedisClient) Decode(encoded string, obj interface{}) error {
decoded, err := utils.Base64Decode(encoded)
decoded, err := base64.Decode(encoded)
if err != nil {
return err
}

err = utils.MsgpackDecode(decoded, obj)
err = msgpack.Decode(decoded, obj)
return err
}
Loading

0 comments on commit f48f53d

Please sign in to comment.