Skip to content

Commit 7a0381f

Browse files
Fixed missing TLS files + Refactoring (#102)
* skipping HTTPS server configuration in case no TLS certificates are provided * linting code using several linters * fixed documentation * upgraded docker images version for redis, node, and nginx * fixed missing depencencies for github action tests * refactored error handling * removed stutter namings * refactored redis mutex locks * refactored redis circuit breaker calls * refactored config to get values automatically from env vars * refactored utils functions * breaking change: CACHE_ALLOWED_STATUSES and CACHE_ALLOWED_METHODS will now support spaces instead of commas * added tests for CONNECT HTTP method * refactored domainConfig to be included in RequestCall (reduced coupling with config.go) * resolved some TODOs * RedirectToHTTPS can use customizable RedirectStatusCode per domain * replaced SIGKILL handling with SIGTERM * customize TLS config per domain: CurvePreferences, MinVersion, MaxVersion, and CipherSuites * refactored transport.handleBody as there was no real error (removed also shouldPanicOnCopyError and ctx) * fixed nginx configuration for ssl settings * added more tests and increased coverage
1 parent 943aad0 commit 7a0381f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1162
-792
lines changed

.env.dist

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
################################################################################
1212

1313
# --- GENERIC
14-
SERVER_HTTP_PORT=80
1514
SERVER_HTTPS_PORT=443
15+
SERVER_HTTP_PORT=80
1616

1717
# --- GZIP
1818
# Automatically enable GZip compression on all requests.
@@ -79,7 +79,7 @@ FORWARD_SCHEME=
7979

8080
# List of IPs/Hostnames to be used as load balanced backend servers.
8181
# They'll be selected using a round robin algorithm.
82-
# A list of comma-separated IPs or Hostnames.
82+
# A list of space-separated IPs or Hostnames.
8383
LB_ENDPOINT_LIST=
8484

8585
# Forces redirect from HTTP to HTTPS.
@@ -92,10 +92,10 @@ REDIRECT_STATUS_CODE=301
9292

9393
# --- CACHE
9494
# --- REDIS SERVER
95+
REDIS_DB=0
9596
REDIS_HOST=
9697
REDIS_PASSWORD=
9798
REDIS_PORT=6379
98-
REDIS_DB=0
9999

100100
# --- TTL
101101
# Fallback storage TTL when saving the cache when no header is specified.

.github/linters/.golangci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
linters:
2+
enable-all: true
3+
disable:
4+
- gochecknoglobals
5+
- interfacer
6+
- maligned
7+
- scopelint
8+
- wrapcheck
9+
10+
issues:
11+
exclude-rules:
12+
- text: could not import C
13+
linters: [typecheck]
14+
- text: undefined
15+
linters: [typecheck]
16+
- text: undeclared name
17+
linters: [typecheck]

.github/linters/.jscpd.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"threshold": 0,
3+
"reporters": ["html", "markdown"],
4+
"ignore": [
5+
"**/*_test.go",
6+
"**/*.md"
7+
]
8+
}

.github/linters/.markdown-lint.yml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
line-length: false
2-
MD033:
2+
MD014: false # Dollar signs used before commands without showing output
3+
MD024: false # Multiple headings with the same content
4+
MD026: # Trailing punctuation in heading
5+
punctuation: ".,;:!。,;:!?"
6+
MD029: false # Ordered list item prefix
7+
MD033: # Inline HTML
38
allowed_elements:
9+
- center
410
- details
511
- summary
6-
MD026:
7-
punctuation: ".,;:!。,;:!?"
8-
MD038: false
12+
MD038: false # Spaces inside code span elements

.github/workflows/linter.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,3 @@ jobs:
1717
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
1818
VALIDATE_ALL_CODEBASE: true
1919
DOCKERFILE_HADOLINT_FILE_NAME: .hadolint.yml
20-
FILTER_REGEX_EXCLUDE: "nginx/(1.17.10|1.18.0|1.19.0|1.19.1|1.19.2)|tpl/.+.sh"

