Skip to content

Commit

Permalink
core: Add tests for many directives (#18)
Browse files Browse the repository at this point in the history
* Test logging and formatting

* Add basic_auth tests

* Add TestHTTPCircuitBreaker

* Add TestHTTPCompression

* Add TestHTTPWebsocketTCPConversion

* Test cleanup

* Add TestDomain

* Add metadata tests

* Add HTTP Scheme test

* Add blank tests

* Add remote_addr test to tcp

* Add CIDR restictions tests

* Update basic tests

* Update labels tests

* Use testing.Run for test cases

* Use require for testing

* Use generic test runner. Allows comparing config and then opts post provision

* Satisfy linter and make sure blank cidr opts aren't set

* Generic ngrok test type for testing main module

* Added make file for easier lint and coverage

* Correct error log message to indicate unhandled subdirective

* Basic auth test fixes. Additional bounds check tests.

* Basic auth block empty test

* Better basic auth

* Name of test

* Add help text to makefile, add fmt shortcut

* Remove all target from Makefile

* Address jsonfield in basic auth

* Test branch logic adjustment
  • Loading branch information
jtszalay authored Mar 23, 2023
1 parent 584808f commit e3e4cbf
Show file tree
Hide file tree
Showing 15 changed files with 1,965 additions and 449 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
# Test binary, built with `go test -c`
*.test

cover.out

Caddyfile
caddy
Expand Down
42 changes: 42 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif

# Targets

##@ General

# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
# target descriptions by '##'. The awk commands is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
# More info on the usage of ANSI control characters for terminal formatting:
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php

.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)


.PHONY: fmt
fmt: ## Run go fmt against code.
go fmt ./...

.PHONY: lint
lint: fmt ## Run the linter
golangci-lint run .

.PHONY: test
test: fmt ## Run tests.
go test -coverprofile cover.out ./...

.PHONY: coverage
coverage: test ## Run tests and Load coverage report
go tool cover -html cover.out
8 changes: 6 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ go 1.20

