Skip to content
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

example: metadata #2500

Merged
merged 17 commits into from
Dec 13, 2018
6 changes: 2 additions & 4 deletions Documentation/grpc-metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeRespo

## Sending and receiving metadata - client side

[//]: # "TODO: uncomment next line after example source added"
[//]: # "Real metadata sending and receiving examples are available [here](TODO:example_dir)."
Client side metadata sending and receiving examples are available [here](../examples/features/metadata/client/main.go).

### Sending metadata

Expand Down Expand Up @@ -165,8 +164,7 @@ trailer := stream.Trailer()

## Sending and receiving metadata - server side

[//]: # "TODO: uncomment next line after example source added"
[//]: # "Real metadata sending and receiving examples are available [here](TODO:example_dir)."
Server side metadata sending and receiving examples are available [here](../examples/features/metadata/server/main.go).

### Receiving metadata

Expand Down
18 changes: 18 additions & 0 deletions examples/features/metadata/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Metadata example

menghanl marked this conversation as resolved.
Show resolved Hide resolved
This example shows how to set and read metadata in RPC headers and trailers.
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
```
306 changes: 306 additions & 0 deletions examples/features/metadata/client/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
/*
*
* 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 client is an example client.
package main

import (
"context"
"flag"
"fmt"
"io"
"log"
"time"

"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/features/proto/echo"
"google.golang.org/grpc/metadata"
)

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

const (
timestampFormat = time.StampNano // "Jan _2 15:04:05.000"
streamingCount = 10
)

func unaryCallWithMetadata(c pb.EchoClient, message string) {
fmt.Printf("--- unary ---\n")
// Create metadata and context.
md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat))
ctx := metadata.NewOutgoingContext(context.Background(), md)

// Make RPC using the context with the metadata.
var header, trailer metadata.MD
r, err := c.UnaryEcho(ctx, &pb.EchoRequest{Message: message}, grpc.Header(&header), grpc.Trailer(&trailer))
if err != nil {
log.Fatalf("failed to call UnaryEcho: %v", err)
}

if t, ok := header["timestamp"]; ok {
fmt.Printf("timestamp from header:\n")
for i, e := range t {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("timestamp expected but doesn't exist in header")
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either:

  • else { log.Fatal() }, or

  • remove the if and just assume it's present. Actually this will work and not output anything if it's missing, since we range over t, and ranging over a nil slice is fine.

In the second case...nothing bad happens if things are broken and the metadata is not present. Is that OK?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added else.

if l, ok := header["location"]; ok {
fmt.Printf("location from header:\n")
for i, e := range l {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("location expected but doesn't exist in header")
}
fmt.Printf("response:\n")
fmt.Printf(" - %s\n", r.Message)

if t, ok := trailer["timestamp"]; ok {
fmt.Printf("timestamp from trailer:\n")
for i, e := range t {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("timestamp expected but doesn't exist in trailer")
}
}

func serverStreamingWithMetadata(c pb.EchoClient, message string) {
fmt.Printf("--- server streaming ---\n")
// Create metadata and context.
md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat))
ctx := metadata.NewOutgoingContext(context.Background(), md)

// Make RPC using the context with the metadata.
stream, err := c.ServerStreamingEcho(ctx, &pb.EchoRequest{Message: message})
if err != nil {
log.Fatalf("failed to call ServerStreamingEcho: %v", err)
}

// Read the header when the header arrives.
header, err := stream.Header()
if err != nil {
log.Fatalf("failed to get header from stream: %v", err)
}
// Read metadata from server's header.
if t, ok := header["timestamp"]; ok {
fmt.Printf("timestamp from header:\n")
for i, e := range t {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("timestamp expected but doesn't exist in header")
}
if l, ok := header["location"]; ok {
fmt.Printf("location from header:\n")
for i, e := range l {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("location expected but doesn't exist in header")
}

// Read all the responses.
var rpcStatus error
fmt.Printf("response:\n")
for {
r, err := stream.Recv()
if err != nil {
rpcStatus = err
break
}
fmt.Printf(" - %s\n", r.Message)
}
if rpcStatus != io.EOF {
log.Fatalf("failed to finish server streaming: %v", rpcStatus)
}

// Read the trailer after the RPC is finished.
trailer := stream.Trailer()
// Read metadata from server's trailer.
if t, ok := trailer["timestamp"]; ok {
fmt.Printf("timestamp from trailer:\n")
for i, e := range t {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("timestamp expected but doesn't exist in trailer")
}
}

func clientStreamWithMetadata(c pb.EchoClient, message string) {
fmt.Printf("--- client streaming ---\n")
// Create metadata and context.
md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat))
ctx := metadata.NewOutgoingContext(context.Background(), md)

// Make RPC using the context with the metadata.
stream, err := c.ClientStreamingEcho(ctx)
if err != nil {
log.Fatalf("failed to call ClientStreamingEcho: %v\n", err)
}

// Read the header when the header arrives.
header, err := stream.Header()
if err != nil {
log.Fatalf("failed to get header from stream: %v", err)
}
// Read metadata from server's header.
if t, ok := header["timestamp"]; ok {
fmt.Printf("timestamp from header:\n")
for i, e := range t {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("timestamp expected but doesn't exist in header")
}
if l, ok := header["location"]; ok {
fmt.Printf("location from header:\n")
for i, e := range l {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("location expected but doesn't exist in header")
}

// Send all requests to the server.
for i := 0; i < streamingCount; i++ {
if err := stream.Send(&pb.EchoRequest{Message: message}); err != nil {
log.Fatalf("failed to send streaming: %v\n", err)
}
}

// Read the response.
r, err := stream.CloseAndRecv()
if err != nil {
log.Fatalf("failed to CloseAndRecv: %v\n", err)
}
fmt.Printf("response:\n")
fmt.Printf(" - %s\n\n", r.Message)

// Read the trailer after the RPC is finished.
trailer := stream.Trailer()
// Read metadata from server's trailer.
if t, ok := trailer["timestamp"]; ok {
fmt.Printf("timestamp from trailer:\n")
for i, e := range t {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("timestamp expected but doesn't exist in trailer")
}
}

func bidirectionalWithMetadata(c pb.EchoClient, message string) {
fmt.Printf("--- bidirectional ---\n")
// Create metadata and context.
md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat))
ctx := metadata.NewOutgoingContext(context.Background(), md)

// Make RPC using the context with the metadata.
stream, err := c.BidirectionalStreamingEcho(ctx)
if err != nil {
log.Fatalf("failed to call BidirectionalStreamingEcho: %v\n", err)
}

go func() {
// Read the header when the header arrives.
header, err := stream.Header()
if err != nil {
log.Fatalf("failed to get header from stream: %v", err)
}
// Read metadata from server's header.
if t, ok := header["timestamp"]; ok {
fmt.Printf("timestamp from header:\n")
for i, e := range t {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("timestamp expected but doesn't exist in header")
}
if l, ok := header["location"]; ok {
fmt.Printf("location from header:\n")
for i, e := range l {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("location expected but doesn't exist in header")
}

// Send all requests to the server.
for i := 0; i < streamingCount; i++ {
if err := stream.Send(&pb.EchoRequest{Message: message}); err != nil {
log.Fatalf("failed to send streaming: %v\n", err)
}
}
stream.CloseSend()
}()

// Read all the responses.
var rpcStatus error
fmt.Printf("response:\n")
for {
r, err := stream.Recv()
if err != nil {
rpcStatus = err
break
}
fmt.Printf(" - %s\n", r.Message)
}
if rpcStatus != io.EOF {
log.Fatalf("failed to finish server streaming: %v", rpcStatus)
}

// Read the trailer after the RPC is finished.
trailer := stream.Trailer()
// Read metadata from server's trailer.
if t, ok := trailer["timestamp"]; ok {
fmt.Printf("timestamp from trailer:\n")
for i, e := range t {
fmt.Printf(" %d. %s\n", i, e)
}
} else {
log.Fatal("timestamp expected but doesn't exist in trailer")
}

}

const message = "this is examples/metadata"

func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()

c := pb.NewEchoClient(conn)

unaryCallWithMetadata(c, message)
time.Sleep(1 * time.Second)

serverStreamingWithMetadata(c, message)
time.Sleep(1 * time.Second)

clientStreamWithMetadata(c, message)
time.Sleep(1 * time.Second)

bidirectionalWithMetadata(c, message)
}
Loading