.github/workflows/main.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
container: golang:alpine
1818
services:
1919
redis:
20-
image: redis:6.0.8-alpine3.12
20+
image: redis:6.2.5-alpine3.14
2121
ports:
2222
- 6379/tcp
2323
options: >-
@@ -58,7 +58,7 @@ jobs:
5858
- name: Install Dependencies
5959
run: |
6060
apk update
61-
apk add bash curl gcc libc-dev make nghttp2 nodejs npm redis
61+
apk add bash curl gcc git libc-dev make nghttp2 nodejs npm redis openssh musl-dev net-snmp-dev
6262
6363
- name: Build
6464
run: make build-race
@@ -67,7 +67,6 @@ jobs:
6767
run: |
6868
echo "127.0.0.1 testing.local www.testing.local" | tee -a /etc/hosts
6969
./go-proxy-cache -debug -config=test/full-setup/config.yml &
70-
npm install test/full-setup
7170
make test
7271
env:
7372
REDIS_HOST: redis

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ test-endtoend: ## test endtoend
9494
go test -race -count=1 --tags=endtoend ./...
9595

9696
test-ws: ## test websocket
97+
cd test/full-setup && npm install
9798
node test/full-setup/ws_client.js
9899

99100
test-http2: ## test HTTP2

cache/cache.go

Lines changed: 59 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,35 @@ package cache
1010
// Repo: https://github.com/fabiocicerchia/go-proxy-cache
1111

1212
import (
13-
"errors"
14-
"fmt"
1513
"net/http"
1614
"net/url"
1715
"strings"
1816
"time"
1917

18+
"github.com/pkg/errors"
19+
2020
"github.com/fabiocicerchia/go-proxy-cache/cache/engine"
2121
"github.com/fabiocicerchia/go-proxy-cache/utils"
2222
"github.com/fabiocicerchia/go-proxy-cache/utils/slice"
2323
log "github.com/sirupsen/logrus"
2424
)
2525

