Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide customization to allow passing X-Geo-* headers into gRPC metadata #201

Merged
merged 5 commits into from Oct 20, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 39 additions & 2 deletions gateway/header.go
Expand Up @@ -114,16 +114,53 @@ func handleForwardResponseTrailer(w http.ResponseWriter, md runtime.ServerMetada
}
}

// AtlasDefaultHeaderMatcher func used to add all headers used by atlas-app-toolkit
// This function also passes through all the headers that runtime.DefaultHeaderMatcher handles.
// AtlasDefaultHeaderMatcher can be used as a Incoming/Outgoing header matcher.
func AtlasDefaultHeaderMatcher() func(string) (string, bool) {
//Put headers only in lower case
allow := map[string]struct{}{
//X-Geo-* headers are set of geo metadata from MaxMind DB injected on ingress nginx
"x-geo-org": struct{}{},
"x-geo-country-code": struct{}{},
"x-geo-country-name": struct{}{},
"x-geo-region-code": struct{}{},
"x-geo-region-name": struct{}{},
"x-geo-city-name": struct{}{},
"x-geo-postal-code": struct{}{},
"x-geo-latitude": struct{}{},
"x-geo-longitude": struct{}{},
//request id header contains unique identifier for request
"request-id": struct{}{},
//Tracing headers
"x-b3-traceid": struct{}{},
"x-b3-parentspanid": struct{}{},
"x-b3-spanid": struct{}{},
"x-b3-sampled": struct{}{},
}

return func(h string) (string, bool) {
if key, ok := runtime.DefaultHeaderMatcher(h); ok {
return key, ok
}

_, ok := allow[strings.ToLower(h)]
return h, ok
}
}

// ExtendedDefaultHeaderMatcher func is used to add custom headers to be matched
// from incoming http requests, If this returns true the header will be added to grpc context.
// This function also passes through all the headers that runtime.DefaultHeaderMatcher handles.
// This function also passes through all the headers that AtlasDefaultHeaderMatcher handles.
func ExtendedDefaultHeaderMatcher(headerNames ...string) func(string) (string, bool) {
customHeaders := map[string]bool{}
for _, name := range headerNames {
customHeaders[strings.ToLower(name)] = true
}

atlasMatcher := AtlasDefaultHeaderMatcher()
return func(headerName string) (string, bool) {
if key, ok := runtime.DefaultHeaderMatcher(headerName); ok {
if key, ok := atlasMatcher(headerName); ok {
Comment on lines +161 to +163
Copy link
Contributor

@kd7lxl kd7lxl Oct 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than plugging the new headers in like this, I think it would be better to implement

func ChainHeaderMatcher(matchers ...runtime.HeaderMatcherFunc)  runtime.HeaderMatcherFunc

so that these custom matchers can be used both independently and together, not just together. It's the same pattern we use to implement interceptors, instead of wrapping interceptors.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It actually a good idea,i will add ChainHeaderMatcher

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I provided an example of how ChainHeaderMatcher() can simplify this code in #202

return key, ok
}
_, ok := customHeaders[strings.ToLower(headerName)]
Expand Down
100 changes: 99 additions & 1 deletion gateway/header_test.go
Expand Up @@ -97,7 +97,7 @@ func TestExtendedDefaultHeaderMatcher(t *testing.T) {
{
name: "custom headers in | without custom headers | failure",
customHeaders: []string{},
in: "Request-Id",
in: "CustomHeader",
isValid: false,
},
}
Expand All @@ -111,3 +111,101 @@ func TestExtendedDefaultHeaderMatcher(t *testing.T) {
})
}
}

func TestAtlasDefaultHeaderMatcher(t *testing.T) {
var customMatcherTests = []struct {
name string
in string
isValid bool
}{
{
name: "X-Geo-Org | success",
in: "X-Geo-Org",
isValid: true,
},
{
name: "X-Geo-Country-Code | success",
in: "X-Geo-Country-Code",
isValid: true,
},
{
name: "X-Geo-Country-Name | success",
in: "X-Geo-Country-Name",
isValid: true,
},
{
name: "X-Geo-Region-Code | success",
in: "X-Geo-Region-Code",
isValid: true,
},
{
name: "X-Geo-Region-Name | success",
in: "X-Geo-Region-Name",
isValid: true,
},
{
name: "X-Geo-City-Name | success",
in: "X-Geo-City-Name",
isValid: true,
},
{
name: "X-Geo-Postal-Code | success",
in: "X-Geo-Postal-Code",
isValid: true,
},
{
name: "X-Geo-Latitude | success",
in: "X-Geo-Latitude",
isValid: true,
},
{
name: "X-Geo-Longitude | success",
in: "X-Geo-Longitude",
isValid: true,
},
{
name: "Request-Id | success",
in: "Request-Id",
isValid: true,
},
{
name: "X-B3-TraceId | success",
in: "X-B3-TraceId",
isValid: true,
},
{
name: "X-B3-ParentSpanId | success",
in: "X-B3-ParentSpanId",
isValid: true,
},
{
name: "X-B3-SpanId | success",
in: "X-B3-SpanId",
isValid: true,
},
{
name: "X-B3-Sampled | success",
in: "X-B3-Sampled",
isValid: true,
},
{
name: "x-b3-sampled | success",
in: "x-b3-sampled",
isValid: true,
},
{
name: "Failed-Header | failure",
in: "Failed-Header",
isValid: false,
},
}
for _, tt := range customMatcherTests {
t.Run(tt.name, func(t *testing.T) {
f := AtlasDefaultHeaderMatcher()
_, ok := f(tt.in)
if ok != tt.isValid {
t.Errorf("got %v, want %v", ok, tt.isValid)
}
})
}
}
9 changes: 7 additions & 2 deletions server/server.go
Expand Up @@ -12,6 +12,7 @@ import (

"errors"

"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/infobloxopen/atlas-app-toolkit/gateway"
"github.com/infobloxopen/atlas-app-toolkit/health"
"google.golang.org/grpc"
Expand Down Expand Up @@ -135,14 +136,18 @@ func WithHealthChecks(checker health.Checker) Option {
func WithGateway(options ...gateway.Option) Option {
return func(s *Server) error {
s.registrars = append(s.registrars, func(mux *http.ServeMux) error {
_, err := gateway.NewGateway(append(options, gateway.WithMux(mux))...)
_, err := gateway.NewGateway(append(options,
gateway.WithGatewayOptions(
runtime.WithIncomingHeaderMatcher(
gateway.AtlasDefaultHeaderMatcher())),
gateway.WithMux(mux))...)
return err
})
return nil
}
}

// WithMiddlewaries add opportunity to add different middleware
// WithMiddlewares add opportunity to add different middleware
func WithMiddlewares(middleware ...Middleware) Option {
return func(s *Server) error {
s.middlewares = append(s.middlewares, middleware...)
Expand Down