-
Notifications
You must be signed in to change notification settings - Fork 563
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #690 from luraproject/backend_level_query_strings
add backend level query strings filtering
- Loading branch information
Showing
7 changed files
with
305 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package proxy | ||
|
||
import ( | ||
"context" | ||
"net/url" | ||
|
||
"github.com/luraproject/lura/v2/config" | ||
"github.com/luraproject/lura/v2/logging" | ||
) | ||
|
||
// NewFilterQueryStringsMiddleware returns a middleware with or without a header filtering | ||
// proxy wrapping the next element (depending on the configuration). | ||
func NewFilterQueryStringsMiddleware(logger logging.Logger, remote *config.Backend) Middleware { | ||
if len(remote.QueryStringsToPass) == 0 { | ||
return emptyMiddlewareFallback(logger) | ||
} | ||
|
||
return func(next ...Proxy) Proxy { | ||
if len(next) > 1 { | ||
logger.Fatal("too many proxies for this proxy middleware: NewFilterQueryStringsMiddleware only accepts 1 proxy, got %d", len(next)) | ||
return nil | ||
} | ||
nextProxy := next[0] | ||
return func(ctx context.Context, request *Request) (*Response, error) { | ||
if len(request.Query) == 0 { | ||
return nextProxy(ctx, request) | ||
} | ||
numQueryStringsToPass := 0 | ||
for _, v := range remote.QueryStringsToPass { | ||
if _, ok := request.Query[v]; ok { | ||
numQueryStringsToPass++ | ||
} | ||
} | ||
if numQueryStringsToPass == len(request.Query) { | ||
// all the query strings should pass, no need to clone the headers | ||
return nextProxy(ctx, request) | ||
} | ||
// ATTENTION: this is not a clone of query strings! | ||
// this just filters the query strings we do not want to send: | ||
// issues and race conditions could happen the same way as when we | ||
// do not filter the headers. This is a design decission, and if we | ||
// want to clone the query string values (because of write modifications), | ||
// that should be done at an upper level (so the approach is the same | ||
// for non filtered parallel requests). | ||
newQueryStrings := make(url.Values, numQueryStringsToPass) | ||
for _, v := range remote.QueryStringsToPass { | ||
if values, ok := request.Query[v]; ok { | ||
newQueryStrings[v] = values | ||
} | ||
} | ||
return nextProxy(ctx, &Request{ | ||
Method: request.Method, | ||
URL: request.URL, | ||
Query: newQueryStrings, | ||
Path: request.Path, | ||
Body: request.Body, | ||
Params: request.Params, | ||
Headers: request.Headers, | ||
}) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package proxy | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/luraproject/lura/v2/config" | ||
"github.com/luraproject/lura/v2/logging" | ||
) | ||
|
||
func TestNewFilterQueryStringsMiddleware(t *testing.T) { | ||
mw := NewFilterQueryStringsMiddleware( | ||
logging.NoOp, | ||
&config.Backend{ | ||
QueryStringsToPass: []string{ | ||
"oak", | ||
"cedar", | ||
}, | ||
}, | ||
) | ||
|
||
var receivedReq *Request | ||
prxy := mw(func(ctx context.Context, req *Request) (*Response, error) { | ||
receivedReq = req | ||
return nil, nil | ||
}) | ||
|
||
sentReq := &Request{ | ||
Body: nil, | ||
Params: map[string]string{}, | ||
Query: map[string][]string{ | ||
"oak": []string{"acorn", "evergreen"}, | ||
"maple": []string{"tree", "shrub"}, | ||
"cedar": []string{"mediterranean", "himalayas"}, | ||
"willow": []string{"350"}, | ||
}, | ||
} | ||
|
||
prxy(context.Background(), sentReq) | ||
|
||
if receivedReq == sentReq { | ||
t.Errorf("request should be different") | ||
return | ||
} | ||
|
||
oak, ok := receivedReq.Query["oak"] | ||
if !ok { | ||
t.Errorf("missing 'oak'") | ||
return | ||
} | ||
if len(oak) != len(sentReq.Query["oak"]) { | ||
t.Errorf("want len(oak): %d, got %d", | ||
len(sentReq.Query["oak"]), len(oak)) | ||
return | ||
} | ||
|
||
for idx, expected := range sentReq.Query["oak"] { | ||
if expected != oak[idx] { | ||
t.Errorf("want oak[%d] = %s, got %s", | ||
idx, expected, oak[idx]) | ||
return | ||
} | ||
} | ||
|
||
if _, ok := receivedReq.Query["cedar"]; !ok { | ||
t.Errorf("missing 'cedar'") | ||
return | ||
} | ||
|
||
if _, ok := receivedReq.Query["mapple"]; ok { | ||
t.Errorf("should not be there: 'mapple'") | ||
return | ||
} | ||
|
||
if _, ok := receivedReq.Query["willow"]; ok { | ||
t.Errorf("should not be there: 'willow'") | ||
return | ||
} | ||
|
||
// check that when query strings are all the expected, no need to copy | ||
sentReq = &Request{ | ||
Body: nil, | ||
Params: map[string]string{}, | ||
Query: map[string][]string{ | ||
"oak": []string{"acorn", "evergreen"}, | ||
"cedar": []string{"mediterranean", "himalayas"}, | ||
}, | ||
} | ||
|
||
prxy(context.Background(), sentReq) | ||
|
||
if receivedReq != sentReq { | ||
t.Errorf("request should be the same, no modification of query string expected") | ||
return | ||
} | ||
} | ||
|
||
func TestFilterQueryStringsBlockAll(t *testing.T) { | ||
// In order to block all the query strings, we must only let pass | ||
// the 'empty' string "" | ||
mw := NewFilterQueryStringsMiddleware( | ||
logging.NoOp, | ||
&config.Backend{ | ||
QueryStringsToPass: []string{""}, | ||
}, | ||
) | ||
|
||
var receivedReq *Request | ||
prxy := mw(func(ctx context.Context, req *Request) (*Response, error) { | ||
receivedReq = req | ||
return nil, nil | ||
}) | ||
|
||
sentReq := &Request{ | ||
Body: nil, | ||
Params: map[string]string{}, | ||
Query: map[string][]string{ | ||
"oak": []string{"acorn", "evergreen"}, | ||
"maple": []string{"tree", "shrub"}, | ||
}, | ||
} | ||
|
||
prxy(context.Background(), sentReq) | ||
|
||
if receivedReq == sentReq { | ||
t.Errorf("request should be different") | ||
return | ||
} | ||
|
||
if len(receivedReq.Query) != 0 { | ||
t.Errorf("should have blocked all query strings") | ||
return | ||
} | ||
} | ||
|
||
func TestFilterQueryStringsAllowAll(t *testing.T) { | ||
// Empty backend query strings to passa everything | ||
mw := NewFilterQueryStringsMiddleware( | ||
logging.NoOp, | ||
&config.Backend{ | ||
QueryStringsToPass: []string{}, | ||
}, | ||
) | ||
|
||
var receivedReq *Request | ||
prxy := mw(func(ctx context.Context, req *Request) (*Response, error) { | ||
receivedReq = req | ||
return nil, nil | ||
}) | ||
|
||
sentReq := &Request{ | ||
Body: nil, | ||
Params: map[string]string{}, | ||
Query: map[string][]string{ | ||
"oak": []string{"acorn", "evergreen"}, | ||
"maple": []string{"tree", "shrub"}, | ||
}, | ||
} | ||
|
||
prxy(context.Background(), sentReq) | ||
|
||
if len(receivedReq.Query) != 2 { | ||
t.Errorf("should have passed all query strings") | ||
return | ||
} | ||
} |