Skip to content

Commit

Permalink
Merge pull request #186 from foomo/feature/referrer
Browse files Browse the repository at this point in the history
feat: http referrer
  • Loading branch information
franklinkim authored Jul 7, 2023
2 parents 7578ff2 + b134b98 commit a7833b3
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ linters:
#- contextcheck # check the function whether to use a non-inherited context [fast: false, auto-fix: false]
#- cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false]
- decorder # check declaration order and count of types, constants, variables and functions [fast: true, auto-fix: false]
- depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
#- depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
#- dupl # Tool for code clone detection [fast: true, auto-fix: false]
- durationcheck # check for two durations multiplied together [fast: false, auto-fix: false]
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ lint.fix:

.PHONY: gomod
## Run go mod tidy
gomod:
tidy:
go mod tidy
cd example && go mod tidy

.PHONY: gomod.outdated
## Show outdated direct dependencies
gomod.outdated:
outdated:
go list -u -m -json all | go-mod-outdated -update -direct

## Show help text
Expand Down
23 changes: 5 additions & 18 deletions errors/wrappederror.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,22 @@
package keelerrors

import "errors"

type wrappedError struct {
err error
cause error
}

// NewWrappedError returns a new wrapped error
func NewWrappedError(err, cause error) error {
return &wrappedError{
err: err,
cause: cause,
}
}

func (e *wrappedError) As(target interface{}) bool {
return errors.As(e.err, target) || errors.As(e.cause, target)
}

func (e *wrappedError) Is(target error) bool {
return errors.Is(e.err, target) || errors.Is(e.cause, target)
}

func (e *wrappedError) Cause() error {
return e.cause
}

func (e *wrappedError) Unwrap() error {
return e.err
}

func (e *wrappedError) Error() string {
return e.err.Error() + ": " + e.cause.Error()
}

func (e *wrappedError) Unwrap() []error {
return []error{e.err, e.cause}
}
75 changes: 75 additions & 0 deletions errors/wrappederror_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package keelerrors_test

import (
"fmt"
"testing"

keelerrors "github.com/foomo/keel/errors"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)

func ExampleNewWrappedError() {
parentErr := errors.New("parent")
childErr := errors.New("child")
wrappedErr := keelerrors.NewWrappedError(parentErr, childErr)
fmt.Println(parentErr)
fmt.Println(childErr)
fmt.Println(wrappedErr)
// Output:
// parent
// child
// parent: child
}

func TestNewWrappedError(t *testing.T) {
parentErr := errors.New("parent")
childErr := errors.New("child")
assert.Error(t, keelerrors.NewWrappedError(parentErr, childErr))
}

func TestWrapped(t *testing.T) {
parentErr := errors.New("parent")
childErr := errors.New("child")
assert.Error(t, keelerrors.NewWrappedError(parentErr, childErr))
}

func Test_wrappedError_As(t *testing.T) {
type (
Parent struct {
error
}
Child struct {
error
}
)
parentErr := &Parent{error: errors.New("parent")}
childErr := &Child{error: errors.New("parent")}
wrappedErr := keelerrors.NewWrappedError(parentErr, childErr)

var (
p *Parent
c *Child
)
if assert.ErrorAs(t, wrappedErr, &p) {
assert.EqualError(t, p, parentErr.Error())
}
if assert.ErrorAs(t, wrappedErr, &c) {
assert.EqualError(t, c, childErr.Error())
}
}

func Test_wrappedError_Error(t *testing.T) {
parentErr := errors.New("parent")
childErr := errors.New("child")
wrappedErr := keelerrors.NewWrappedError(parentErr, childErr)
assert.Equal(t, wrappedErr.Error(), "parent: child")
}

func Test_wrappedError_Is(t *testing.T) {
parentErr := errors.New("parent")
childErr := errors.New("child")
wrappedErr := keelerrors.NewWrappedError(parentErr, childErr)
assert.ErrorIs(t, wrappedErr, parentErr)
assert.ErrorIs(t, wrappedErr, childErr)
}
2 changes: 1 addition & 1 deletion example/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/foomo/keel/example

go 1.19
go 1.20

