I'm trying to write a unary interceptor to add a request ID to incoming requests:
func RequestIDInterceptor(ctx context.Context, req interface{},
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (val interface{}, err error) {
newCtx := ctx
newCtx = createAndSetMetadata(newCtx, metadata.FromIncomingContext, metadata.NewIncomingContext)
newCtx = createAndSetMetadata(newCtx, metadata.FromOutgoingContext, metadata.NewOutgoingContext)
md, ok := metadata.FromOutgoingContext(newCtx)
fmt.Printf("After update. Metadata: %v, Found: %t\n", md, ok)
return handler(newCtx, req)
}
func createAndSetMetadata(ctx context.Context, getter func(context.Context) (metadata.MD, bool),
setter func(context.Context, metadata.MD) context.Context) context.Context {
meta, ok := getter(ctx)
fmt.Printf("Found? %t, Metadata: %v\n", ok, meta)
if !ok {
meta = metadata.New(map[string]string{})
ctx = setter(ctx, meta)
}
meta.Set("X-Request-Id", "TEST_REQUEST_ID")
fmt.Printf("Metadata: %v\n", meta)
return ctx
}
This code works as expected when the request doesn't include metadata (RequestIDInterceptor(context.Background(), nil, nil, handler)
) but if the context includes metadata then the ID won't populated. Essentially, the output looks like this:
WITHOUT METADATA
Found? false, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
Found? false, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
After update. Metadata: map[x-request-id:[TEST_REQUEST_ID]], Found: true
WITH METADATA
Found? true, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
Found? true, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
After update. Metadata: map[], Found: true
To my understanding, the reason this is probably happening is that metadata.MD
is being passed by value rather than by reference, so updates are being discarded when createAndSetMetadata
returns. How can I fix this code?