Skip to content

Commit

Permalink
Intermediate status #59
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Schneider authored and Marcel Ludwig committed Oct 26, 2020
1 parent 6149d68 commit fc8437a
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 25 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
/*.coverage

#binary
/couper
7 changes: 7 additions & 0 deletions config/request/path_params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package request

// PathParam represents the named path param object.
type PathParam struct {
Name string
Position int
}
56 changes: 47 additions & 9 deletions config/runtime/handler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package runtime

import (
e "errors"
"fmt"
"io/ioutil"
"net"
Expand All @@ -16,6 +17,7 @@ import (

ac "github.com/avenga/couper/accesscontrol"
"github.com/avenga/couper/config"
"github.com/avenga/couper/config/request"
"github.com/avenga/couper/errors"
"github.com/avenga/couper/handler"
"github.com/avenga/couper/internal/seetie"
Expand Down Expand Up @@ -152,11 +154,16 @@ func BuildEntrypointHandlers(conf *config.Gateway, httpConf *HTTPConfig, log *lo
// map backends to endpoint
endpoints := make(map[string]bool)
for e, endpoint := range server.API.Endpoint {
pattern, _, err := createPattern(utils.JoinPath(server.API.BasePath, endpoint.Pattern))
if err != nil {
log.Fatal(err)
}

conf.Server[idx].API.Endpoint[e].Server = server // assign parent
if endpoints[endpoint.Pattern] {
log.Fatal("Duplicate endpoint: ", endpoint.Pattern)
if endpoints[pattern] {
log.Fatal("Duplicate endpoint: ", pattern)
}
endpoints[endpoint.Pattern] = true
endpoints[pattern] = true

// setACHandlerFn individual wrap for access_control configuration per endpoint
setACHandlerFn := func(protectedBackend backendDefinition) {
Expand Down Expand Up @@ -249,13 +256,8 @@ func BuildEntrypointHandlers(conf *config.Gateway, httpConf *HTTPConfig, log *lo
}

setACHandlerFn(backendDefinition{conf: inlineConf, handler: inlineBackend})
}

for _, endpoint := range server.API.Endpoint {
mux.API = mux.API.Add(
utils.JoinPath(server.API.BasePath, endpoint.Pattern),
entryHandler.api[endpoint],
)
mux.API = mux.API.Add(pattern, entryHandler.api[endpoint])
}

if err = configureHandlers(httpConf, server, mux, handlers); err != nil {
Expand Down Expand Up @@ -471,3 +473,39 @@ func getAbsPath(file string, log *logrus.Entry) string {

return path.Join(wd, file)
}

func createPattern(path string) (string, []*request.PathParam, error) {
var params []*request.PathParam
var pattern string

if !strings.HasPrefix(path, "/") {
return "", nil, e.New(fmt.Sprintf("Invalid path %q given", path))
}

for i, part := range strings.Split(path, "/") {
sub := part

if strings.HasPrefix(part, "{") || strings.HasSuffix(part, "}") {
if !strings.HasPrefix(part, "{") || !strings.HasSuffix(part, "}") || part == "{}" {
return "", nil, e.New(fmt.Sprintf("Invalid path sequence %q given", part))
}

sub = "{}"

name := part[1 : len(part)-1]
//if !seetie.validKey.MatchString(name) {
// return "", nil, e.New(fmt.Sprintf("Invalid path param name %q given", name))
//}

params = append(params, &request.PathParam{Name: name, Position: i})
}

if !strings.HasSuffix(pattern, "/") {
pattern += "/"
}

pattern += sub
}

return pattern, params, nil
}
40 changes: 40 additions & 0 deletions config/runtime/handler_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package runtime

import (
"fmt"
"reflect"
"testing"

"github.com/avenga/couper/config/request"
)

func TestHandler_CreatePattern(t *testing.T) {
type testCase struct {
input string
expPattern string
expParams []*request.PathParam
expErr string
}

for i, tc := range []testCase{
{"a", "", nil, ""},
{"/", "/", nil, ""},
{"/{", "", nil, ""},
{"/}", "", nil, ""},
{"/{}", "", nil, ""},
{"/abc", "/abc", nil, ""},
{"/abc/{x}", "/abc/{}", []*request.PathParam{{Name: "x", Position: 2}}, ""},
{"/abc/{x}/", "/abc/{}/", []*request.PathParam{{Name: "x", Position: 2}}, ""},
{"/abc/{x}/{y}/", "/abc/{}/{}/", []*request.PathParam{{Name: "x", Position: 2}, {Name: "y", Position: 3}}, ""},
} {
gotPattern, gotParams, _ := createPattern(tc.input)

if tc.expPattern != gotPattern {
t.Errorf("%d: Expected pattern %q, got %q", i, tc.expPattern, gotPattern)
}

if !reflect.DeepEqual(tc.expParams, gotParams) {
t.Errorf("%d: Expected params %q, got %q", i, fmt.Sprintf("%#v", tc.expParams), fmt.Sprintf("%#v", gotParams))
}
}
}
5 changes: 5 additions & 0 deletions config/runtime/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ func NewRoute(pattern string, handler http.Handler) (*Route, error) {
matchPattern = matchPattern + "$"
}

pathParams := strings.Split(pattern, "/{}")
if len(pathParams) > 1 {
// TODO: /{} => /[^/]+ & sortLen-=2
}

matchPattern = strings.ReplaceAll(matchPattern, wildcardSearch, wildcardReplacement)
matcher := regexp.MustCompile(matchPattern)
return &Route{
Expand Down
38 changes: 25 additions & 13 deletions eval/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/url"
"os"
"strconv"
"strings"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
Expand Down Expand Up @@ -49,7 +50,7 @@ func NewENVContext(src []byte) *hcl.EvalContext {
}
}

func NewHTTPContext(baseCtx *hcl.EvalContext, bufOpt BufferOption, req, bereq *http.Request, beresp *http.Response) *hcl.EvalContext {
func NewHTTPContext(baseCtx *hcl.EvalContext, bufOpt BufferOption, req, bereq *http.Request, beresp *http.Response, pathParams []*request.PathParam) *hcl.EvalContext {
if req == nil {
return baseCtx
}
Expand All @@ -66,23 +67,34 @@ func NewHTTPContext(baseCtx *hcl.EvalContext, bufOpt BufferOption, req, bereq *h
id = uid
}

pathFields := make(map[string]cty.Value)
if len(pathParams) > 0 {
pathParts := strings.Split(req.URL.Path, "/")

for _, param := range pathParams {
pathFields[param.Name] = cty.StringVal(pathParts[param.Position])
}
}

evalCtx.Variables[ClientRequest] = cty.ObjectVal(reqCtxMap.Merge(ContextMap{
ID: cty.StringVal(id),
Method: cty.StringVal(req.Method),
Path: cty.StringVal(req.URL.Path),
URL: cty.StringVal(newRawURL(req.URL).String()),
Query: seetie.ValuesMapToValue(req.URL.Query()),
Post: seetie.ValuesMapToValue(parseForm(req).PostForm),
JsonBody: seetie.MapToValue(parseReqJSON(req)),
ID: cty.StringVal(id),
JsonBody: seetie.MapToValue(parseReqJSON(req)),
Method: cty.StringVal(req.Method),
Path: cty.StringVal(req.URL.Path),
PathParam: cty.ObjectVal(pathFields),
Post: seetie.ValuesMapToValue(parseForm(req).PostForm),
Query: seetie.ValuesMapToValue(req.URL.Query()),
URL: cty.StringVal(newRawURL(req.URL).String()),
}.Merge(newVariable(httpCtx, req.Cookies(), req.Header))))

if beresp != nil {
evalCtx.Variables[BackendRequest] = cty.ObjectVal(ContextMap{
Method: cty.StringVal(bereq.Method),
Path: cty.StringVal(bereq.URL.Path),
URL: cty.StringVal(newRawURL(bereq.URL).String()),
Query: seetie.ValuesMapToValue(bereq.URL.Query()),
Post: seetie.ValuesMapToValue(parseForm(bereq).PostForm),
Method: cty.StringVal(bereq.Method),
Path: cty.StringVal(bereq.URL.Path),
PathParam: cty.ObjectVal(pathFields),
Post: seetie.ValuesMapToValue(parseForm(bereq).PostForm),
Query: seetie.ValuesMapToValue(bereq.URL.Query()),
URL: cty.StringVal(newRawURL(bereq.URL).String()),
}.Merge(newVariable(httpCtx, bereq.Cookies(), bereq.Header)))

var jsonBody map[string]interface{}
Expand Down
2 changes: 1 addition & 1 deletion eval/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func TestNewHTTPContext(t *testing.T) {
t.Fatal(err)
}

ctx := eval.NewHTTPContext(tt.baseCtx, eval.BufferRequest, req, bereq, beresp)
ctx := eval.NewHTTPContext(tt.baseCtx, eval.BufferRequest, req, bereq, beresp, nil)
ctx.Functions = nil // we are not interested in a functions test

var resultMap map[string]cty.Value
Expand Down
3 changes: 2 additions & 1 deletion eval/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ const (
Cookies = "cookies"
Endpoint = "endpoint"
Environment = "env"
HttpStatus = "status"
Headers = "headers"
HttpStatus = "status"
ID = "id"
JsonBody = "json_body"
Method = "method"
Path = "path"
PathParam = "path_param"
Post = "post"
Query = "query"
URL = "url"
Expand Down
2 changes: 1 addition & 1 deletion handler/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ func (p *Proxy) SetRoundtripContext(req *http.Request, beresp *http.Response) {
headerCtx = req.Header
}

evalCtx := eval.NewHTTPContext(p.evalContext, p.bufferOption, req, bereq, beresp)
evalCtx := eval.NewHTTPContext(p.evalContext, p.bufferOption, req, bereq, beresp, nil)

// Remove blacklisted headers after evaluation to be accessible within our context configuration.
if attrCtx == attrReqHeaders {
Expand Down

0 comments on commit fc8437a

Please sign in to comment.