26-
// CacheObj - Contains cache settings and current cached/cacheable object.
27-
type CacheObj struct {
28-
AllowedStatuses []int
29-
AllowedMethods []string
30-
CurrentObj URIObj
31-
DomainID string
26+
var errMissingRedisConnection = errors.New("missing redis connection")
27+
var errNotAllowed = errors.New("not allowed")
28+
var errCannotFetchMetadata = errors.New("cannot fetch metadata")
29+
var errCannotGetKey = errors.New("cannot get key")
30+
var errCannotDecode = errors.New("cannot decode")
31+
var errVaryWildcard = errors.New("vary: *")
32+
33+
// Object - Contains cache settings and current cached/cacheable object.
34+
type Object struct {
35+
AllowedStatuses []int
36+
AllowedMethods []string
37+
CurrentURIObject URIObj
38+
DomainID string
3239
}
3340

34-
// URIObj - Holds details about the response
41+
// URIObj - Holds details about the response.
3542
type URIObj struct {
3643
URL url.URL
3744
Host string
@@ -44,35 +51,34 @@ type URIObj struct {
4451
}
4552

4653
// IsStatusAllowed - Checks if a status code is allowed to be cached.
47-
func (c CacheObj) IsStatusAllowed() bool {
48-
return slice.ContainsInt(c.AllowedStatuses, c.CurrentObj.StatusCode)
54+
func (c Object) IsStatusAllowed() bool {
55+
return slice.ContainsInt(c.AllowedStatuses, c.CurrentURIObject.StatusCode)
4956
}
5057

5158
// IsMethodAllowed - Checks if a HTTP method is allowed to be cached.
52-
func (c CacheObj) IsMethodAllowed() bool {
53-
return slice.ContainsString(c.AllowedMethods, c.CurrentObj.Method)
59+
func (c Object) IsMethodAllowed() bool {
60+
return slice.ContainsString(c.AllowedMethods, c.CurrentURIObject.Method)
5461
}
5562

5663
// IsValid - Verifies the validity of a cacheable object.
57-
func (c CacheObj) IsValid() (bool, error) {
58-
if !c.IsStatusAllowed() || slice.LenSliceBytes(c.CurrentObj.Content) == 0 {
59-
return false, fmt.Errorf(
60-
"not allowed. status %d - content length %d",
61-
c.CurrentObj.StatusCode,
62-
slice.LenSliceBytes(c.CurrentObj.Content),
63-
)
64+
func (c Object) IsValid() (bool, error) {
65+
if !c.IsStatusAllowed() || slice.LenSliceBytes(c.CurrentURIObject.Content) == 0 {
66+
return false, errors.Wrapf(errNotAllowed,
67+
"status %d - content length %d",
68+
c.CurrentURIObject.StatusCode,
69+
slice.LenSliceBytes(c.CurrentURIObject.Content))
6470
}
6571

6672
return true, nil
6773
}
6874

69-
func (c CacheObj) handleMetadata(domainID string, targetURL url.URL, expiration time.Duration) ([]string, error) {
70-
meta, err := GetVary(c.CurrentObj.ResponseHeaders)
75+
func (c Object) handleMetadata(domainID string, targetURL url.URL, expiration time.Duration) ([]string, error) {
76+
meta, err := GetVary(c.CurrentURIObject.ResponseHeaders)
7177
if err != nil {
7278
return []string{}, err
7379
}
7480

75-
_, err = StoreMetadata(domainID, c.CurrentObj.Method, targetURL, meta, expiration)
81+
_, err = StoreMetadata(domainID, c.CurrentURIObject.Method, targetURL, meta, expiration)
7682
if err != nil {
7783
return []string{}, err
7884
}
@@ -81,15 +87,21 @@ func (c CacheObj) handleMetadata(domainID string, targetURL url.URL, expiration
8187
}
8288

8389
// StoreFullPage - Stores the whole page response in cache.
84-
func (c CacheObj) StoreFullPage(expiration time.Duration) (bool, error) {
90+
func (c Object) StoreFullPage(expiration time.Duration) (bool, error) {
8591
if !c.IsStatusAllowed() || !c.IsMethodAllowed() || expiration < 1 {
86-
log.Debugf("Not allowed to be stored. Status: %v - Method: %v - Expiration: %v", c.IsStatusAllowed(), c.IsMethodAllowed(), expiration)
92+
log.Debugf(
93+
"Not allowed to be stored. Status: %v - Method: %v - Expiration: %v",
94+
c.IsStatusAllowed(),
95+
c.IsMethodAllowed(),
96+
expiration,
97+
)
98+
8799
return false, nil
88100
}
89101

90-
targetURL := c.CurrentObj.URL
91-
targetURL.Scheme = c.CurrentObj.Scheme
92-
targetURL.Host = c.CurrentObj.Host
102+
targetURL := c.CurrentURIObject.URL
103+
targetURL.Scheme = c.CurrentURIObject.Scheme
104+
targetURL.Host = c.CurrentURIObject.Host
93105

94106
meta, err := c.handleMetadata(c.DomainID, targetURL, expiration)
95107
if err != nil {
@@ -98,61 +110,61 @@ func (c CacheObj) StoreFullPage(expiration time.Duration) (bool, error) {
98110

99111
conn := engine.GetConn(c.DomainID)
100112
if conn == nil {
101-
return false, fmt.Errorf("missing redis connection for %s", c.DomainID)
113+
return false, errors.Wrapf(errMissingRedisConnection, "Error for %s", c.DomainID)
102114
}
103115

104-
encoded, err := conn.Encode(c.CurrentObj)
116+
encoded, err := conn.Encode(c.CurrentURIObject)
105117
if err != nil {
106118
return false, err
107119
}
108120

109-
key := StorageKey(c.CurrentObj.Method, targetURL, meta, c.CurrentObj.RequestHeaders)
121+
key := StorageKey(c.CurrentURIObject.Method, targetURL, meta, c.CurrentURIObject.RequestHeaders)
110122

111123
return conn.Set(key, encoded, expiration)
112124
}
113125

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

118130
meta, err := FetchMetadata(c.DomainID, method, url)
119131
if err != nil {
120-
return fmt.Errorf("cannot fetch metadata: %s", err)
132+
return errors.Wrap(errCannotFetchMetadata, err.Error())
121133
}
122134

123135
conn := engine.GetConn(c.DomainID)
124136
if conn == nil {
125-
return fmt.Errorf("missing redis connection for %s", c.DomainID)
137+
return errors.Wrapf(errMissingRedisConnection, "Error for %s", c.DomainID)
126138
}
127139

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

131143
encoded, err := conn.Get(key)
132144
if err != nil {
133-
return fmt.Errorf("cannot get key: %s", err)
145+
return errors.Wrap(errCannotGetKey, err.Error())
134146
}
135147

136148
err = conn.Decode(encoded, obj)
137149
if err != nil {
138-
return fmt.Errorf("cannot decode: %s", err)
150+
return errors.Wrap(errCannotDecode, err.Error())
139151
}
140152

141-
c.CurrentObj = *obj
153+
c.CurrentURIObject = *obj
142154

143155
return nil
144156
}
145157

146158
// PurgeFullPage - Deletes the whole page response from cache.
147-
func (c CacheObj) PurgeFullPage(method string, url url.URL) (bool, error) {
159+
func (c Object) PurgeFullPage(method string, url url.URL) (bool, error) {
148160
err := PurgeMetadata(c.DomainID, url)
149161
if err != nil {
150162
return false, err
151163
}
152164

153165
conn := engine.GetConn(c.DomainID)
154166
if conn == nil {
155-
return false, fmt.Errorf("missing redis connection for %s", c.DomainID)
167+
return false, errors.Wrapf(errMissingRedisConnection, "Error for %s", c.DomainID)
156168
}
157169

158170
var meta []string
@@ -161,6 +173,7 @@ func (c CacheObj) PurgeFullPage(method string, url url.URL) (bool, error) {
161173
match := utils.StringSeparatorOne + "PURGE" + utils.StringSeparatorOne
162174
replace := utils.StringSeparatorOne + "*" + utils.StringSeparatorOne
163175
keyPattern := strings.Replace(key, match, replace, 1) + "*"
176+
164177
affected, err := conn.DelWildcard(keyPattern)
165178
if err != nil {
166179
return false, err
@@ -192,7 +205,7 @@ func FetchMetadata(domainID string, method string, url url.URL) ([]string, error
192205

193206
conn := engine.GetConn(domainID)
194207
if conn == nil {
195-
return []string{}, fmt.Errorf("missing redis connection for %s", domainID)
208+
return []string{}, errors.Wrapf(errMissingRedisConnection, "Error for %s", domainID)
196209
}
197210

198211
return conn.List(key)
@@ -204,10 +217,11 @@ func PurgeMetadata(domainID string, url url.URL) error {
204217

205218
conn := engine.GetConn(domainID)
206219
if conn == nil {
207-
return fmt.Errorf("missing redis connection for %s", domainID)
220+
return errors.Wrapf(errMissingRedisConnection, "Error for %s", domainID)
208221
}
209222

210223
_, err := conn.DelWildcard(keyPattern)
224+
211225
return err
212226
}
213227

@@ -217,10 +231,11 @@ func StoreMetadata(domainID string, method string, url url.URL, meta []string, e
217231

218232
conn := engine.GetConn(domainID)
219233
if conn == nil {
220-
return false, fmt.Errorf("missing redis connection for %s", domainID)
234+
return false, errors.Wrapf(errMissingRedisConnection, "Error for %s", domainID)
221235
}
222236

223-
_ = conn.Del(key) //nolint:golint,errcheck
237+
_ = conn.Del(key)
238+
224239
err := conn.Push(key, meta)
225240
if err != nil {
226241
return false, err
@@ -242,7 +257,7 @@ func GetVary(headers http.Header) ([]string, error) {
242257
vary := headers.Get("Vary")
243258

244259
if vary == "*" {
245-
return []string{}, errors.New("vary: *")
260+
return []string{}, errVaryWildcard
246261
}
247262

248263
varyList := strings.Split(vary, ",")

0 commit comments

Comments
 (0)