Skip to content

Commit 17b6bbc

Browse files
author
Michal Witkowski
committed
add support for x-forwaded-for and x-forwarded-host
1 parent ab4f1c2 commit 17b6bbc

File tree

2 files changed

+67
-9
lines changed

2 files changed

+67
-9
lines changed

runtime/context.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ import (
44
"net/http"
55
"strings"
66

7+
"net"
8+
79
"golang.org/x/net/context"
810
"google.golang.org/grpc/metadata"
911
)
1012

1113
const metadataHeaderPrefix = "Grpc-Metadata-"
1214
const metadataTrailerPrefix = "Grpc-Trailer-"
15+
const xForwardedFor = "X-Forwarded-For"
16+
const xForwardedHost = "X-Forwarded-Host"
1317

1418
/*
1519
AnnotateContext adds context information such as metadata from the request.
@@ -22,14 +26,27 @@ func AnnotateContext(ctx context.Context, req *http.Request) context.Context {
2226
for key, vals := range req.Header {
2327
for _, val := range vals {
2428
if key == "Authorization" {
25-
pairs = append(pairs, key, val)
29+
pairs = append(pairs, "authorization", val)
2630
continue
2731
}
2832
if strings.HasPrefix(key, metadataHeaderPrefix) {
2933
pairs = append(pairs, key[len(metadataHeaderPrefix):], val)
3034
}
3135
}
3236
}
37+
if host := req.Header.Get(xForwardedHost); host != "" {
38+
pairs = append(pairs, strings.ToLower(xForwardedHost), host)
39+
} else if req.Host != "" {
40+
pairs = append(pairs, strings.ToLower(xForwardedHost), req.Host)
41+
}
42+
remoteIp, _, err := net.SplitHostPort(req.RemoteAddr)
43+
if err == nil {
44+
if req.Header.Get(xForwardedFor) == "" {
45+
pairs = append(pairs, strings.ToLower(xForwardedFor), remoteIp)
46+
} else {
47+
pairs = append(pairs, strings.ToLower(xForwardedFor), req.Header.Get(xForwardedFor)+", "+remoteIp)
48+
}
49+
}
3350

3451
if len(pairs) == 0 {
3552
return ctx

runtime/context_test.go

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,45 @@ import (
1010
"google.golang.org/grpc/metadata"
1111
)
1212

13-
func TestAnnotateContext(t *testing.T) {
13+
const (
14+
emptyForwardMetaCount = 2
15+
)
16+
17+
func TestAnnotateContext_WorksWithEmpty(t *testing.T) {
1418
ctx := context.Background()
1519

16-
request, err := http.NewRequest("GET", "http://localhost", nil)
20+
request, err := http.NewRequest("GET", "http://www.example.com", nil)
1721
if err != nil {
18-
t.Fatalf("http.NewRequest(%q, %q, nil) failed with %v; want success", "GET", "http://localhost", err)
22+
t.Fatalf("http.NewRequest(%q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err)
1923
}
24+
// Make sure we set a remote.
25+
request.RemoteAddr = "192.168.0.1:12345"
26+
2027
request.Header.Add("Some-Irrelevant-Header", "some value")
2128
annotated := runtime.AnnotateContext(ctx, request)
22-
if annotated != ctx {
23-
t.Errorf("AnnotateContext(ctx, request) = %v; want %v", annotated, ctx)
29+
md, ok := metadata.FromContext(annotated)
30+
if !ok || len(md) != emptyForwardMetaCount {
31+
t.Errorf("Expected 2 metadata items in context; got %v", md)
2432
}
33+
}
2534

35+
func TestAnnotateContext_ForwardsGrpcMetadata(t *testing.T) {
36+
ctx := context.Background()
37+
request, err := http.NewRequest("GET", "http://www.example.com", nil)
38+
if err != nil {
39+
t.Fatalf("http.NewRequest(%q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err)
40+
}
41+
request.RemoteAddr = "192.168.0.1:12345"
42+
43+
request.Header.Add("Some-Irrelevant-Header", "some value")
2644
request.Header.Add("Grpc-Metadata-FooBar", "Value1")
2745
request.Header.Add("Grpc-Metadata-Foo-BAZ", "Value2")
2846
request.Header.Add("Grpc-Metadata-foo-bAz", "Value3")
2947
request.Header.Add("Authorization", "Token 1234567890")
30-
annotated = runtime.AnnotateContext(ctx, request)
48+
annotated := runtime.AnnotateContext(ctx, request)
3149
md, ok := metadata.FromContext(annotated)
32-
if !ok || len(md) != 3 {
33-
t.Errorf("Expected 3 metadata items in context; got %v", md)
50+
if !ok || len(md) != emptyForwardMetaCount+3 {
51+
t.Errorf("Expected 5 metadata items in context; got %v", md)
3452
}
3553
if got, want := md["foobar"], []string{"Value1"}; !reflect.DeepEqual(got, want) {
3654
t.Errorf(`md["foobar"] = %q; want %q`, got, want)
@@ -42,3 +60,26 @@ func TestAnnotateContext(t *testing.T) {
4260
t.Errorf(`md["authorization"] = %q want %q`, got, want)
4361
}
4462
}
63+
64+
func TestAnnotateContext_XForwardedFor(t *testing.T) {
65+
ctx := context.Background()
66+
request, err := http.NewRequest("GET", "http://bar.foo.example.com", nil)
67+
if err != nil {
68+
t.Fatalf("http.NewRequest(%q, %q, nil) failed with %v; want success", "GET", "http://bar.foo.example.com", err)
69+
}
70+
request.Header.Add("X-Forwarded-For", "192.168.0.100") // client
71+
request.RemoteAddr = "8.8.8.8:12345" // proxy
72+
73+
annotated := runtime.AnnotateContext(ctx, request)
74+
md, ok := metadata.FromContext(annotated)
75+
if !ok || len(md) != emptyForwardMetaCount {
76+
t.Errorf("Expected 2 metadata items in context; got %v", md)
77+
}
78+
if got, want := md["x-forwarded-host"], []string{"bar.foo.example.com"}; !reflect.DeepEqual(got, want) {
79+
t.Errorf("md[\"host\"] = %v; want %v", got, want)
80+
}
81+
// Note: it must be in order client, proxy1, proxy2
82+
if got, want := md["x-forwarded-for"], []string{"192.168.0.100, 8.8.8.8"}; !reflect.DeepEqual(got, want) {
83+
t.Errorf("md[\"x-forwarded-for\"] = %v want %v", got, want)
84+
}
85+
}

0 commit comments

Comments
 (0)