require (
github.com/caddyserver/caddy/v2 v2.6.4
github.com/stretchr/testify v1.8.0
go.uber.org/zap v1.24.0
golang.ngrok.com/ngrok v1.0.0
golang.ngrok.com/ngrok/log/zap v0.0.0-20230309141916-084685b542e7
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/caddyserver/certmagic v0.17.2 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect
Expand All @@ -28,6 +32,7 @@ require (
github.com/mholt/acmez v1.1.0 // indirect
github.com/miekg/dns v1.1.50 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
Expand All @@ -39,8 +44,6 @@ require (
github.com/quic-go/quic-go v0.32.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.ngrok.com/ngrok/log/zap v0.0.0-20230309141916-084685b542e7 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.6.0 // indirect
Expand All @@ -50,4 +53,5 @@ require (
golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.2.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
Expand Down Expand Up @@ -188,6 +189,7 @@ github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
Expand Down Expand Up @@ -231,10 +233,14 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -553,6 +559,9 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
93 changes: 44 additions & 49 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,16 @@ type HTTP struct {
WebsocketTCPConverter bool `json:"websocket_tcp_converter,omitempty"`

// A map of basicauth, username and password value pairs for this tunnel.
BasicAuth map[string]string `json:"basic_auth,omitempty"`
BasicAuth []basicAuthCred `json:"basic_auth,omitempty"`

l *zap.Logger
}

type basicAuthCred struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}

// CaddyModule implements caddy.Module
func (*HTTP) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
Expand All @@ -62,9 +67,7 @@ func (*HTTP) CaddyModule() caddy.ModuleInfo {
func (t *HTTP) Provision(ctx caddy.Context) error {
t.l = ctx.Logger()

if err := t.doReplace(); err != nil {
return fmt.Errorf("loading doing replacements: %v", err)
}
t.doReplace()

if err := t.provisionOpts(); err != nil {
return fmt.Errorf("provisioning http tunnel opts: %v", err)
Expand Down Expand Up @@ -99,25 +102,28 @@ func (t *HTTP) provisionOpts() error {
}

if t.Scheme != "" {
if t.Scheme == "http" {
switch t.Scheme {
case "http":
t.opts = append(t.opts, config.WithScheme(config.SchemeHTTP))
} else if t.Scheme == "https" {
case "https":
t.opts = append(t.opts, config.WithScheme(config.SchemeHTTPS))
default:
return fmt.Errorf("unrecognized http tunnel scheme %s", t.Scheme)
}
}

if t.WebsocketTCPConverter {
t.opts = append(t.opts, config.WithWebsocketTCPConversion())
}

for username, password := range t.BasicAuth {
t.opts = append(t.opts, config.WithBasicAuth(username, password))
for _, basic_auth := range t.BasicAuth {
t.opts = append(t.opts, config.WithBasicAuth(basic_auth.Username, basic_auth.Password))
}

return nil
}

func (t *HTTP) doReplace() error {
func (t *HTTP) doReplace() {
repl := caddy.NewReplacer()
replaceableFields := []*string{
&t.Metadata,
Expand All @@ -131,39 +137,26 @@ func (t *HTTP) doReplace() error {
*field = actual
}

replacedAllowCIDR := make([]string, len(t.AllowCIDR))

for index, cidr := range t.AllowCIDR {
actual := repl.ReplaceKnown(cidr, "")

replacedAllowCIDR[index] = actual
t.AllowCIDR[index] = actual
}

t.AllowCIDR = replacedAllowCIDR

replacedDenyCIDR := make([]string, len(t.DenyCIDR))

for index, cidr := range t.DenyCIDR {
actual := repl.ReplaceKnown(cidr, "")

replacedDenyCIDR[index] = actual
t.DenyCIDR[index] = actual
}

t.DenyCIDR = replacedDenyCIDR
for i, basic_auth := range t.BasicAuth {
actualUsername := repl.ReplaceKnown(basic_auth.Username, "")

replacedBasicAuth := make(map[string]string, len(t.BasicAuth))
actualPassword := repl.ReplaceKnown(basic_auth.Password, "")

for username, password := range t.BasicAuth {
actualUsername := repl.ReplaceKnown(username, "")
t.BasicAuth[i] = basicAuthCred{Username: actualUsername, Password: actualPassword}

actualPassword := repl.ReplaceKnown(password, "")

replacedBasicAuth[actualUsername] = actualPassword
}

t.BasicAuth = replacedBasicAuth

return nil
}

// convert to ngrok's Tunnel type
Expand Down Expand Up @@ -217,7 +210,7 @@ func (t *HTTP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return err
}
default:
return d.ArgErr()
return d.Errf("unrecognized subdirective %s", subdirective)
}
}
}
Expand Down Expand Up @@ -281,52 +274,54 @@ func (t *HTTP) unmarshalDenyCidr(d *caddyfile.Dispenser) error {

func (t *HTTP) unmarshalBasicAuth(d *caddyfile.Dispenser) error {
var (
username string
password string
hasArgs bool
username string
password string
foundBasicAuth bool
)

if t.BasicAuth == nil {
t.BasicAuth = map[string]string{}
}

minLenPassword := 8

username = d.Val()
if d.NextArg() { // basic_auth is defined inline

username = d.Val()

if d.CountRemainingArgs() != 0 { // basicauth is defined inline
if !d.AllArgs(&username, &password) {
hasArgs = true
if !d.AllArgs(&password) {
return d.ArgErr()
}

if username == "" || password == "" {
return d.Err("username and password cannot be empty or missing")
}
foundBasicAuth = true

if len(password) < minLenPassword {
return d.Err("password must be at least eight characters.")
}

t.BasicAuth[username] = password
t.BasicAuth = append(t.BasicAuth, basicAuthCred{Username: username, Password: password})

return nil
}

for nesting := d.Nesting(); d.NextBlock(nesting); { // block of basicauth
for nesting := d.Nesting(); d.NextBlock(nesting); { // block of basic_auth
username := d.Val()

if hasArgs {
return d.Err("cannot specify basic_auth in both arguments and block") // because it would be weird
}

if !d.AllArgs(&password) {
return d.ArgErr()
}

if username == "" || password == "" {
return d.Err("username and password cannot be empty or missing")
}
foundBasicAuth = true

if len(password) < minLenPassword {
return d.Err("password must be at least eight characters.")
}

t.BasicAuth[username] = password
t.BasicAuth = append(t.BasicAuth, basicAuthCred{Username: username, Password: password})
}

if !foundBasicAuth {
return d.ArgErr()
}

return nil
Expand Down
Loading

0 comments on commit e3e4cbf

Please sign in to comment.