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
examples: add new example to show updating metadata in interceptors #5788
Changes from 3 commits
f94a588
cbcd56a
4a1b599
1534296
a4e7c89
47ee86e
9e03c85
2107776
95fe097
a3dabd3
56d9d52
2b34898
fd94734
54af026
152132f
5108a19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -223,3 +223,49 @@ func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) erro | |
stream.SetTrailer(trailer) | ||
} | ||
``` | ||
|
||
## Updating metadata in the interceptor - server side | ||
|
||
Server side metadata updating in the interceptor examples are available [here](../examples/features/metadata/server/main.go). | ||
|
||
#### Unary interceptor | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move these sections to the new README instead, to avoid clutter here and also to put the documentation near the example that illustrates it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dfawley Thank you very much for your response. Do you mean moving the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean move everything from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dfawley , thank you very much for your response. I have updated the document per your review comment. Could you please review it again? |
||
|
||
To read and update metadata in the interceptor under unary call, the server needs to retrieve it from RPC context and update it through this RPC context. | ||
|
||
```go | ||
func SomeInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { | ||
// get the metadata from context | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Maybe replace with: // Get the incoming metadata from the RPC context, and add a new
// key-value pair to it.
md, ok := metadata.FromIncomingContext(ctx)
md.Append("key1", "value1")
// Create a context with the new metadata and pass it to handler.
ctx = metadata.NewIncomingContext(ctx, md)
return handler(ctx, req) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it, sorry for my bad statement in the comment |
||
md, ok := metadata.FromIncomingContext(ctx) | ||
|
||
// update metadata | ||
md.Append("key1", "value1") | ||
|
||
// set the metadata to context | ||
ctx = metadata.NewIncomingContext(ctx, md) | ||
``` | ||
|
||
#### Streaming interceptor | ||
|
||
Since there is no direct way to set RPC context in the stream interceptor, the `wrappedStream` could be created and override `Context()` method with owned `context`. Then pass this `wrappedStream` to the stream interceptor. | ||
|
||
```go | ||
type wrappedStream struct { | ||
grpc.ServerStream | ||
ctx context.Context | ||
} | ||
|
||
func (s *wrappedStream) Context() context.Context { | ||
return s.ctx | ||
} | ||
|
||
func streamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { | ||
// get the metadata from context | ||
md, ok := metadata.FromIncomingContext(ss.Context()) | ||
// update metadata | ||
md.Append("key1", "value1") | ||
// set the metadata to context | ||
ctx := metadata.NewIncomingContext(ss.Context(), md) | ||
|
||
return handler(srv, &wrappedStream{ss, ctx}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar comments here as the one above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it. I will fix it |
||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,14 +31,15 @@ import ( | |
|
||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/codes" | ||
pb "google.golang.org/grpc/examples/features/proto/echo" | ||
"google.golang.org/grpc/metadata" | ||
"google.golang.org/grpc/status" | ||
|
||
pb "google.golang.org/grpc/examples/features/proto/echo" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this change? Although we don't do it at all places, we try (as much as possible) to group the proto imports separately. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for the typo, it was caused by the Goland. I will fix it. |
||
) | ||
|
||
var port = flag.Int("port", 50051, "the port to serve on") | ||
|
||
var errMissingMetadata = status.Errorf(codes.InvalidArgument, "missing metadata") | ||
|
||
const ( | ||
timestampFormat = time.StampNano | ||
streamingCount = 10 | ||
|
@@ -68,6 +69,13 @@ func (s *server) UnaryEcho(ctx context.Context, in *pb.EchoRequest) (*pb.EchoRes | |
} | ||
} | ||
|
||
if v, ok := md["key1"]; ok { | ||
fmt.Printf("key1 from metadata: \n") | ||
for i, e := range v { | ||
fmt.Printf(" %d. %s\n", i, e) | ||
} | ||
} | ||
|
||
// Create and send header. | ||
header := metadata.New(map[string]string{"location": "MTV", "timestamp": time.Now().Format(timestampFormat)}) | ||
grpc.SendHeader(ctx, header) | ||
|
@@ -154,6 +162,39 @@ func (s *server) ClientStreamingEcho(stream pb.Echo_ClientStreamingEchoServer) e | |
} | ||
} | ||
|
||
func unaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { | ||
md, ok := metadata.FromIncomingContext(ctx) | ||
if !ok { | ||
return nil, errMissingMetadata | ||
} | ||
|
||
md.Append("key1", "value1") | ||
ctx = metadata.NewIncomingContext(ctx, md) | ||
|
||
return handler(ctx, req) | ||
} | ||
|
||
type wrappedStream struct { | ||
grpc.ServerStream | ||
ctx context.Context | ||
} | ||
|
||
func (s *wrappedStream) Context() context.Context { | ||
return s.ctx | ||
} | ||
|
||
func streamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { | ||
md, ok := metadata.FromIncomingContext(ss.Context()) | ||
if !ok { | ||
return errMissingMetadata | ||
} | ||
|
||
md.Append("key1", "value1") | ||
ctx := metadata.NewIncomingContext(ss.Context(), md) | ||
|
||
return handler(srv, &wrappedStream{ss, ctx}) | ||
} | ||
|
||
func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { | ||
fmt.Printf("--- BidirectionalStreamingEcho ---\n") | ||
// Create trailer in defer to record function return time. | ||
|
@@ -175,6 +216,13 @@ func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamin | |
} | ||
} | ||
|
||
if v, ok := md["key1"]; ok { | ||
fmt.Printf("key1 from metadata: \n") | ||
for i, e := range v { | ||
fmt.Printf(" %d. %s\n", i, e) | ||
} | ||
} | ||
|
||
// Create and send header. | ||
header := metadata.New(map[string]string{"location": "MTV", "timestamp": time.Now().Format(timestampFormat)}) | ||
stream.SendHeader(header) | ||
|
@@ -204,7 +252,9 @@ func main() { | |
} | ||
fmt.Printf("server listening at %v\n", lis.Addr()) | ||
|
||
s := grpc.NewServer() | ||
s := grpc.NewServer( | ||
grpc.UnaryInterceptor(unaryInterceptor), | ||
grpc.StreamInterceptor(streamInterceptor)) | ||
pb.RegisterEchoServer(s, &server{}) | ||
s.Serve(lis) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about replacing this with:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, I will fix it