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

mux: forward X- headers #1264

Merged
merged 5 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions backend/gateway/mux/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http"
"net/http/httptest"
"net/http/pprof"
"net/textproto"
"net/url"
"path"
"regexp"
Expand All @@ -28,6 +29,12 @@ import (
awsservice "github.com/lyft/clutch/backend/service/aws"
)

const (
xHeader = "X-"
xForwardedFor = "X-Forwarded-For"
xForwardedHost = "X-Forwarded-Host"
)

var apiPattern = regexp.MustCompile(`^/v\d+/`)

type assetHandler struct {
Expand Down Expand Up @@ -180,6 +187,19 @@ func customResponseForwarder(ctx context.Context, w http.ResponseWriter, resp pr
return nil
}

func customHeaderMatcher(key string) (string, bool) {
key = textproto.CanonicalMIMEHeaderKey(key)
if strings.HasPrefix(key, xHeader) {
danielhochman marked this conversation as resolved.
Show resolved Hide resolved
// exclude handling these headers as they are looked up by grpc's annotate context flow and added to the context
// metadata if they're not found
if key != xForwardedFor && key != xForwardedHost {
return runtime.MetadataPrefix + key, true
}
}
// the the default header mapping rule
return runtime.DefaultHeaderMatcher(key)
}

func customErrorHandler(ctx context.Context, mux *runtime.ServeMux, m runtime.Marshaler, w http.ResponseWriter, req *http.Request, err error) {
// TODO(maybe): once we have non-browser clients we probably want to avoid the redirect and directly return the error.
if s, ok := status.FromError(err); ok && s.Code() == codes.Unauthenticated {
Expand Down Expand Up @@ -219,6 +239,7 @@ func New(unaryInterceptors []grpc.UnaryServerInterceptor, assets http.FileSystem
UnmarshalOptions: protojson.UnmarshalOptions{},
},
),
runtime.WithIncomingHeaderMatcher(customHeaderMatcher),
)

// If there is a configured asset provider, we check to see if the service is configured before proceeding.
Expand Down
56 changes: 56 additions & 0 deletions backend/gateway/mux/mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,59 @@ func TestGetAssetProivderService(t *testing.T) {
_, err := getAssetProviderService(assetCfg)
assert.Error(t, err)
}

func TestCustomHeaderMatcher(t *testing.T) {
testCases := []struct {
key string
expectedKey string
expectedBool bool
}{
{
key: "X-Foo-Bar",
expectedKey: "grpcgateway-X-Foo-Bar",
expectedBool: true,
},
// testing that the headers get uppercased
{
key: "x-foo-bar",
expectedKey: "grpcgateway-X-Foo-Bar",
expectedBool: true,
},
// testing the default rule - isPermanentHTTPHeader group
{
key: "Cookie",
expectedKey: "grpcgateway-Cookie",
expectedBool: true,
},
// testing the default rule - Grpc-Metadata prefix
{
key: "Grpc-Metadata-Foo",
expectedKey: "Foo",
expectedBool: true,
},
// testing the prefix doesn't get applied and doesn't match default rule
{
key: xForwardedFor,
expectedKey: "",
expectedBool: false,
},
// testing the prefix doesn't get applied and doesn't match default rule
{
key: xForwardedHost,
expectedKey: "",
expectedBool: false,
},
// doesn't match custom or default rules
{
key: "Foo-Bar",
expectedKey: "",
expectedBool: false,
},
}

for _, test := range testCases {
result, ok := customHeaderMatcher(test.key)
assert.Equal(t, test.expectedKey, result)
assert.Equal(t, test.expectedBool, ok)
}
}