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 12 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 |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Metadata interceptor example | ||
|
||
This example shows how to update metadata from unary and streaming interceptors on the server. | ||
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,86 @@ | ||
/* | ||
* | ||
* Copyright 2022 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 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") | ||
|
||
func callUnaryEcho(ctx context.Context, client pb.EchoClient) { | ||
resp, err := client.UnaryEcho(ctx, &pb.EchoRequest{Message: "hello world"}) | ||
if err != nil { | ||
log.Fatalf("UnaryEcho %v", err) | ||
} | ||
fmt.Println("UnaryEcho: ", resp.Message) | ||
} | ||
|
||
func callBidiStreamingEcho(ctx context.Context, client pb.EchoClient) { | ||
c, err := client.BidirectionalStreamingEcho(ctx) | ||
if err != nil { | ||
log.Fatalf("BidiStreamingEcho %v", err) | ||
} | ||
|
||
if err := c.Send(&pb.EchoRequest{Message: "hello world"}); err != nil { | ||
log.Fatalf("Sending echo request: %v", err) | ||
} | ||
c.CloseSend() | ||
|
||
for { | ||
resp, err := c.Recv() | ||
if err == io.EOF { | ||
break | ||
} | ||
if err != nil { | ||
log.Fatalf("Receiving echo response: %v", err) | ||
} | ||
fmt.Println("BidiStreaming Echo: ", resp.Message) | ||
} | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
|
||
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials())) | ||
if err != nil { | ||
log.Fatalf("grpc.Dial(%q): %v", *addr, err) | ||
} | ||
defer conn.Close() | ||
|
||
ec := pb.NewEchoClient(conn) | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||
defer cancel() | ||
|
||
callUnaryEcho(ctx, ec) | ||
|
||
callBidiStreamingEcho(ctx, ec) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/* | ||
* | ||
* Copyright 2022 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" | ||
"net" | ||
|
||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/metadata" | ||
"google.golang.org/grpc/status" | ||
|
||
pb "google.golang.org/grpc/examples/features/proto/echo" | ||
) | ||
|
||
var port = flag.Int("port", 50051, "the port to serve on") | ||
|
||
var errMissingMetadata = status.Errorf(codes.InvalidArgument, "no incoming metadata in rpc context") | ||
|
||
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.Internal, "UnaryEcho: missing incoming metadata in rpc context") | ||
} | ||
|
||
// Read and print metadata added by the 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) | ||
} | ||
} | ||
|
||
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.Internal, "BidirectionalStreamingEcho: missing incoming metadata in rpc context") | ||
} | ||
|
||
// Read and print metadata added by the 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) | ||
} | ||
} | ||
|
||
// Read requests and send responses. | ||
for { | ||
in, err := stream.Recv() | ||
if err == io.EOF { | ||
return nil | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
if err = stream.Send(&pb.EchoResponse{Message: in.Message}); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) | ||
if err != nil { | ||
log.Fatalf("net.Listen() failed: %v", err) | ||
} | ||
fmt.Printf("Server listening at %v\n", lis.Addr()) | ||
|
||
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.
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 comment
The 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
Updating metadata from a server interceptor
part ofgrpc-metadata.md
to theREADME.md
under the folderexamples/features/metadata_interceptor
? Please correct me if anything I am misunderstanding or missing.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.
I mean move everything from
#### Unary interceptor
down intoexamples/features/metadata_interceptor/README.md
.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.
@dfawley , thank you very much for your response. I have updated the document per your review comment. Could you please review it again?