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 5 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_interceptor/server/main.go). | ||
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. Please wrap these lines at 80 cols width. Thanks. 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 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. Could you please update this line to look like:
Also, please ensure that this line is also wrapped to 80-cols width. Thanks. |
||
|
||
#### 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,10 +31,9 @@ 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") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Metadata interceptor example | ||
|
||
This example shows how to update metadata in RPC unary interceptor and streaming 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. How about:
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 update the document |
||
Please see | ||
[grpc-metadata.md](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) | ||
for more information. | ||
|
||
## Start the server | ||
|
||
``` | ||
go run server/main.go | ||
``` | ||
|
||
## Run the client | ||
|
||
``` | ||
go run client/main.go | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* | ||
* Copyright 2018 gRPC authors. | ||
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. Update the year on the copyright notice please. 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 that, I will update it. |
||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
// Binary client is an example client. | ||
package main | ||
|
||
import ( | ||
"context" | ||
"flag" | ||
"fmt" | ||
"io" | ||
"log" | ||
"time" | ||
|
||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/credentials/insecure" | ||
pb "google.golang.org/grpc/examples/features/proto/echo" | ||
) | ||
|
||
var addr = flag.String("addr", "localhost:50051", "the address to connect to") | ||
|
||
const message = "hello world" | ||
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: Get rid of this const and move it inline to the calls to 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. Thank you for your view |
||
|
||
func callUnaryEcho(client pb.EchoClient, message string) { | ||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||
defer cancel() | ||
resp, err := client.UnaryEcho(ctx, &pb.EchoRequest{Message: message}) | ||
if err != nil { | ||
log.Fatalf("client.UnaryEcho(_) = _, %v: ", err) | ||
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 know we do this in many (or all) examples, but I think we can print a simpler/nicer error message. Something like: log.Fatalf("UnaryEcho: %v", err) 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 |
||
} | ||
fmt.Println("UnaryEcho: ", resp.Message) | ||
} | ||
|
||
func callBidiStreamingEcho(client pb.EchoClient, message string) { | ||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||
defer cancel() | ||
|
||
c, err := client.BidirectionalStreamingEcho(ctx) | ||
if err != nil { | ||
return | ||
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.
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 |
||
} | ||
|
||
if err := c.Send(&pb.EchoRequest{Message: message}); err != nil { | ||
log.Fatalf("failed to send request due to error: %v", err) | ||
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. log.Fatalf("Sending echo request: %v", err) 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 |
||
} | ||
c.CloseSend() | ||
|
||
for { | ||
resp, err := c.Recv() | ||
if err == io.EOF { | ||
break | ||
} | ||
if err != nil { | ||
log.Fatalf("failed to receive response due to error: %v", err) | ||
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. log.Fatalf("Receiving echo response: %v", err) 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 |
||
} | ||
fmt.Println("BidiStreaming Echo: ", resp.Message) | ||
} | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
// Set up a connection to the server. | ||
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: remove this comment as it is saying the obvious. 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 |
||
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials())) | ||
if err != nil { | ||
log.Fatalf("did not connect: %v", err) | ||
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. log.Fatalf("grpc.Dial(%q): %v", *addr, err) 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 |
||
} | ||
defer conn.Close() | ||
|
||
ec := pb.NewEchoClient(conn) | ||
|
||
callUnaryEcho(ec, message) | ||
time.Sleep(1 * time.Second) | ||
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. Why do we need this sleep? 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 will remove this sleep. |
||
|
||
callBidiStreamingEcho(ec, message) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* | ||
* | ||
* Copyright 2018 gRPC authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
// Binary server is an example server. | ||
package main | ||
|
||
import ( | ||
"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. Seems like we need some reordering of the imports here to pass goimports examples/features/metadata_interceptor/server/main.go to see how the file should look like 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. @arvindbr8 , thank you very much for your response, I have updated the reordering of the imports through |
||
"flag" | ||
"fmt" | ||
"io" | ||
"log" | ||
"math/rand" | ||
"net" | ||
"time" | ||
|
||
"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" | ||
) | ||
|
||
var port = flag.Int("port", 50051, "the port to serve on") | ||
|
||
var errMissingMetadata = status.Errorf(codes.InvalidArgument, "missing metadata") | ||
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. s/missing metadata/no incoming metadata in rpc 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. Got it, I will fix it |
||
|
||
type server struct { | ||
pb.UnimplementedEchoServer | ||
} | ||
|
||
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) | ||
} | ||
|
||
func (s *server) UnaryEcho(ctx context.Context, in *pb.EchoRequest) (*pb.EchoResponse, error) { | ||
fmt.Printf("--- UnaryEcho ---\n") | ||
|
||
md, ok := metadata.FromIncomingContext(ctx) | ||
if !ok { | ||
return nil, status.Errorf(codes.DataLoss, "UnaryEcho: failed to get metadata") | ||
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. Use 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 |
||
} | ||
|
||
// Read metadata from 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. Change this to 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 |
||
if v, ok := md["key1"]; ok { | ||
fmt.Printf("key1 from metadata: \n") | ||
for i, e := range v { | ||
fmt.Printf(" %d. %s\n", i, e) | ||
} | ||
} | ||
|
||
fmt.Printf("request received: %v, sending echo\n", in) | ||
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. Please remove this print. Thanks. Also, get rid of unnecessary newlines. 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. |
||
|
||
return &pb.EchoResponse{Message: in.Message}, nil | ||
} | ||
|
||
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") | ||
|
||
md, ok := metadata.FromIncomingContext(stream.Context()) | ||
if !ok { | ||
return status.Errorf(codes.DataLoss, "BidirectionalStreamingEcho: failed to get metadata") | ||
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. Same comment here about the status code and the error message. 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. |
||
} | ||
|
||
// Read metadata from interceptor. | ||
if v, ok := md["key1"]; ok { | ||
fmt.Printf("key1 from metadata: \n") | ||
for i, e := range v { | ||
fmt.Printf(" %d. %s\n", i, e) | ||
} | ||
} | ||
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. Same comment here like the one in the unary handler. 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. |
||
|
||
// Read requests and send responses. | ||
for { | ||
in, err := stream.Recv() | ||
if err == io.EOF { | ||
return nil | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Printf("request received %v, sending echo\n", in) | ||
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. Remove this print please. 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. |
||
if err := stream.Send(&pb.EchoResponse{Message: in.Message}); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
rand.Seed(time.Now().UnixNano()) | ||
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. This seems to be unused. Could you please remove 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. Got it, I will fix it. |
||
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) | ||
if err != nil { | ||
log.Fatalf("failed to listen: %v", err) | ||
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. log.Fatalf("net.Listen() failed: %v", err) 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. |
||
} | ||
fmt.Printf("server listening at %v\n", lis.Addr()) | ||
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. s/server/Server/ 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. |
||
|
||
s := grpc.NewServer( | ||
grpc.UnaryInterceptor(unaryInterceptor), | ||
grpc.StreamInterceptor(streamInterceptor)) | ||
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. Please make this a single line. See: https://google.github.io/styleguide/go/guide#line-length |
||
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