Skip to content

Commit

Permalink
AppendPath parameter (#476)
Browse files Browse the repository at this point in the history
* add new append-path parameter

* make fmt

* support specifying the part of the path
  • Loading branch information
Samu Tamminen authored Feb 9, 2022
1 parent c4aff67 commit d14d7fe
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
19 changes: 19 additions & 0 deletions pkg/filter/headerlookup/headerlookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"context"
"fmt"
"net/http"
"regexp"
"strings"
"time"

Expand Down Expand Up @@ -55,6 +56,7 @@ type (
spec *Spec
etcdPrefix string
headerKey string
pathRegExp *regexp.Regexp

cache *lru.Cache
cluster cluster.Cluster
Expand All @@ -71,9 +73,15 @@ type (
// Spec defines header key and etcd prefix that form etcd key like /custom-data/{etcdPrefix}/{headerKey's value}.
// This /custom-data/{etcdPrefix}/{headerKey's value} is retrieved from etcd and HeaderSetters extract keys from the
// from the retrieved etcd item.
// When PathRegExp is defined, PathRegExp is used with `regexp.FindStringSubmatch` to identify a group from path.
// The first captured group is appended to the etcd key in following format:
// /custom-data/{etcdPrefix}/{headerKey's value}-{regex group} . For example, for path
// "/api/bananas/33" and pathRegExp: "^/api/([a-z]+)/[0-9]*", the group "bananas" is extracted and etcd key is
// /custom-data/{etcdPrefix}/{headerKey's value}-bananas.
Spec struct {
HeaderKey string `yaml:"headerKey" jsonschema:"required"`
EtcdPrefix string `yaml:"etcdPrefix" jsonschema:"required"`
PathRegExp string `yaml:"pathRegExp" jsonschema:"omitempty"`
HeaderSetters []*HeaderSetterSpec `yaml:"headerSetters" jsonschema:"required"`
}
)
Expand All @@ -97,6 +105,10 @@ func (spec Spec) Validate() error {
return fmt.Errorf("headerSetters[i].headerKey is required")
}
}

if _, err := regexp.Compile(spec.PathRegExp); err != nil {
return err
}
return nil
}

Expand Down Expand Up @@ -130,6 +142,7 @@ func (hl *HeaderLookup) Init(filterSpec *httppipeline.FilterSpec) {
hl.headerKey = http.CanonicalHeaderKey(hl.spec.HeaderKey)
hl.cache, _ = lru.New(cacheSize)
hl.stopCtx, hl.cancel = context.WithCancel(context.Background())
hl.pathRegExp = regexp.MustCompile(hl.spec.PathRegExp)
hl.watchChanges()
}

Expand Down Expand Up @@ -249,6 +262,12 @@ func (hl *HeaderLookup) handle(ctx httpcontext.HTTPContext) string {
logger.Warnf("request does not have header '%s'", hl.spec.HeaderKey)
return ""
}
if hl.spec.PathRegExp != "" {
path := ctx.Request().Path()
if match := hl.pathRegExp.FindStringSubmatch(path); match != nil && len(match) > 1 {
headerVal = headerVal + "-" + match[1]
}
}
headersToAdd, err := hl.lookup(headerVal)
if err != nil {
logger.Errorf(err.Error())
Expand Down
85 changes: 85 additions & 0 deletions pkg/filter/headerlookup/headerlookup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,24 @@ headerKey: "X-AUTH-USER"
etcdPrefix: "/credentials/"
headerSetters:
- etcdKey: "ext-id"
`,
`
name: headerLookup
kind: HeaderLookup
headerKey: "X-AUTH-USER"
etcdPrefix: "/credentials/"
headerSetters:
- headerKey: "X-ext-id"
`,
`
name: headerLookup
kind: HeaderLookup
headerKey: "X-AUTH-USER"
pathRegExp: "**"
etcdPrefix: "/credentials/"
headerSetters:
- headerKey: "X-ext-id"
etcdKey: "ext-id"
`,
}

Expand Down Expand Up @@ -236,6 +254,73 @@ extra-entry: "extra"
}
}

if hl.Status() != nil {
t.Errorf("status should be nil")
}
if len(hl.Description()) == 0 {
t.Errorf("description should not be empty")
}
hl.Close()
wg := &sync.WaitGroup{}
wg.Add(1)
clusterInstance.CloseServer(wg)
wg.Wait()
}

func TestHandleWithPath(t *testing.T) {
etcdDirName, err := ioutil.TempDir("", "etcd-headerlookup-path-test")
check(err)
defer os.RemoveAll(etcdDirName)
const config = `
name: headerLookup
kind: HeaderLookup
headerKey: "X-AUTH-USER"
etcdPrefix: "credentials/"
pathRegExp: "^/api/([a-z]+)/[0-9]*"
headerSetters:
- etcdKey: "ext-id"
headerKey: "user-ext-id"
`
clusterInstance := cluster.CreateClusterForTest(etcdDirName)
var mockMap sync.Map
supervisor := supervisor.NewMock(
nil, clusterInstance, mockMap, mockMap, nil, nil, false, nil, nil)

// let's put data to 'bob'
clusterInstance.Put("/custom-data/credentials/bob-bananas",
`
ext-id: 333
extra-entry: "extra"
`)
clusterInstance.Put("/custom-data/credentials/bob-pearls",
`
ext-id: 4444
extra-entry: "extra"
`)
hl, err := createHeaderLookup(config, nil, supervisor)
check(err)

ctx, header := prepareCtxAndHeader()
header.Set("X-AUTH-USER", "bob")
hl.Handle(ctx) // path does not match
if header.Get("user-ext-id") != "" {
t.Errorf("failed")
}
ctx.MockedRequest.MockedPath = func() string {
return "/api/bananas/9281"
}
hl.Handle(ctx)
if header.Get("user-ext-id") != "333" {
t.Errorf("failed")
}
ctx.MockedRequest.MockedPath = func() string {
return "/api/pearls/"
}
hl.Handle(ctx)
if header.Get("user-ext-id") != "4444" {
t.Errorf("failed")
}

hl.Close()
wg := &sync.WaitGroup{}
wg.Add(1)
Expand Down

0 comments on commit d14d7fe

Please sign in to comment.