Skip to content

Commit 6d23620

Browse files
authored
documentation: on server, use FromIncomingContext for retrieving context and SetHeader, SetTrailer to send metadata to client (#7238)
1 parent 7e5898e commit 6d23620

File tree

3 files changed

+92
-27
lines changed

3 files changed

+92
-27
lines changed

Documentation/grpc-metadata.md

+38-24
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ Four kinds of service method:
1212
- [Client streaming RPC](https://grpc.io/docs/guides/concepts.html#client-streaming-rpc)
1313
- [Bidirectional streaming RPC](https://grpc.io/docs/guides/concepts.html#bidirectional-streaming-rpc)
1414

15-
And concept of [metadata](https://grpc.io/docs/guides/concepts.html#metadata).
15+
And concept of [metadata].
1616

1717
## Constructing metadata
1818

19-
A metadata can be created using package [metadata](https://godoc.org/google.golang.org/grpc/metadata).
19+
A metadata can be created using package [metadata].
2020
The type MD is actually a map from string to a list of strings:
2121

2222
```go
@@ -64,20 +64,10 @@ md := metadata.Pairs(
6464
)
6565
```
6666

67-
## Retrieving metadata from context
68-
69-
Metadata can be retrieved from context using `FromIncomingContext`:
70-
71-
```go
72-
func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) {
73-
md, ok := metadata.FromIncomingContext(ctx)
74-
// do something with metadata
75-
}
76-
```
77-
7867
## Sending and receiving metadata - client side
7968

80-
Client side metadata sending and receiving examples are available [here](../examples/features/metadata/client/main.go).
69+
Client side metadata sending and receiving examples are available
70+
[here](../examples/features/metadata/client/main.go).
8171

8272
### Sending metadata
8373

@@ -127,7 +117,8 @@ Metadata that a client can receive includes header and trailer.
127117

128118
#### Unary call
129119

130-
Header and trailer sent along with a unary call can be retrieved using function [Header](https://godoc.org/google.golang.org/grpc#Header) and [Trailer](https://godoc.org/google.golang.org/grpc#Trailer) in [CallOption](https://godoc.org/google.golang.org/grpc#CallOption):
120+
Header and trailer sent along with a unary call can be retrieved using function
121+
[Header] and [Trailer] in [CallOption]:
131122

132123
```go
133124
var header, trailer metadata.MD // variable to store header and trailer
@@ -149,7 +140,8 @@ For streaming calls including:
149140
- Client streaming RPC
150141
- Bidirectional streaming RPC
151142

152-
Header and trailer can be retrieved from the returned stream using function `Header` and `Trailer` in interface [ClientStream](https://godoc.org/google.golang.org/grpc#ClientStream):
143+
Header and trailer can be retrieved from the returned stream using function
144+
`Header` and `Trailer` in interface [ClientStream]:
153145

154146
```go
155147
stream, err := client.SomeStreamingRPC(ctx)
@@ -164,11 +156,13 @@ trailer := stream.Trailer()
164156

165157
## Sending and receiving metadata - server side
166158

167-
Server side metadata sending and receiving examples are available [here](../examples/features/metadata/server/main.go).
159+
Server side metadata sending and receiving examples are available
160+
[here](../examples/features/metadata/server/main.go).
168161

169162
### Receiving metadata
170163

171-
To read metadata sent by the client, the server needs to retrieve it from RPC context.
164+
To read metadata sent by the client, the server needs to retrieve it from RPC
165+
context using [FromIncomingContext].
172166
If it is a unary call, the RPC handler's context can be used.
173167
For streaming calls, the server needs to get context from the stream.
174168

@@ -194,15 +188,16 @@ func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) erro
194188

195189
#### Unary call
196190

197-
To send header and trailer to client in unary call, the server can call [SendHeader](https://godoc.org/google.golang.org/grpc#SendHeader) and [SetTrailer](https://godoc.org/google.golang.org/grpc#SetTrailer) functions in module [grpc](https://godoc.org/google.golang.org/grpc).
191+
To send header and trailer to client in unary call, the server can call
192+
[SetHeader] and [SetTrailer] functions in module [grpc].
198193
These two functions take a context as the first parameter.
199194
It should be the RPC handler's context or one derived from it:
200195

201196
```go
202197
func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
203-
// create and send header
198+
// create and set header
204199
header := metadata.Pairs("header-key", "val")
205-
grpc.SendHeader(ctx, header)
200+
grpc.SetHeader(ctx, header)
206201
// create and set trailer
207202
trailer := metadata.Pairs("trailer-key", "val")
208203
grpc.SetTrailer(ctx, trailer)
@@ -211,20 +206,39 @@ func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someRespo
211206

212207
#### Streaming call
213208

214-
For streaming calls, header and trailer can be sent using function `SendHeader` and `SetTrailer` in interface [ServerStream](https://godoc.org/google.golang.org/grpc#ServerStream):
209+
For streaming calls, header and trailer can be sent using function
210+
[SetHeader] and [SetTrailer] in interface [ServerStream]:
215211

216212
```go
217213
func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error {
218-
// create and send header
214+
// create and set header
219215
header := metadata.Pairs("header-key", "val")
220-
stream.SendHeader(header)
216+
stream.SetHeader(header)
221217
// create and set trailer
222218
trailer := metadata.Pairs("trailer-key", "val")
223219
stream.SetTrailer(trailer)
224220
}
225221
```
226222

223+
**Important**
224+
225+
Do not use
226+
[FromOutgoingContext] on the server to write metadata to be sent to the client.
227+
[FromOutgoingContext] is for client-side use only.
228+
227229
## Updating metadata from a server interceptor
228230

229231
An example for updating metadata from a server interceptor is
230232
available [here](../examples/features/metadata_interceptor/server/main.go).
233+
234+
[FromIncomingContext]: <https://pkg.go.dev/google.golang.org/grpc/metadata#FromIncomingContext>
235+
[SetHeader]: <https://godoc.org/google.golang.org/grpc#SetHeader>
236+
[SetTrailer]: https://godoc.org/google.golang.org/grpc#SetTrailer
237+
[FromOutgoingContext]: https://pkg.go.dev/google.golang.org/grpc/metadata#FromOutgoingContext
238+
[ServerStream]: https://godoc.org/google.golang.org/grpc#ServerStream
239+
[grpc]: https://godoc.org/google.golang.org/grpc
240+
[ClientStream]: https://godoc.org/google.golang.org/grpc#ClientStream
241+
[Header]: https://godoc.org/google.golang.org/grpc#Header
242+
[Trailer]: https://godoc.org/google.golang.org/grpc#Trailer
243+
[CallOption]: https://godoc.org/google.golang.org/grpc#CallOption
244+
[metadata]: https://godoc.org/google.golang.org/grpc/metadata

examples/features/metadata_interceptor/client/main.go

+28-1
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,30 @@ import (
2929

3030
"google.golang.org/grpc"
3131
"google.golang.org/grpc/credentials/insecure"
32+
"google.golang.org/grpc/metadata"
3233

3334
pb "google.golang.org/grpc/examples/features/proto/echo"
3435
)
3536

3637
var addr = flag.String("addr", "localhost:50051", "the address to connect to")
3738

3839
func callUnaryEcho(ctx context.Context, client pb.EchoClient) {
39-
resp, err := client.UnaryEcho(ctx, &pb.EchoRequest{Message: "hello world"})
40+
var header, trailer metadata.MD
41+
resp, err := client.UnaryEcho(ctx, &pb.EchoRequest{Message: "hello world"}, grpc.Header(&header), grpc.Trailer(&trailer))
4042
if err != nil {
4143
log.Fatalf("UnaryEcho: %v", err)
4244
}
4345
fmt.Println("UnaryEcho: ", resp.Message)
46+
47+
fmt.Println("Received headers:")
48+
for k, v := range header {
49+
fmt.Printf("%s: %v\n", k, v)
50+
}
51+
52+
fmt.Println("Received trailers:")
53+
for k, v := range trailer {
54+
fmt.Printf("%s: %v\n", k, v)
55+
}
4456
}
4557

4658
func callBidiStreamingEcho(ctx context.Context, client pb.EchoClient) {
@@ -64,6 +76,21 @@ func callBidiStreamingEcho(ctx context.Context, client pb.EchoClient) {
6476
}
6577
fmt.Println("BidiStreaming Echo: ", resp.Message)
6678
}
79+
80+
header, err := c.Header()
81+
if err != nil {
82+
log.Fatalf("Receiving headers: %v", err)
83+
}
84+
fmt.Println("Received headers:")
85+
for k, v := range header {
86+
fmt.Printf("%s: %v\n", k, v)
87+
}
88+
89+
trailer := c.Trailer()
90+
fmt.Println("Received tailers:")
91+
for k, v := range trailer {
92+
fmt.Printf("%s: %v\n", k, v)
93+
}
6794
}
6895

6996
func main() {

examples/features/metadata_interceptor/server/main.go

+26-2
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,22 @@ func unaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo,
4949
return nil, errMissingMetadata
5050
}
5151

52+
// Create and set metadata from interceptor to server.
5253
md.Append("key1", "value1")
5354
ctx = metadata.NewIncomingContext(ctx, md)
5455

55-
return handler(ctx, req)
56+
// Call the handler to complete the normal execution of the RPC.
57+
resp, err := handler(ctx, req)
58+
59+
// Create and set header metadata from interceptor to client.
60+
header := metadata.Pairs("header-key", "val")
61+
grpc.SetHeader(ctx, header)
62+
63+
// Create and set trailer metadata from interceptor to client.
64+
trailer := metadata.Pairs("trailer-key", "val")
65+
grpc.SetTrailer(ctx, trailer)
66+
67+
return resp, err
5668
}
5769

5870
func (s *server) UnaryEcho(ctx context.Context, in *pb.EchoRequest) (*pb.EchoResponse, error) {
@@ -89,10 +101,22 @@ func streamInterceptor(srv any, ss grpc.ServerStream, info *grpc.StreamServerInf
89101
return errMissingMetadata
90102
}
91103

104+
// Create and set metadata from interceptor to server.
92105
md.Append("key1", "value1")
93106
ctx := metadata.NewIncomingContext(ss.Context(), md)
94107

95-
return handler(srv, &wrappedStream{ss, ctx})
108+
// Call the handler to complete the normal execution of the RPC.
109+
err := handler(srv, &wrappedStream{ss, ctx})
110+
111+
// Create and set header metadata from interceptor to client.
112+
header := metadata.Pairs("header-key", "val")
113+
ss.SetHeader(header)
114+
115+
// Create and set trailer metadata from interceptor to client.
116+
trailer := metadata.Pairs("trailer-key", "val")
117+
ss.SetTrailer(trailer)
118+
119+
return err
96120
}
97121

98122
func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error {

0 commit comments

Comments
 (0)