require (
github.com/davecgh/go-spew v1.1.1
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/foomo/keel

go 1.19
go 1.20

require (
github.com/avast/retry-go v3.0.0+incompatible
Expand Down
19 changes: 19 additions & 0 deletions net/http/context/referrer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package context

import (
"context"
)

const ContextKeyReferrer contextKey = "referrer"

func GetReferrer(ctx context.Context) (string, bool) {
if value, ok := ctx.Value(ContextKeyReferrer).(string); ok {
return value, true
} else {
return "", false
}
}

func SetReferrer(ctx context.Context, referer string) context.Context {
return context.WithValue(ctx, ContextKeyReferrer, referer)
}
2 changes: 2 additions & 0 deletions net/http/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const (
HeaderLastModified = "Last-Modified"
HeaderLocation = "Location"
HeaderUpgrade = "Upgrade"
HeaderReferrer = "Referer"
HeaderVary = "Vary"
HeaderWWWAuthenticate = "WWW-Authenticate"
HeaderXForwardedHost = "X-Forwarded-Host"
Expand All @@ -36,6 +37,7 @@ const (
HeaderServer = "Server"
HeaderTrueClientIP = "True-Client-Ip"
HeaderOrigin = "Origin"
HeaderXReferrer = "X-Referrer"
HeaderUserAgent = "User-Agent"

// Cloudflare
Expand Down
67 changes: 67 additions & 0 deletions net/http/middleware/referrer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package middleware

import (
"net/http"

"github.com/foomo/keel/net/http/context"
"go.uber.org/zap"
)

type (
RefererOptions struct {
RequestHeader []string
SetContext bool
}
RefererOption func(*RefererOptions)
)

// GetDefaultRefererOptions returns the default options
func GetDefaultRefererOptions() RefererOptions {
return RefererOptions{
RequestHeader: []string{"X-Referer", "X-Referrer", "Referer", "Referrer"},
SetContext: true,
}
}

// RefererWithRequestHeader middleware option
func RefererWithRequestHeader(v ...string) RefererOption {
return func(o *RefererOptions) {
o.RequestHeader = append(o.RequestHeader, v...)
}
}

// RefererWithSetContext middleware option
func RefererWithSetContext(v bool) RefererOption {
return func(o *RefererOptions) {
o.SetContext = v
}
}

// Referer middleware
func Referer(opts ...RefererOption) Middleware {
options := GetDefaultRefererOptions()
for _, opt := range opts {
if opt != nil {
opt(&options)
}
}
return RefererWithOptions(options)
}

// RefererWithOptions middleware
func RefererWithOptions(opts RefererOptions) Middleware {
return func(l *zap.Logger, name string, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var referer string
for _, value := range opts.RequestHeader {
if referer = r.Header.Get(value); referer != "" {
break
}
}
if referer != "" && opts.SetContext {
r = r.WithContext(context.SetReferrer(r.Context(), referer))
}
next.ServeHTTP(w, r)
})
}
}
51 changes: 51 additions & 0 deletions net/http/roundtripware/referrer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package roundtripware

import (
"net/http"

"go.uber.org/zap"

keelhttpcontext "github.com/foomo/keel/net/http/context"
)

type (
ReferrerOptions struct {
Header string
}
ReferrerOption func(*ReferrerOptions)
ReferrerGenerator func() string
)

// GetDefaultReferrerOptions returns the default options
func GetDefaultReferrerOptions() ReferrerOptions {
return ReferrerOptions{
Header: "X-Referrer",
}
}

// ReferrerWithHeader middleware option
func ReferrerWithHeader(v string) ReferrerOption {
return func(o *ReferrerOptions) {
o.Header = v
}
}

// Referrer returns a RoundTripper which prints out the request & response object
func Referrer(opts ...ReferrerOption) RoundTripware {
o := GetDefaultReferrerOptions()
for _, opt := range opts {
if opt != nil {
opt(&o)
}
}
return func(l *zap.Logger, next Handler) Handler {
return func(r *http.Request) (*http.Response, error) {
if value := r.Header.Get(o.Header); value == "" {
if value, ok := keelhttpcontext.GetReferrer(r.Context()); ok && value != "" {
r.Header.Set(o.Header, value)
}
}
return next(r)
}
}
}
Loading

0 comments on commit a7833b3

Please sign in to comment.