├── .gitignore ├── README.md ├── features ├── authentication │ ├── basic-auth │ │ ├── client │ │ │ ├── cacert.pem │ │ │ └── main.go │ │ └── server │ │ │ ├── main.go │ │ │ ├── server.crt │ │ │ └── server.key │ ├── oauth2-token-auth │ │ ├── client │ │ │ ├── cacert.pem │ │ │ └── main.go │ │ └── server │ │ │ ├── main.go │ │ │ ├── server.crt │ │ │ └── server.key │ └── token-auth │ │ ├── client │ │ ├── cacert.pem │ │ └── main.go │ │ └── server │ │ ├── main.go │ │ ├── server.crt │ │ └── server.key ├── cancel │ ├── client │ │ └── main.go │ └── server │ │ └── main.go ├── deadline │ ├── client │ │ └── main.go │ └── server │ │ └── main.go ├── echopb │ ├── echo.pb.go │ ├── echo.proto │ └── protoc-gen.sh ├── errors │ ├── client │ │ └── main.go │ └── server │ │ └── main.go ├── interceptor │ ├── client │ │ ├── cacert.pem │ │ └── main.go │ └── server │ │ ├── main.go │ │ ├── server.crt │ │ └── server.key └── tls │ ├── client │ ├── cacert.pem │ └── main.go │ └── server │ ├── main.go │ ├── server.crt │ └── server.key ├── go.mod ├── go.sum ├── greet ├── greet_client │ └── main.go ├── greet_server │ └── main.go └── greetpb │ ├── greet.pb.go │ ├── greet.proto │ └── protoc-gen.sh ├── math ├── math_client │ └── main.go ├── math_server │ └── main.go └── mathpb │ ├── math.pb.go │ ├── math.proto │ └── protoc-gen.sh ├── restful-api-plus ├── client │ ├── cacert.pem │ └── main.go ├── go-bindata-assetfs │ └── bindata.go ├── server │ ├── cacert.pem │ ├── main.go │ ├── server.crt │ └── server.key ├── third_party │ └── swagger-ui │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── index.html │ │ ├── oauth2-redirect.html │ │ ├── swagger-ui-bundle.js │ │ ├── swagger-ui-bundle.js.map │ │ ├── swagger-ui-standalone-preset.js │ │ ├── swagger-ui-standalone-preset.js.map │ │ ├── swagger-ui.css │ │ ├── swagger-ui.css.map │ │ ├── swagger-ui.js │ │ └── swagger-ui.js.map └── userpb │ ├── protoc-gen.sh │ ├── service.pb.go │ ├── service.pb.gw.go │ ├── service.proto │ └── service.swagger.json └── restful-api ├── client ├── cacert.pem └── main.go ├── reverse-proxy ├── cacert.pem └── main.go ├── server ├── main.go ├── server.crt └── server.key └── userpb ├── protoc-gen.sh ├── service.pb.go ├── service.pb.gw.go └── service.proto /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Go 微服务实战系列](https://madmalls.com/blog/category/go-microservices/) 2 | 3 | 4 | # 1. 教程 5 | 6 | - [Go微服务实战|第0章:环境配置](https://madmalls.com/blog/post/grpc-setup-go-dependencies/) 7 | - [Go微服务实战|第1章:gRPC 简介](https://madmalls.com/blog/post/what-is-grpc/) 8 | - [Go微服务实战|第2章:gRPC Unary RPC](https://madmalls.com/blog/post/grpc-unary-rpc/) 9 | - [Go微服务实战|第3章:gRPC Server-side Streaming RPC](https://madmalls.com/blog/post/grpc-server-streaming-rpc/) 10 | - [Go微服务实战|第4章:gRPC Client-side Streaming RPC](https://madmalls.com/blog/post/grpc-client-streaming-rpc/) 11 | - [Go微服务实战|第5章:gRPC Bidirectional Streaming RPC](https://madmalls.com/blog/post/grpc-bidirectional-streaming-rpc/) 12 | - [Go微服务实战|第6章:gRPC Error Handling](https://madmalls.com/blog/post/grpc-error-handling/) 13 | - [Go微服务实战|第7章:gRPC Deadlines & Timeouts](https://madmalls.com/blog/post/grpc-deadline/) 14 | - [Go微服务实战|第8章:gRPC Cancelling RPCs](https://madmalls.com/blog/post/grpc-cancel/) 15 | - [Go微服务实战|第9章:gRPC TLS Secure](https://madmalls.com/blog/post/grpc-enable-tls/) 16 | - [Go微服务实战|第10章:gRPC Authentication](https://madmalls.com/blog/post/grpc-authentication/) 17 | - [Go微服务实战|第11章:gRPC Interceptors](https://madmalls.com/blog/post/grpc-interceptors/) 18 | - [Go微服务实战|第12章:gRPC-gateway generate RESTful API](https://madmalls.com/blog/post/grpc-gateway-generate-restfull-api/) 19 | 20 | 21 | # 2. 使用 22 | 23 | Clone the code: 24 | 25 | ```bash 26 | [root@CentOS ~]# git clone https://github.com/wangy8961/grpc-go-tutorial.git 27 | ``` 28 | 29 | Open a new terminal: 30 | 31 | ```bash 32 | [root@CentOS ~]# cd grpc-go-tutorial 33 | [root@CentOS grpc-go-tutorial]# go run greet/greet_server/main.go 34 | ``` 35 | 36 | Open a new terminal: 37 | 38 | ```bash 39 | [root@CentOS ~]# cd grpc-go-tutorial 40 | [root@CentOS grpc-go-tutorial]# go run greet/greet_client/main.go 41 | ``` 42 | -------------------------------------------------------------------------------- /features/authentication/basic-auth/client/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3TCCAsWgAwIBAgIJAKoKZuLTxD+eMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD 3 | VQQGEwJDTjESMBAGA1UECAwJR3VhbmdEb25nMREwDwYDVQQHDAhTaGVuWmhlbjEd 4 | MBsGA1UECgwUTWFkbWFsbHMuY29tIENvLixMdGQxHTAbBgNVBAsMFE1hZG1hbGxz 5 | LmNvbSBSb290IENBMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDUyOTA3MjMwM1oX 6 | DTI5MDUyNjA3MjMwM1owgYQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0Rv 7 | bmcxETAPBgNVBAcMCFNoZW5aaGVuMR0wGwYDVQQKDBRNYWRtYWxscy5jb20gQ28u 8 | LEx0ZDEdMBsGA1UECwwUTWFkbWFsbHMuY29tIFJvb3QgQ0ExEDAOBgNVBAMMB1Jv 9 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsaZ3C8XzVTfir 10 | 2LZpJMMnxo2ZUSRcjrVRav3ATtcubqE94XT/nq5wrUClhvOWKeUIFaPZHY5dXSLm 11 | LAIRaCNW6t0cHcfCyd6ArVbjVCT8dbo1wwis2ATFRVRiVmuFjPSu2iLR/pm9m94Q 12 | C1uZe4ypXGW08TieivNiRTflfXNvjKzDENJriEQ6ukhwAixOLZcbYKZKi4wZXscl 13 | iaBcitcCZty0ODtH7TXQbrQNU2lkQBT/HmtABgL4ln5p+tiDUTNCJjvPeNBT4FTs 14 | KLtcnJM4NJspRwHO4YIGGHhmgJhneSPsHm5poE6pVu3DDRFHuforPv/k70cX9PGz 15 | OQlREfNRAgMBAAGjUDBOMB0GA1UdDgQWBBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAf 16 | BgNVHSMEGDAWgBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAMBgNVHRMEBTADAQH/MA0G 17 | CSqGSIb3DQEBCwUAA4IBAQCfcXII5evHWW5qcF3f2wMRCFhizOCojQ6CA2mwH9LN 18 | GsZRclTwniQ7OqH67b94ii3mF3dUK7klth0jbx6rJ9jYsGZ6dCuK6a1MWtCey8P4 19 | VEbiBA2qXwjJzeap1G9mdPz16YTRlbdLhJDgN7fxmTK+A4GOObiq8x2EX9DjjCym 20 | fI39D0Z9o/2i29KZGC25DWYvjnLrz3EjfGkct1QrQG9q93pwQNKm+SKHRoUIOExe 21 | KsmgS0jPxie/IUWSDHlkCY19tHSX2swRNjkiomt1pKOozN3eTu1qs29f2HA/tKAw 22 | 6/Hwdo0XD+ybVEHq/STsppCe4zMojmChxuDVTYo2de3C 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /features/authentication/basic-auth/client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "encoding/base64" 7 | "flag" 8 | "fmt" 9 | "log" 10 | 11 | "google.golang.org/grpc/credentials" 12 | 13 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 14 | "google.golang.org/grpc" 15 | ) 16 | 17 | type basicAuth struct { 18 | username string 19 | password string 20 | } 21 | 22 | // Return value is mapped to request headers. 23 | func (b *basicAuth) GetRequestMetadata(ctx context.Context, in ...string) (map[string]string, error) { 24 | auth := b.username + ":" + b.password 25 | enc := base64.StdEncoding.EncodeToString([]byte(auth)) 26 | return map[string]string{ 27 | "authorization": "Basic " + enc, 28 | }, nil 29 | } 30 | 31 | // 是否使用 TLS 安全加密 32 | func (b *basicAuth) RequireTransportSecurity() bool { 33 | return true 34 | } 35 | 36 | func main() { 37 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 38 | certFile := flag.String("cacert", "cacert.pem", "CA root certificate") 39 | username := flag.String("username", "admin", "The username to authenticate with") 40 | password := flag.String("password", "password", "The password to authenticate with") 41 | flag.Parse() 42 | 43 | creds, err := credentials.NewClientTLSFromFile(*certFile, "") 44 | if err != nil { 45 | log.Fatalf("failed to load CA root certificate: %v", err) 46 | } 47 | 48 | opts := []grpc.DialOption{ 49 | // 1. TLS 认证 50 | grpc.WithTransportCredentials(creds), 51 | // 2. basic token 认证 52 | grpc.WithPerRPCCredentials(&basicAuth{ 53 | username: *username, 54 | password: *password, 55 | }), 56 | } 57 | 58 | // Set up a connection to the server. 59 | conn, err := grpc.Dial(*addr, opts...) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 60 | if err != nil { 61 | log.Fatalf("did not connect: %v", err) 62 | } 63 | defer conn.Close() 64 | 65 | c := pb.NewEchoClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewEchoClient method provided in the pb package we generated from our .proto. 66 | 67 | // Contact the server and print out its response. 68 | msg := "madmalls.com" 69 | resp, err := c.UnaryEcho(context.Background(), &pb.EchoRequest{Message: msg}) // Now let’s look at how we call our service methods. Note that in gRPC-Go, RPCs operate in a blocking/synchronous mode, which means that the RPC call waits for the server to respond, and will either return a response or an error. 70 | if err != nil { 71 | log.Fatalf("failed to call UnaryEcho: %v", err) 72 | } 73 | fmt.Printf("response:\n") 74 | fmt.Printf(" - %q\n", resp.GetMessage()) 75 | } 76 | -------------------------------------------------------------------------------- /features/authentication/basic-auth/server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "encoding/base64" 7 | "flag" 8 | "fmt" 9 | "log" 10 | "net" 11 | "strings" 12 | 13 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 14 | "google.golang.org/grpc" 15 | "google.golang.org/grpc/codes" 16 | "google.golang.org/grpc/credentials" 17 | "google.golang.org/grpc/metadata" 18 | "google.golang.org/grpc/status" 19 | ) 20 | 21 | // server is used to implement echopb.EchoServer. 22 | type server struct{} 23 | 24 | /* 25 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 26 | return nil, status.Errorf(codes.Unimplemented, "method UnaryEcho not implemented") 27 | } 28 | */ 29 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 30 | fmt.Printf("--- gRPC Unary RPC ---\n") 31 | fmt.Printf("request received: %v\n", req) 32 | 33 | // md 的值类似于: map[:authority:[192.168.40.123:50051] authorization:[Basic YWRtaW46cGFzc3dvcmQ=] content-type:[application/grpc] user-agent:[grpc-go/1.21.1]] 34 | md, ok := metadata.FromIncomingContext(ctx) 35 | if !ok { 36 | return nil, status.Errorf(codes.InvalidArgument, "missing metadata") 37 | } 38 | //fmt.Printf("Type of 'metadata.MD' is %T, and its value is %v \n", md, md) 39 | 40 | // 1. 判断是否存在 authorization 请求头 41 | authorization, ok := md["authorization"] 42 | if !ok { 43 | return nil, status.Errorf(codes.Unauthenticated, `missing "Authorization" header`) 44 | } 45 | //fmt.Printf("Type of 'authorization' is %T, and its value is %v \n", authorization, authorization) 46 | 47 | const prefix = "Basic " 48 | 49 | // 2. 如果存在 authorization 请求头的话,则 md["authorization"] 是一个 []string 50 | if !strings.HasPrefix(authorization[0], prefix) { 51 | return nil, status.Errorf(codes.Unauthenticated, `missing "Basic " prefix in "Authorization" header`) 52 | } 53 | 54 | // 3. 验证用户名和密码 55 | // 用户名和密码被 Base64 编码了 56 | sEnc := strings.TrimPrefix(authorization[0], prefix) 57 | sDec, err := base64.StdEncoding.DecodeString(sEnc) 58 | if err != nil { 59 | return nil, status.Error(codes.Unauthenticated, `invalid base64 in header`) 60 | } 61 | //fmt.Printf("Enc is: %v\nDec is: %v\n", sEnc, sDec) 62 | // 先转换为字符串 63 | basicAuthStr := string(sDec) 64 | //fmt.Printf("Basic Auth string is: %v\n", basicAuthStr) 65 | // 用户名和密码之间要用 : 隔开 66 | i := strings.IndexByte(basicAuthStr, ':') 67 | if i < 0 { 68 | return nil, status.Error(codes.Unauthenticated, `invalid basic auth format`) 69 | } 70 | //fmt.Printf("Index of ':' is: %v\n", i) 71 | // 验证用户名和密码是否一致 72 | username, password := basicAuthStr[:i], basicAuthStr[i+1:] 73 | //fmt.Printf("username is: %v\npassword is: %v\n", username, password) 74 | if username != "admin" || password != "password" { 75 | return nil, status.Error(codes.Unauthenticated, "invalid user or password") 76 | } 77 | 78 | return &pb.EchoResponse{Message: req.GetMessage()}, nil 79 | } 80 | 81 | func (s *server) ServerStreamingEcho(req *pb.EchoRequest, stream pb.Echo_ServerStreamingEchoServer) error { 82 | return status.Errorf(codes.Unimplemented, "method ServerStreamingEcho not implemented") 83 | } 84 | 85 | func (s *server) ClientStreamingEcho(stream pb.Echo_ClientStreamingEchoServer) error { 86 | return status.Errorf(codes.Unimplemented, "method ClientStreamingEcho not implemented") 87 | } 88 | 89 | func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { 90 | return status.Errorf(codes.Unimplemented, "method BidirectionalStreamingEcho not implemented") 91 | } 92 | 93 | func main() { 94 | port := flag.Int("port", 50051, "the port to serve on") 95 | certFile := flag.String("certfile", "server.crt", "Server certificate") 96 | keyFile := flag.String("keyfile", "server.key", "Server private key") 97 | flag.Parse() 98 | 99 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests 100 | if err != nil { 101 | log.Fatalf("failed to listen: %v", err) 102 | } 103 | fmt.Printf("server listening at %v\n", lis.Addr()) 104 | 105 | creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) 106 | if err != nil { 107 | log.Fatalf("failed to load certificates: %v", err) 108 | } 109 | 110 | s := grpc.NewServer(grpc.Creds(creds)) // Create an instance of the gRPC server 111 | 112 | pb.RegisterEchoServer(s, &server{}) // Register our service implementation with the gRPC server 113 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 114 | log.Fatalf("failed to serve: %v", err) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /features/authentication/basic-auth/server/server.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 (0x5) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: C=CN, ST=GuangDong, L=ShenZhen, O=Madmalls.com Co.,Ltd, OU=Madmalls.com Root CA, CN=Root CA 7 | Validity 8 | Not Before: Jun 11 07:22:30 2019 GMT 9 | Not After : Jun 10 07:22:30 2020 GMT 10 | Subject: C=CN, ST=GuangDong, O=Madmalls.com Co.,Ltd, OU=gRPC Server, CN=*.wangy.com 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | Public-Key: (2048 bit) 14 | Modulus: 15 | 00:e2:93:8c:43:64:e9:c6:02:6b:d1:98:f3:79:47: 16 | d5:99:c6:46:a8:6d:24:cc:a0:d8:3b:ec:e4:d0:83: 17 | 79:42:f5:bf:23:e1:0e:34:b3:f1:35:c6:27:6e:44: 18 | 58:48:ca:91:42:8c:6c:2f:6c:9d:09:d0:1b:73:d8: 19 | 42:f4:70:71:6c:40:f7:6c:71:63:39:db:d1:f8:f6: 20 | d4:f8:b7:18:03:7a:7d:32:0c:3f:f4:fd:0a:4c:e3: 21 | ad:71:33:8b:e6:25:53:52:d8:72:67:9f:56:1b:7e: 22 | d4:b7:27:28:0e:1d:28:92:b6:94:09:4f:bd:51:ef: 23 | 06:bc:80:39:97:cb:d2:20:8c:0c:ff:cf:33:97:7d: 24 | 7a:0e:01:e7:90:a2:30:64:34:be:3e:78:d9:3f:19: 25 | 7b:27:fc:c1:b2:0d:07:34:8b:6e:2e:0b:50:f4:8a: 26 | be:07:63:98:25:24:00:97:89:d2:ad:96:cd:56:01: 27 | 84:df:dc:af:be:a8:12:ef:f8:77:75:46:2d:49:21: 28 | ae:1c:bc:b1:87:94:4d:8e:b7:5d:7c:01:0a:c6:e6: 29 | 32:32:47:c3:d1:38:ab:5d:02:4f:72:c5:10:9c:a2: 30 | 83:9d:ee:a1:37:ee:02:bb:50:8a:78:ba:bf:f7:f7: 31 | 69:b0:ff:17:31:14:68:a5:db:05:ef:ab:2e:53:ac: 32 | ae:df 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Subject Alternative Name: 36 | DNS:www.wangy.com, DNS:blog.wangy.com, IP Address:192.168.40.123 37 | Signature Algorithm: sha256WithRSAEncryption 38 | 42:83:8d:68:cf:9b:93:59:63:94:5d:62:a8:14:bb:79:f4:09: 39 | cf:6d:ea:e5:86:bd:9a:fc:28:e2:9b:c5:fa:df:e9:50:b0:ac: 40 | 28:4c:7a:ff:38:67:1a:9b:e6:aa:8d:bd:da:3d:6f:e8:49:fa: 41 | f2:7e:fa:dd:4b:a8:4d:06:ee:1c:47:ff:36:99:23:24:9b:4c: 42 | 19:d1:f4:1c:85:c7:94:84:54:19:b0:78:6f:d7:54:25:a8:da: 43 | b1:18:76:1b:71:15:ad:35:d5:64:b6:6f:ee:80:6d:82:98:71: 44 | b8:db:bb:e5:01:da:f7:f1:aa:97:a7:ae:f8:90:b6:f6:ac:38: 45 | fa:9e:a2:15:10:2f:ca:c5:eb:bf:91:67:bc:f7:ee:2a:0e:80: 46 | 35:ad:6d:03:20:98:4f:df:39:5b:ac:f2:ef:43:01:95:b9:0a: 47 | 7b:29:b4:6d:83:de:1f:94:a6:69:6b:42:18:ef:cb:e6:68:66: 48 | b2:79:59:f8:ae:9f:89:25:ec:ee:46:f9:3c:34:ab:74:7d:72: 49 | 0a:3f:39:6e:07:28:79:e5:8c:8b:3a:da:1f:b9:7c:af:0c:0a: 50 | 0c:71:3d:aa:76:87:69:4c:75:9a:0b:8f:8e:10:f8:d6:53:c9: 51 | 35:8b:10:42:0a:32:01:a5:83:e3:22:0e:86:c0:1d:87:75:af: 52 | cb:30:cb:1a 53 | -----BEGIN CERTIFICATE----- 54 | MIIDnjCCAoagAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMCQ04x 55 | EjAQBgNVBAgMCUd1YW5nRG9uZzERMA8GA1UEBwwIU2hlblpoZW4xHTAbBgNVBAoM 56 | FE1hZG1hbGxzLmNvbSBDby4sTHRkMR0wGwYDVQQLDBRNYWRtYWxscy5jb20gUm9v 57 | dCBDQTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xOTA2MTEwNzIyMzBaFw0yMDA2MTAw 58 | NzIyMzBaMGwxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxHTAbBgNV 59 | BAoMFE1hZG1hbGxzLmNvbSBDby4sTHRkMRQwEgYDVQQLDAtnUlBDIFNlcnZlcjEU 60 | MBIGA1UEAwwLKi53YW5neS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 61 | AoIBAQDik4xDZOnGAmvRmPN5R9WZxkaobSTMoNg77OTQg3lC9b8j4Q40s/E1xidu 62 | RFhIypFCjGwvbJ0J0Btz2EL0cHFsQPdscWM529H49tT4txgDen0yDD/0/QpM461x 63 | M4vmJVNS2HJnn1YbftS3JygOHSiStpQJT71R7wa8gDmXy9IgjAz/zzOXfXoOAeeQ 64 | ojBkNL4+eNk/GXsn/MGyDQc0i24uC1D0ir4HY5glJACXidKtls1WAYTf3K++qBLv 65 | +Hd1Ri1JIa4cvLGHlE2Ot118AQrG5jIyR8PROKtdAk9yxRCcooOd7qE37gK7UIp4 66 | ur/392mw/xcxFGil2wXvqy5TrK7fAgMBAAGjMjAwMC4GA1UdEQQnMCWCDXd3dy53 67 | YW5neS5jb22CDmJsb2cud2FuZ3kuY29thwTAqCh7MA0GCSqGSIb3DQEBCwUAA4IB 68 | AQBCg41oz5uTWWOUXWKoFLt59AnPberlhr2a/Cjim8X63+lQsKwoTHr/OGcam+aq 69 | jb3aPW/oSfryfvrdS6hNBu4cR/82mSMkm0wZ0fQchceUhFQZsHhv11QlqNqxGHYb 70 | cRWtNdVktm/ugG2CmHG427vlAdr38aqXp674kLb2rDj6nqIVEC/Kxeu/kWe89+4q 71 | DoA1rW0DIJhP3zlbrPLvQwGVuQp7KbRtg94flKZpa0IY78vmaGayeVn4rp+JJezu 72 | Rvk8NKt0fXIKPzluByh55YyLOtofuXyvDAoMcT2qdodpTHWaC4+OEPjWU8k1ixBC 73 | CjIBpYPjIg6GwB2Hda/LMMsa 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /features/authentication/basic-auth/server/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpgIBAAKCAQEA4pOMQ2TpxgJr0ZjzeUfVmcZGqG0kzKDYO+zk0IN5QvW/I+EO 3 | NLPxNcYnbkRYSMqRQoxsL2ydCdAbc9hC9HBxbED3bHFjOdvR+PbU+LcYA3p9Mgw/ 4 | 9P0KTOOtcTOL5iVTUthyZ59WG37UtycoDh0okraUCU+9Ue8GvIA5l8vSIIwM/88z 5 | l316DgHnkKIwZDS+PnjZPxl7J/zBsg0HNItuLgtQ9Iq+B2OYJSQAl4nSrZbNVgGE 6 | 39yvvqgS7/h3dUYtSSGuHLyxh5RNjrddfAEKxuYyMkfD0TirXQJPcsUQnKKDne6h 7 | N+4Cu1CKeLq/9/dpsP8XMRRopdsF76suU6yu3wIDAQABAoIBAQCjrPDjco/KAc+/ 8 | ft1LnI/6YRiD7SxrQjpSt+PnmUJNE9e7ZIXtnpu+O+IaLvcTxnm++E/ixnR/NT3P 9 | psdfa6cUC65xQUvr7Rc24aCh9yo6wQ6Vy/Gb2fvJ5aNSpmkGnaoeq8uhfaInhKzH 10 | jlrKL1gy+//e5iKegKx+GacBODUYWpc8bVoROpvqgtSDg1wtuwUU/5fy6f8IIHuT 11 | 78cPm6t39ylcFkV3lt1yS4LhvUoW2Nlm5SHOicDc/cYVE8xqfJlARzN9xUfM+5LB 12 | sUdbR5UZOxOPsd24q1D1DZc1s34soj/wKXPVyyuhp0o7Db6vSsxQDfOetfuRUSR6 13 | +5zPMLqxAoGBAP6laEBin8SVThAvQmS4PoyqPK8Hb0GgjxPGxyXyrtSL3/Xy86E0 14 | vhOGTm8gh2oA0wZQ3KIg5TIhVa3G+UNMbZmIuYCQdeAQpl/sdMnpw66aNtHj6kt7 15 | gqzWzH1en4w2T3DxU7LtCQIx4q3Akv0oyZ/YovlNb5dkSHtuz4ZVRYkXAoGBAOPH 16 | 74KHzkD/IWkGf7/QG9XQ93hjggxmDS/WzeKwKYewPyZoWRvkvj7i7d+P1AUrCvgd 17 | YeB132S0S1JrSUP4Fn+DNwgj3Kyg2Fq2/HmwTKkqVPjOKlonTKlA+h3fNG/NbJDE 18 | E41yFkK+zjSbxxcAYLCgR2A9PCEyXPXyV20U8BV5AoGBAJnq5unL8yBC0u2Lc0kn 19 | 6H7jw0xUZRY482KTyvoQB0bnyRaDpGkzVRS+IJihA9i56NOverzwvzie14fzdeUM 20 | xE6CSwX/y5AE4FuotCr7hlD6W9pgNdUsMZ9BMlcxI6T/iuMMq3fCOKi/+HDnrrEg 21 | v0ZEDrY77RCICBu7repXjnE/AoGBAIdBvhOAmRU3apt25Hz+EslQoOK4FA1QvBvg 22 | LbmiacbM/XLNG7zYg6/MCPxr57Z57LWQnQIwfErMVL3IP2VA9/sX66HFydAoYtDb 23 | P+jyq1L4dCSaJ8QI+hi3IM6EMBsDnKgKBqJDULypmMDcj8g0zTWUt02Kjx4XTeQt 24 | 14RKnpXhAoGBAJkImhczkTucb/Yhaw1du2IkylPSb+HAJH6NFH9LFNvKYOIFfUaA 25 | id8ZkR5WGDJiK7vm3QT4n6j14/CnTXqrQ52W9ixxOIjbd1AHgHNwlbQGJodInT+l 26 | j3Iqa44peXerIk0MO8qYEjEGybHu7DhZhkbZoCn1Qqn+XpDbx2bbPe7w 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /features/authentication/oauth2-token-auth/client/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3TCCAsWgAwIBAgIJAKoKZuLTxD+eMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD 3 | VQQGEwJDTjESMBAGA1UECAwJR3VhbmdEb25nMREwDwYDVQQHDAhTaGVuWmhlbjEd 4 | MBsGA1UECgwUTWFkbWFsbHMuY29tIENvLixMdGQxHTAbBgNVBAsMFE1hZG1hbGxz 5 | LmNvbSBSb290IENBMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDUyOTA3MjMwM1oX 6 | DTI5MDUyNjA3MjMwM1owgYQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0Rv 7 | bmcxETAPBgNVBAcMCFNoZW5aaGVuMR0wGwYDVQQKDBRNYWRtYWxscy5jb20gQ28u 8 | LEx0ZDEdMBsGA1UECwwUTWFkbWFsbHMuY29tIFJvb3QgQ0ExEDAOBgNVBAMMB1Jv 9 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsaZ3C8XzVTfir 10 | 2LZpJMMnxo2ZUSRcjrVRav3ATtcubqE94XT/nq5wrUClhvOWKeUIFaPZHY5dXSLm 11 | LAIRaCNW6t0cHcfCyd6ArVbjVCT8dbo1wwis2ATFRVRiVmuFjPSu2iLR/pm9m94Q 12 | C1uZe4ypXGW08TieivNiRTflfXNvjKzDENJriEQ6ukhwAixOLZcbYKZKi4wZXscl 13 | iaBcitcCZty0ODtH7TXQbrQNU2lkQBT/HmtABgL4ln5p+tiDUTNCJjvPeNBT4FTs 14 | KLtcnJM4NJspRwHO4YIGGHhmgJhneSPsHm5poE6pVu3DDRFHuforPv/k70cX9PGz 15 | OQlREfNRAgMBAAGjUDBOMB0GA1UdDgQWBBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAf 16 | BgNVHSMEGDAWgBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAMBgNVHRMEBTADAQH/MA0G 17 | CSqGSIb3DQEBCwUAA4IBAQCfcXII5evHWW5qcF3f2wMRCFhizOCojQ6CA2mwH9LN 18 | GsZRclTwniQ7OqH67b94ii3mF3dUK7klth0jbx6rJ9jYsGZ6dCuK6a1MWtCey8P4 19 | VEbiBA2qXwjJzeap1G9mdPz16YTRlbdLhJDgN7fxmTK+A4GOObiq8x2EX9DjjCym 20 | fI39D0Z9o/2i29KZGC25DWYvjnLrz3EjfGkct1QrQG9q93pwQNKm+SKHRoUIOExe 21 | KsmgS0jPxie/IUWSDHlkCY19tHSX2swRNjkiomt1pKOozN3eTu1qs29f2HA/tKAw 22 | 6/Hwdo0XD+ybVEHq/STsppCe4zMojmChxuDVTYo2de3C 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /features/authentication/oauth2-token-auth/client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | 10 | "golang.org/x/oauth2" 11 | "google.golang.org/grpc/credentials/oauth" 12 | 13 | "google.golang.org/grpc/credentials" 14 | 15 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 16 | "google.golang.org/grpc" 17 | ) 18 | 19 | func main() { 20 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 21 | certFile := flag.String("cacert", "cacert.pem", "CA root certificate") 22 | flag.Parse() 23 | 24 | creds, err := credentials.NewClientTLSFromFile(*certFile, "") 25 | if err != nil { 26 | log.Fatalf("failed to load CA root certificate: %v", err) 27 | } 28 | 29 | opts := []grpc.DialOption{ 30 | // 1. TLS 认证 31 | grpc.WithTransportCredentials(creds), 32 | // 2. oauth2 acces token 认证 33 | grpc.WithPerRPCCredentials(oauth.NewOauthAccess(&oauth2.Token{ 34 | AccessToken: "some-oauth2-secret-token", 35 | })), 36 | } 37 | 38 | // Set up a connection to the server. 39 | conn, err := grpc.Dial(*addr, opts...) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 40 | if err != nil { 41 | log.Fatalf("did not connect: %v", err) 42 | } 43 | defer conn.Close() 44 | 45 | c := pb.NewEchoClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewEchoClient method provided in the pb package we generated from our .proto. 46 | 47 | // Contact the server and print out its response. 48 | msg := "madmalls.com" 49 | resp, err := c.UnaryEcho(context.Background(), &pb.EchoRequest{Message: msg}) // Now let’s look at how we call our service methods. Note that in gRPC-Go, RPCs operate in a blocking/synchronous mode, which means that the RPC call waits for the server to respond, and will either return a response or an error. 50 | if err != nil { 51 | log.Fatalf("failed to call UnaryEcho: %v", err) 52 | } 53 | fmt.Printf("response:\n") 54 | fmt.Printf(" - %q\n", resp.GetMessage()) 55 | } 56 | -------------------------------------------------------------------------------- /features/authentication/oauth2-token-auth/server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net" 10 | "strings" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 13 | "google.golang.org/grpc" 14 | "google.golang.org/grpc/codes" 15 | "google.golang.org/grpc/credentials" 16 | "google.golang.org/grpc/metadata" 17 | "google.golang.org/grpc/status" 18 | ) 19 | 20 | // server is used to implement echopb.EchoServer. 21 | type server struct{} 22 | 23 | /* 24 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 25 | return nil, status.Errorf(codes.Unimplemented, "method UnaryEcho not implemented") 26 | } 27 | */ 28 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 29 | fmt.Printf("--- gRPC Unary RPC ---\n") 30 | fmt.Printf("request received: %v\n", req) 31 | 32 | // md 的值类似于: map[:authority:[192.168.40.123:50051] authorization:[Bearer some-secret-token] content-type:[application/grpc] user-agent:[grpc-go/1.20.1]] 33 | md, ok := metadata.FromIncomingContext(ctx) 34 | if !ok { 35 | return nil, status.Errorf(codes.InvalidArgument, "missing metadata") 36 | } 37 | fmt.Printf("Type of 'metadata.MD' is %T, and its value is %v \n", md, md) 38 | 39 | // 1. 判断是否存在 authorization 请求头 40 | authorization, ok := md["authorization"] 41 | if !ok { 42 | return nil, status.Errorf(codes.Unauthenticated, `missing "Authorization" header`) 43 | } 44 | fmt.Printf("Type of 'authorization' is %T, and its value is %v \n", authorization, authorization) 45 | 46 | const prefix = "Bearer " 47 | 48 | // 2. 如果存在 authorization 请求头的话,则 md["authorization"] 是一个 []string 49 | if !strings.HasPrefix(authorization[0], prefix) { 50 | return nil, status.Errorf(codes.Unauthenticated, `missing "Bearer " prefix in "Authorization" header`) 51 | } 52 | 53 | // 3. 验证 token 是否一致 54 | if token := strings.TrimPrefix(authorization[0], prefix); token != "some-oauth2-secret-token" { 55 | return nil, status.Errorf(codes.Unauthenticated, "invalid token") 56 | } 57 | 58 | return &pb.EchoResponse{Message: req.GetMessage()}, nil 59 | } 60 | 61 | func (s *server) ServerStreamingEcho(req *pb.EchoRequest, stream pb.Echo_ServerStreamingEchoServer) error { 62 | return status.Errorf(codes.Unimplemented, "method ServerStreamingEcho not implemented") 63 | } 64 | 65 | func (s *server) ClientStreamingEcho(stream pb.Echo_ClientStreamingEchoServer) error { 66 | return status.Errorf(codes.Unimplemented, "method ClientStreamingEcho not implemented") 67 | } 68 | 69 | func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { 70 | return status.Errorf(codes.Unimplemented, "method BidirectionalStreamingEcho not implemented") 71 | } 72 | 73 | func main() { 74 | port := flag.Int("port", 50051, "the port to serve on") 75 | certFile := flag.String("certfile", "server.crt", "Server certificate") 76 | keyFile := flag.String("keyfile", "server.key", "Server private key") 77 | flag.Parse() 78 | 79 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests 80 | if err != nil { 81 | log.Fatalf("failed to listen: %v", err) 82 | } 83 | fmt.Printf("server listening at %v\n", lis.Addr()) 84 | 85 | creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) 86 | if err != nil { 87 | log.Fatalf("failed to load certificates: %v", err) 88 | } 89 | 90 | s := grpc.NewServer(grpc.Creds(creds)) // Create an instance of the gRPC server 91 | 92 | pb.RegisterEchoServer(s, &server{}) // Register our service implementation with the gRPC server 93 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 94 | log.Fatalf("failed to serve: %v", err) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /features/authentication/oauth2-token-auth/server/server.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 (0x5) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: C=CN, ST=GuangDong, L=ShenZhen, O=Madmalls.com Co.,Ltd, OU=Madmalls.com Root CA, CN=Root CA 7 | Validity 8 | Not Before: Jun 11 07:22:30 2019 GMT 9 | Not After : Jun 10 07:22:30 2020 GMT 10 | Subject: C=CN, ST=GuangDong, O=Madmalls.com Co.,Ltd, OU=gRPC Server, CN=*.wangy.com 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | Public-Key: (2048 bit) 14 | Modulus: 15 | 00:e2:93:8c:43:64:e9:c6:02:6b:d1:98:f3:79:47: 16 | d5:99:c6:46:a8:6d:24:cc:a0:d8:3b:ec:e4:d0:83: 17 | 79:42:f5:bf:23:e1:0e:34:b3:f1:35:c6:27:6e:44: 18 | 58:48:ca:91:42:8c:6c:2f:6c:9d:09:d0:1b:73:d8: 19 | 42:f4:70:71:6c:40:f7:6c:71:63:39:db:d1:f8:f6: 20 | d4:f8:b7:18:03:7a:7d:32:0c:3f:f4:fd:0a:4c:e3: 21 | ad:71:33:8b:e6:25:53:52:d8:72:67:9f:56:1b:7e: 22 | d4:b7:27:28:0e:1d:28:92:b6:94:09:4f:bd:51:ef: 23 | 06:bc:80:39:97:cb:d2:20:8c:0c:ff:cf:33:97:7d: 24 | 7a:0e:01:e7:90:a2:30:64:34:be:3e:78:d9:3f:19: 25 | 7b:27:fc:c1:b2:0d:07:34:8b:6e:2e:0b:50:f4:8a: 26 | be:07:63:98:25:24:00:97:89:d2:ad:96:cd:56:01: 27 | 84:df:dc:af:be:a8:12:ef:f8:77:75:46:2d:49:21: 28 | ae:1c:bc:b1:87:94:4d:8e:b7:5d:7c:01:0a:c6:e6: 29 | 32:32:47:c3:d1:38:ab:5d:02:4f:72:c5:10:9c:a2: 30 | 83:9d:ee:a1:37:ee:02:bb:50:8a:78:ba:bf:f7:f7: 31 | 69:b0:ff:17:31:14:68:a5:db:05:ef:ab:2e:53:ac: 32 | ae:df 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Subject Alternative Name: 36 | DNS:www.wangy.com, DNS:blog.wangy.com, IP Address:192.168.40.123 37 | Signature Algorithm: sha256WithRSAEncryption 38 | 42:83:8d:68:cf:9b:93:59:63:94:5d:62:a8:14:bb:79:f4:09: 39 | cf:6d:ea:e5:86:bd:9a:fc:28:e2:9b:c5:fa:df:e9:50:b0:ac: 40 | 28:4c:7a:ff:38:67:1a:9b:e6:aa:8d:bd:da:3d:6f:e8:49:fa: 41 | f2:7e:fa:dd:4b:a8:4d:06:ee:1c:47:ff:36:99:23:24:9b:4c: 42 | 19:d1:f4:1c:85:c7:94:84:54:19:b0:78:6f:d7:54:25:a8:da: 43 | b1:18:76:1b:71:15:ad:35:d5:64:b6:6f:ee:80:6d:82:98:71: 44 | b8:db:bb:e5:01:da:f7:f1:aa:97:a7:ae:f8:90:b6:f6:ac:38: 45 | fa:9e:a2:15:10:2f:ca:c5:eb:bf:91:67:bc:f7:ee:2a:0e:80: 46 | 35:ad:6d:03:20:98:4f:df:39:5b:ac:f2:ef:43:01:95:b9:0a: 47 | 7b:29:b4:6d:83:de:1f:94:a6:69:6b:42:18:ef:cb:e6:68:66: 48 | b2:79:59:f8:ae:9f:89:25:ec:ee:46:f9:3c:34:ab:74:7d:72: 49 | 0a:3f:39:6e:07:28:79:e5:8c:8b:3a:da:1f:b9:7c:af:0c:0a: 50 | 0c:71:3d:aa:76:87:69:4c:75:9a:0b:8f:8e:10:f8:d6:53:c9: 51 | 35:8b:10:42:0a:32:01:a5:83:e3:22:0e:86:c0:1d:87:75:af: 52 | cb:30:cb:1a 53 | -----BEGIN CERTIFICATE----- 54 | MIIDnjCCAoagAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMCQ04x 55 | EjAQBgNVBAgMCUd1YW5nRG9uZzERMA8GA1UEBwwIU2hlblpoZW4xHTAbBgNVBAoM 56 | FE1hZG1hbGxzLmNvbSBDby4sTHRkMR0wGwYDVQQLDBRNYWRtYWxscy5jb20gUm9v 57 | dCBDQTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xOTA2MTEwNzIyMzBaFw0yMDA2MTAw 58 | NzIyMzBaMGwxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxHTAbBgNV 59 | BAoMFE1hZG1hbGxzLmNvbSBDby4sTHRkMRQwEgYDVQQLDAtnUlBDIFNlcnZlcjEU 60 | MBIGA1UEAwwLKi53YW5neS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 61 | AoIBAQDik4xDZOnGAmvRmPN5R9WZxkaobSTMoNg77OTQg3lC9b8j4Q40s/E1xidu 62 | RFhIypFCjGwvbJ0J0Btz2EL0cHFsQPdscWM529H49tT4txgDen0yDD/0/QpM461x 63 | M4vmJVNS2HJnn1YbftS3JygOHSiStpQJT71R7wa8gDmXy9IgjAz/zzOXfXoOAeeQ 64 | ojBkNL4+eNk/GXsn/MGyDQc0i24uC1D0ir4HY5glJACXidKtls1WAYTf3K++qBLv 65 | +Hd1Ri1JIa4cvLGHlE2Ot118AQrG5jIyR8PROKtdAk9yxRCcooOd7qE37gK7UIp4 66 | ur/392mw/xcxFGil2wXvqy5TrK7fAgMBAAGjMjAwMC4GA1UdEQQnMCWCDXd3dy53 67 | YW5neS5jb22CDmJsb2cud2FuZ3kuY29thwTAqCh7MA0GCSqGSIb3DQEBCwUAA4IB 68 | AQBCg41oz5uTWWOUXWKoFLt59AnPberlhr2a/Cjim8X63+lQsKwoTHr/OGcam+aq 69 | jb3aPW/oSfryfvrdS6hNBu4cR/82mSMkm0wZ0fQchceUhFQZsHhv11QlqNqxGHYb 70 | cRWtNdVktm/ugG2CmHG427vlAdr38aqXp674kLb2rDj6nqIVEC/Kxeu/kWe89+4q 71 | DoA1rW0DIJhP3zlbrPLvQwGVuQp7KbRtg94flKZpa0IY78vmaGayeVn4rp+JJezu 72 | Rvk8NKt0fXIKPzluByh55YyLOtofuXyvDAoMcT2qdodpTHWaC4+OEPjWU8k1ixBC 73 | CjIBpYPjIg6GwB2Hda/LMMsa 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /features/authentication/oauth2-token-auth/server/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpgIBAAKCAQEA4pOMQ2TpxgJr0ZjzeUfVmcZGqG0kzKDYO+zk0IN5QvW/I+EO 3 | NLPxNcYnbkRYSMqRQoxsL2ydCdAbc9hC9HBxbED3bHFjOdvR+PbU+LcYA3p9Mgw/ 4 | 9P0KTOOtcTOL5iVTUthyZ59WG37UtycoDh0okraUCU+9Ue8GvIA5l8vSIIwM/88z 5 | l316DgHnkKIwZDS+PnjZPxl7J/zBsg0HNItuLgtQ9Iq+B2OYJSQAl4nSrZbNVgGE 6 | 39yvvqgS7/h3dUYtSSGuHLyxh5RNjrddfAEKxuYyMkfD0TirXQJPcsUQnKKDne6h 7 | N+4Cu1CKeLq/9/dpsP8XMRRopdsF76suU6yu3wIDAQABAoIBAQCjrPDjco/KAc+/ 8 | ft1LnI/6YRiD7SxrQjpSt+PnmUJNE9e7ZIXtnpu+O+IaLvcTxnm++E/ixnR/NT3P 9 | psdfa6cUC65xQUvr7Rc24aCh9yo6wQ6Vy/Gb2fvJ5aNSpmkGnaoeq8uhfaInhKzH 10 | jlrKL1gy+//e5iKegKx+GacBODUYWpc8bVoROpvqgtSDg1wtuwUU/5fy6f8IIHuT 11 | 78cPm6t39ylcFkV3lt1yS4LhvUoW2Nlm5SHOicDc/cYVE8xqfJlARzN9xUfM+5LB 12 | sUdbR5UZOxOPsd24q1D1DZc1s34soj/wKXPVyyuhp0o7Db6vSsxQDfOetfuRUSR6 13 | +5zPMLqxAoGBAP6laEBin8SVThAvQmS4PoyqPK8Hb0GgjxPGxyXyrtSL3/Xy86E0 14 | vhOGTm8gh2oA0wZQ3KIg5TIhVa3G+UNMbZmIuYCQdeAQpl/sdMnpw66aNtHj6kt7 15 | gqzWzH1en4w2T3DxU7LtCQIx4q3Akv0oyZ/YovlNb5dkSHtuz4ZVRYkXAoGBAOPH 16 | 74KHzkD/IWkGf7/QG9XQ93hjggxmDS/WzeKwKYewPyZoWRvkvj7i7d+P1AUrCvgd 17 | YeB132S0S1JrSUP4Fn+DNwgj3Kyg2Fq2/HmwTKkqVPjOKlonTKlA+h3fNG/NbJDE 18 | E41yFkK+zjSbxxcAYLCgR2A9PCEyXPXyV20U8BV5AoGBAJnq5unL8yBC0u2Lc0kn 19 | 6H7jw0xUZRY482KTyvoQB0bnyRaDpGkzVRS+IJihA9i56NOverzwvzie14fzdeUM 20 | xE6CSwX/y5AE4FuotCr7hlD6W9pgNdUsMZ9BMlcxI6T/iuMMq3fCOKi/+HDnrrEg 21 | v0ZEDrY77RCICBu7repXjnE/AoGBAIdBvhOAmRU3apt25Hz+EslQoOK4FA1QvBvg 22 | LbmiacbM/XLNG7zYg6/MCPxr57Z57LWQnQIwfErMVL3IP2VA9/sX66HFydAoYtDb 23 | P+jyq1L4dCSaJ8QI+hi3IM6EMBsDnKgKBqJDULypmMDcj8g0zTWUt02Kjx4XTeQt 24 | 14RKnpXhAoGBAJkImhczkTucb/Yhaw1du2IkylPSb+HAJH6NFH9LFNvKYOIFfUaA 25 | id8ZkR5WGDJiK7vm3QT4n6j14/CnTXqrQ52W9ixxOIjbd1AHgHNwlbQGJodInT+l 26 | j3Iqa44peXerIk0MO8qYEjEGybHu7DhZhkbZoCn1Qqn+XpDbx2bbPe7w 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /features/authentication/token-auth/client/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3TCCAsWgAwIBAgIJAKoKZuLTxD+eMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD 3 | VQQGEwJDTjESMBAGA1UECAwJR3VhbmdEb25nMREwDwYDVQQHDAhTaGVuWmhlbjEd 4 | MBsGA1UECgwUTWFkbWFsbHMuY29tIENvLixMdGQxHTAbBgNVBAsMFE1hZG1hbGxz 5 | LmNvbSBSb290IENBMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDUyOTA3MjMwM1oX 6 | DTI5MDUyNjA3MjMwM1owgYQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0Rv 7 | bmcxETAPBgNVBAcMCFNoZW5aaGVuMR0wGwYDVQQKDBRNYWRtYWxscy5jb20gQ28u 8 | LEx0ZDEdMBsGA1UECwwUTWFkbWFsbHMuY29tIFJvb3QgQ0ExEDAOBgNVBAMMB1Jv 9 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsaZ3C8XzVTfir 10 | 2LZpJMMnxo2ZUSRcjrVRav3ATtcubqE94XT/nq5wrUClhvOWKeUIFaPZHY5dXSLm 11 | LAIRaCNW6t0cHcfCyd6ArVbjVCT8dbo1wwis2ATFRVRiVmuFjPSu2iLR/pm9m94Q 12 | C1uZe4ypXGW08TieivNiRTflfXNvjKzDENJriEQ6ukhwAixOLZcbYKZKi4wZXscl 13 | iaBcitcCZty0ODtH7TXQbrQNU2lkQBT/HmtABgL4ln5p+tiDUTNCJjvPeNBT4FTs 14 | KLtcnJM4NJspRwHO4YIGGHhmgJhneSPsHm5poE6pVu3DDRFHuforPv/k70cX9PGz 15 | OQlREfNRAgMBAAGjUDBOMB0GA1UdDgQWBBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAf 16 | BgNVHSMEGDAWgBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAMBgNVHRMEBTADAQH/MA0G 17 | CSqGSIb3DQEBCwUAA4IBAQCfcXII5evHWW5qcF3f2wMRCFhizOCojQ6CA2mwH9LN 18 | GsZRclTwniQ7OqH67b94ii3mF3dUK7klth0jbx6rJ9jYsGZ6dCuK6a1MWtCey8P4 19 | VEbiBA2qXwjJzeap1G9mdPz16YTRlbdLhJDgN7fxmTK+A4GOObiq8x2EX9DjjCym 20 | fI39D0Z9o/2i29KZGC25DWYvjnLrz3EjfGkct1QrQG9q93pwQNKm+SKHRoUIOExe 21 | KsmgS0jPxie/IUWSDHlkCY19tHSX2swRNjkiomt1pKOozN3eTu1qs29f2HA/tKAw 22 | 6/Hwdo0XD+ybVEHq/STsppCe4zMojmChxuDVTYo2de3C 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /features/authentication/token-auth/client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | 10 | "google.golang.org/grpc/credentials" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 13 | "google.golang.org/grpc" 14 | ) 15 | 16 | type tokenAuth struct { 17 | token string 18 | } 19 | 20 | // Return value is mapped to request headers. 21 | func (t *tokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { 22 | return map[string]string{ 23 | "authorization": "Bearer " + t.token, 24 | }, nil 25 | } 26 | 27 | // 是否使用 TLS 安全加密 28 | func (t *tokenAuth) RequireTransportSecurity() bool { 29 | return true 30 | } 31 | 32 | func main() { 33 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 34 | certFile := flag.String("cacert", "cacert.pem", "CA root certificate") 35 | flag.Parse() 36 | 37 | creds, err := credentials.NewClientTLSFromFile(*certFile, "") 38 | if err != nil { 39 | log.Fatalf("failed to load CA root certificate: %v", err) 40 | } 41 | 42 | opts := []grpc.DialOption{ 43 | // 1. TLS 认证 44 | grpc.WithTransportCredentials(creds), 45 | // 2. token 认证 46 | grpc.WithPerRPCCredentials(&tokenAuth{ 47 | token: "some-secret-token", 48 | }), 49 | } 50 | 51 | grpc.WithPerRPCCredentials(creds) 52 | // Set up a connection to the server. 53 | conn, err := grpc.Dial(*addr, opts...) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 54 | if err != nil { 55 | log.Fatalf("did not connect: %v", err) 56 | } 57 | defer conn.Close() 58 | 59 | c := pb.NewEchoClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewEchoClient method provided in the pb package we generated from our .proto. 60 | 61 | // Contact the server and print out its response. 62 | msg := "madmalls.com" 63 | resp, err := c.UnaryEcho(context.Background(), &pb.EchoRequest{Message: msg}) // Now let’s look at how we call our service methods. Note that in gRPC-Go, RPCs operate in a blocking/synchronous mode, which means that the RPC call waits for the server to respond, and will either return a response or an error. 64 | if err != nil { 65 | log.Fatalf("failed to call UnaryEcho: %v", err) 66 | } 67 | fmt.Printf("response:\n") 68 | fmt.Printf(" - %q\n", resp.GetMessage()) 69 | } 70 | -------------------------------------------------------------------------------- /features/authentication/token-auth/server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net" 10 | "strings" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 13 | "google.golang.org/grpc" 14 | "google.golang.org/grpc/codes" 15 | "google.golang.org/grpc/credentials" 16 | "google.golang.org/grpc/metadata" 17 | "google.golang.org/grpc/status" 18 | ) 19 | 20 | // server is used to implement echopb.EchoServer. 21 | type server struct{} 22 | 23 | /* 24 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 25 | return nil, status.Errorf(codes.Unimplemented, "method UnaryEcho not implemented") 26 | } 27 | */ 28 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 29 | fmt.Printf("--- gRPC Unary RPC ---\n") 30 | fmt.Printf("request received: %v\n", req) 31 | 32 | // md 的值类似于: map[:authority:[192.168.40.123:50051] authorization:[Bearer some-secret-token] content-type:[application/grpc] user-agent:[grpc-go/1.20.1]] 33 | md, ok := metadata.FromIncomingContext(ctx) 34 | if !ok { 35 | return nil, status.Errorf(codes.InvalidArgument, "missing metadata") 36 | } 37 | // fmt.Printf("Type of 'metadata.MD' is %T, and its value is %v \n", md, md) 38 | 39 | // 1. 判断是否存在 authorization 请求头 40 | authorization, ok := md["authorization"] 41 | if !ok { 42 | return nil, status.Errorf(codes.Unauthenticated, `missing "Authorization" header`) 43 | } 44 | // fmt.Printf("Type of 'authorization' is %T, and its value is %v \n", authorization, authorization) 45 | 46 | const prefix = "Bearer " 47 | 48 | // 2. 如果存在 authorization 请求头的话,则 md["authorization"] 是一个 []string 49 | if !strings.HasPrefix(authorization[0], prefix) { 50 | return nil, status.Errorf(codes.Unauthenticated, `missing "Bearer " prefix in "Authorization" header`) 51 | } 52 | 53 | // 3. 验证 token 是否一致 54 | if token := strings.TrimPrefix(authorization[0], prefix); token != "some-secret-token" { 55 | return nil, status.Errorf(codes.Unauthenticated, "invalid token") 56 | } 57 | 58 | return &pb.EchoResponse{Message: req.GetMessage()}, nil 59 | } 60 | 61 | func (s *server) ServerStreamingEcho(req *pb.EchoRequest, stream pb.Echo_ServerStreamingEchoServer) error { 62 | return status.Errorf(codes.Unimplemented, "method ServerStreamingEcho not implemented") 63 | } 64 | 65 | func (s *server) ClientStreamingEcho(stream pb.Echo_ClientStreamingEchoServer) error { 66 | return status.Errorf(codes.Unimplemented, "method ClientStreamingEcho not implemented") 67 | } 68 | 69 | func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { 70 | return status.Errorf(codes.Unimplemented, "method BidirectionalStreamingEcho not implemented") 71 | } 72 | 73 | func main() { 74 | port := flag.Int("port", 50051, "the port to serve on") 75 | certFile := flag.String("certfile", "server.crt", "Server certificate") 76 | keyFile := flag.String("keyfile", "server.key", "Server private key") 77 | flag.Parse() 78 | 79 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests 80 | if err != nil { 81 | log.Fatalf("failed to listen: %v", err) 82 | } 83 | fmt.Printf("server listening at %v\n", lis.Addr()) 84 | 85 | creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) 86 | if err != nil { 87 | log.Fatalf("failed to load certificates: %v", err) 88 | } 89 | 90 | s := grpc.NewServer(grpc.Creds(creds)) // Create an instance of the gRPC server 91 | 92 | pb.RegisterEchoServer(s, &server{}) // Register our service implementation with the gRPC server 93 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 94 | log.Fatalf("failed to serve: %v", err) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /features/authentication/token-auth/server/server.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 (0x5) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: C=CN, ST=GuangDong, L=ShenZhen, O=Madmalls.com Co.,Ltd, OU=Madmalls.com Root CA, CN=Root CA 7 | Validity 8 | Not Before: Jun 11 07:22:30 2019 GMT 9 | Not After : Jun 10 07:22:30 2020 GMT 10 | Subject: C=CN, ST=GuangDong, O=Madmalls.com Co.,Ltd, OU=gRPC Server, CN=*.wangy.com 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | Public-Key: (2048 bit) 14 | Modulus: 15 | 00:e2:93:8c:43:64:e9:c6:02:6b:d1:98:f3:79:47: 16 | d5:99:c6:46:a8:6d:24:cc:a0:d8:3b:ec:e4:d0:83: 17 | 79:42:f5:bf:23:e1:0e:34:b3:f1:35:c6:27:6e:44: 18 | 58:48:ca:91:42:8c:6c:2f:6c:9d:09:d0:1b:73:d8: 19 | 42:f4:70:71:6c:40:f7:6c:71:63:39:db:d1:f8:f6: 20 | d4:f8:b7:18:03:7a:7d:32:0c:3f:f4:fd:0a:4c:e3: 21 | ad:71:33:8b:e6:25:53:52:d8:72:67:9f:56:1b:7e: 22 | d4:b7:27:28:0e:1d:28:92:b6:94:09:4f:bd:51:ef: 23 | 06:bc:80:39:97:cb:d2:20:8c:0c:ff:cf:33:97:7d: 24 | 7a:0e:01:e7:90:a2:30:64:34:be:3e:78:d9:3f:19: 25 | 7b:27:fc:c1:b2:0d:07:34:8b:6e:2e:0b:50:f4:8a: 26 | be:07:63:98:25:24:00:97:89:d2:ad:96:cd:56:01: 27 | 84:df:dc:af:be:a8:12:ef:f8:77:75:46:2d:49:21: 28 | ae:1c:bc:b1:87:94:4d:8e:b7:5d:7c:01:0a:c6:e6: 29 | 32:32:47:c3:d1:38:ab:5d:02:4f:72:c5:10:9c:a2: 30 | 83:9d:ee:a1:37:ee:02:bb:50:8a:78:ba:bf:f7:f7: 31 | 69:b0:ff:17:31:14:68:a5:db:05:ef:ab:2e:53:ac: 32 | ae:df 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Subject Alternative Name: 36 | DNS:www.wangy.com, DNS:blog.wangy.com, IP Address:192.168.40.123 37 | Signature Algorithm: sha256WithRSAEncryption 38 | 42:83:8d:68:cf:9b:93:59:63:94:5d:62:a8:14:bb:79:f4:09: 39 | cf:6d:ea:e5:86:bd:9a:fc:28:e2:9b:c5:fa:df:e9:50:b0:ac: 40 | 28:4c:7a:ff:38:67:1a:9b:e6:aa:8d:bd:da:3d:6f:e8:49:fa: 41 | f2:7e:fa:dd:4b:a8:4d:06:ee:1c:47:ff:36:99:23:24:9b:4c: 42 | 19:d1:f4:1c:85:c7:94:84:54:19:b0:78:6f:d7:54:25:a8:da: 43 | b1:18:76:1b:71:15:ad:35:d5:64:b6:6f:ee:80:6d:82:98:71: 44 | b8:db:bb:e5:01:da:f7:f1:aa:97:a7:ae:f8:90:b6:f6:ac:38: 45 | fa:9e:a2:15:10:2f:ca:c5:eb:bf:91:67:bc:f7:ee:2a:0e:80: 46 | 35:ad:6d:03:20:98:4f:df:39:5b:ac:f2:ef:43:01:95:b9:0a: 47 | 7b:29:b4:6d:83:de:1f:94:a6:69:6b:42:18:ef:cb:e6:68:66: 48 | b2:79:59:f8:ae:9f:89:25:ec:ee:46:f9:3c:34:ab:74:7d:72: 49 | 0a:3f:39:6e:07:28:79:e5:8c:8b:3a:da:1f:b9:7c:af:0c:0a: 50 | 0c:71:3d:aa:76:87:69:4c:75:9a:0b:8f:8e:10:f8:d6:53:c9: 51 | 35:8b:10:42:0a:32:01:a5:83:e3:22:0e:86:c0:1d:87:75:af: 52 | cb:30:cb:1a 53 | -----BEGIN CERTIFICATE----- 54 | MIIDnjCCAoagAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMCQ04x 55 | EjAQBgNVBAgMCUd1YW5nRG9uZzERMA8GA1UEBwwIU2hlblpoZW4xHTAbBgNVBAoM 56 | FE1hZG1hbGxzLmNvbSBDby4sTHRkMR0wGwYDVQQLDBRNYWRtYWxscy5jb20gUm9v 57 | dCBDQTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xOTA2MTEwNzIyMzBaFw0yMDA2MTAw 58 | NzIyMzBaMGwxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxHTAbBgNV 59 | BAoMFE1hZG1hbGxzLmNvbSBDby4sTHRkMRQwEgYDVQQLDAtnUlBDIFNlcnZlcjEU 60 | MBIGA1UEAwwLKi53YW5neS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 61 | AoIBAQDik4xDZOnGAmvRmPN5R9WZxkaobSTMoNg77OTQg3lC9b8j4Q40s/E1xidu 62 | RFhIypFCjGwvbJ0J0Btz2EL0cHFsQPdscWM529H49tT4txgDen0yDD/0/QpM461x 63 | M4vmJVNS2HJnn1YbftS3JygOHSiStpQJT71R7wa8gDmXy9IgjAz/zzOXfXoOAeeQ 64 | ojBkNL4+eNk/GXsn/MGyDQc0i24uC1D0ir4HY5glJACXidKtls1WAYTf3K++qBLv 65 | +Hd1Ri1JIa4cvLGHlE2Ot118AQrG5jIyR8PROKtdAk9yxRCcooOd7qE37gK7UIp4 66 | ur/392mw/xcxFGil2wXvqy5TrK7fAgMBAAGjMjAwMC4GA1UdEQQnMCWCDXd3dy53 67 | YW5neS5jb22CDmJsb2cud2FuZ3kuY29thwTAqCh7MA0GCSqGSIb3DQEBCwUAA4IB 68 | AQBCg41oz5uTWWOUXWKoFLt59AnPberlhr2a/Cjim8X63+lQsKwoTHr/OGcam+aq 69 | jb3aPW/oSfryfvrdS6hNBu4cR/82mSMkm0wZ0fQchceUhFQZsHhv11QlqNqxGHYb 70 | cRWtNdVktm/ugG2CmHG427vlAdr38aqXp674kLb2rDj6nqIVEC/Kxeu/kWe89+4q 71 | DoA1rW0DIJhP3zlbrPLvQwGVuQp7KbRtg94flKZpa0IY78vmaGayeVn4rp+JJezu 72 | Rvk8NKt0fXIKPzluByh55YyLOtofuXyvDAoMcT2qdodpTHWaC4+OEPjWU8k1ixBC 73 | CjIBpYPjIg6GwB2Hda/LMMsa 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /features/authentication/token-auth/server/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpgIBAAKCAQEA4pOMQ2TpxgJr0ZjzeUfVmcZGqG0kzKDYO+zk0IN5QvW/I+EO 3 | NLPxNcYnbkRYSMqRQoxsL2ydCdAbc9hC9HBxbED3bHFjOdvR+PbU+LcYA3p9Mgw/ 4 | 9P0KTOOtcTOL5iVTUthyZ59WG37UtycoDh0okraUCU+9Ue8GvIA5l8vSIIwM/88z 5 | l316DgHnkKIwZDS+PnjZPxl7J/zBsg0HNItuLgtQ9Iq+B2OYJSQAl4nSrZbNVgGE 6 | 39yvvqgS7/h3dUYtSSGuHLyxh5RNjrddfAEKxuYyMkfD0TirXQJPcsUQnKKDne6h 7 | N+4Cu1CKeLq/9/dpsP8XMRRopdsF76suU6yu3wIDAQABAoIBAQCjrPDjco/KAc+/ 8 | ft1LnI/6YRiD7SxrQjpSt+PnmUJNE9e7ZIXtnpu+O+IaLvcTxnm++E/ixnR/NT3P 9 | psdfa6cUC65xQUvr7Rc24aCh9yo6wQ6Vy/Gb2fvJ5aNSpmkGnaoeq8uhfaInhKzH 10 | jlrKL1gy+//e5iKegKx+GacBODUYWpc8bVoROpvqgtSDg1wtuwUU/5fy6f8IIHuT 11 | 78cPm6t39ylcFkV3lt1yS4LhvUoW2Nlm5SHOicDc/cYVE8xqfJlARzN9xUfM+5LB 12 | sUdbR5UZOxOPsd24q1D1DZc1s34soj/wKXPVyyuhp0o7Db6vSsxQDfOetfuRUSR6 13 | +5zPMLqxAoGBAP6laEBin8SVThAvQmS4PoyqPK8Hb0GgjxPGxyXyrtSL3/Xy86E0 14 | vhOGTm8gh2oA0wZQ3KIg5TIhVa3G+UNMbZmIuYCQdeAQpl/sdMnpw66aNtHj6kt7 15 | gqzWzH1en4w2T3DxU7LtCQIx4q3Akv0oyZ/YovlNb5dkSHtuz4ZVRYkXAoGBAOPH 16 | 74KHzkD/IWkGf7/QG9XQ93hjggxmDS/WzeKwKYewPyZoWRvkvj7i7d+P1AUrCvgd 17 | YeB132S0S1JrSUP4Fn+DNwgj3Kyg2Fq2/HmwTKkqVPjOKlonTKlA+h3fNG/NbJDE 18 | E41yFkK+zjSbxxcAYLCgR2A9PCEyXPXyV20U8BV5AoGBAJnq5unL8yBC0u2Lc0kn 19 | 6H7jw0xUZRY482KTyvoQB0bnyRaDpGkzVRS+IJihA9i56NOverzwvzie14fzdeUM 20 | xE6CSwX/y5AE4FuotCr7hlD6W9pgNdUsMZ9BMlcxI6T/iuMMq3fCOKi/+HDnrrEg 21 | v0ZEDrY77RCICBu7repXjnE/AoGBAIdBvhOAmRU3apt25Hz+EslQoOK4FA1QvBvg 22 | LbmiacbM/XLNG7zYg6/MCPxr57Z57LWQnQIwfErMVL3IP2VA9/sX66HFydAoYtDb 23 | P+jyq1L4dCSaJ8QI+hi3IM6EMBsDnKgKBqJDULypmMDcj8g0zTWUt02Kjx4XTeQt 24 | 14RKnpXhAoGBAJkImhczkTucb/Yhaw1du2IkylPSb+HAJH6NFH9LFNvKYOIFfUaA 25 | id8ZkR5WGDJiK7vm3QT4n6j14/CnTXqrQ52W9ixxOIjbd1AHgHNwlbQGJodInT+l 26 | j3Iqa44peXerIk0MO8qYEjEGybHu7DhZhkbZoCn1Qqn+XpDbx2bbPe7w 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /features/cancel/client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "time" 10 | 11 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/codes" 14 | "google.golang.org/grpc/status" 15 | ) 16 | 17 | func sendMessage(stream pb.Echo_BidirectionalStreamingEchoClient, msg string) error { 18 | fmt.Printf("sending message %q\n", msg) 19 | return stream.Send(&pb.EchoRequest{Message: msg}) 20 | } 21 | 22 | func recvMessage(stream pb.Echo_BidirectionalStreamingEchoClient, wantErrCode codes.Code) { 23 | res, err := stream.Recv() 24 | if status.Code(err) != wantErrCode { 25 | log.Fatalf("stream.Recv() = %v, %v; want=%v, status.Code(err)=%v", res, err, wantErrCode, status.Code(err)) 26 | } 27 | if err != nil { 28 | fmt.Printf("stream.Recv() returned expected error %v\n", err) 29 | return 30 | } 31 | fmt.Printf("received message %q\n", res.GetMessage()) 32 | } 33 | 34 | func main() { 35 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 36 | flag.Parse() 37 | 38 | // Set up a connection to the server. 39 | conn, err := grpc.Dial(*addr, grpc.WithInsecure()) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 40 | if err != nil { 41 | log.Fatalf("did not connect: %v", err) 42 | } 43 | defer conn.Close() 44 | 45 | c := pb.NewEchoClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewEchoClient method provided in the pb package we generated from our .proto. 46 | 47 | // Initiate the stream with a context that supports cancellation. 48 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 49 | stream, err := c.BidirectionalStreamingEcho(ctx) 50 | if err != nil { 51 | log.Fatalf("error creating stream: %v", err) 52 | } 53 | 54 | // Send some test messages. 55 | if err := sendMessage(stream, "hello"); err != nil { 56 | log.Fatalf("error sending on stream: %v", err) 57 | } 58 | if err := sendMessage(stream, "world"); err != nil { 59 | log.Fatalf("error sending on stream: %v", err) 60 | } 61 | 62 | // Ensure the RPC is working. 63 | recvMessage(stream, codes.OK) 64 | recvMessage(stream, codes.OK) 65 | 66 | fmt.Println("cancelling context") 67 | cancel() 68 | 69 | // This Send may or may not return an error, depending on whether the 70 | // monitored context detects cancellation before the call is made. 71 | sendMessage(stream, "closed") 72 | 73 | // This Recv should never succeed. 74 | recvMessage(stream, codes.Canceled) 75 | } 76 | -------------------------------------------------------------------------------- /features/cancel/server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "net" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 13 | "google.golang.org/grpc" 14 | "google.golang.org/grpc/codes" 15 | "google.golang.org/grpc/status" 16 | ) 17 | 18 | // server is used to implement echopb.EchoServer. 19 | type server struct{} 20 | 21 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 22 | return nil, status.Errorf(codes.Unimplemented, "method UnaryEcho not implemented") 23 | } 24 | 25 | func (s *server) ServerStreamingEcho(req *pb.EchoRequest, stream pb.Echo_ServerStreamingEchoServer) error { 26 | return status.Errorf(codes.Unimplemented, "method ServerStreamingEcho not implemented") 27 | } 28 | 29 | func (s *server) ClientStreamingEcho(stream pb.Echo_ClientStreamingEchoServer) error { 30 | return status.Errorf(codes.Unimplemented, "method ClientStreamingEcho not implemented") 31 | } 32 | 33 | func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { 34 | for { 35 | in, err := stream.Recv() 36 | if err != nil { 37 | fmt.Printf("server: error receiving from stream: %v\n", err) 38 | if err == io.EOF { 39 | return nil 40 | } 41 | return err 42 | } 43 | fmt.Printf("echoing message %q\n", in.Message) 44 | stream.Send(&pb.EchoResponse{Message: in.Message}) 45 | } 46 | } 47 | 48 | func main() { 49 | port := flag.Int("port", 50051, "the port to serve on") 50 | flag.Parse() 51 | 52 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests 53 | if err != nil { 54 | log.Fatalf("failed to listen: %v", err) 55 | } 56 | fmt.Printf("server listening at %v\n", lis.Addr()) 57 | 58 | s := grpc.NewServer() // Create an instance of the gRPC server 59 | pb.RegisterEchoServer(s, &server{}) // Register our service implementation with the gRPC server 60 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 61 | log.Fatalf("failed to serve: %v", err) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /features/deadline/client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 13 | "google.golang.org/grpc" 14 | "google.golang.org/grpc/codes" 15 | "google.golang.org/grpc/status" 16 | ) 17 | 18 | func unaryCallWithDeadline(c pb.EchoClient, timeout time.Duration, msg string) { 19 | fmt.Printf("--- gRPC Unary RPC Call ---\n") 20 | // Make unary RPC 21 | // ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout)) 22 | ctx, cancel := context.WithTimeout(context.Background(), timeout) 23 | defer cancel() 24 | 25 | req := &pb.EchoRequest{Message: msg} 26 | resp, err := c.UnaryEcho(ctx, req) 27 | if err != nil { 28 | // Error Handling 29 | errStatus, ok := status.FromError(err) 30 | if ok { 31 | if codes.DeadlineExceeded == errStatus.Code() { // take specific action based on specific error 32 | fmt.Println("Error: Deadline was exceeded") 33 | } 34 | } 35 | // Otherwise, ok is false and a Status is returned with codes.Unknown and the original error message 36 | fmt.Printf("Error Code: %v\n", errStatus.Code()) 37 | fmt.Printf("Error Description: %v\n\n", errStatus.Message()) 38 | } 39 | 40 | fmt.Printf("response:\n") 41 | fmt.Printf(" - %q\n", resp.GetMessage()) 42 | } 43 | 44 | func main() { 45 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 46 | flag.Parse() 47 | 48 | // Set up a connection to the server. 49 | conn, err := grpc.Dial(*addr, grpc.WithInsecure()) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 50 | if err != nil { 51 | log.Fatalf("did not connect: %v", err) 52 | } 53 | defer conn.Close() 54 | 55 | c := pb.NewEchoClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewEchoClient method provided in the pb package we generated from our .proto. 56 | 57 | // Contact the server and print out its response. 58 | msg := "Madman" 59 | if len(os.Args) > 1 { 60 | msg = os.Args[1] 61 | } 62 | 63 | // 1. succeed 64 | unaryCallWithDeadline(c, 5*time.Second, msg) 65 | fmt.Println() 66 | 67 | // 2. failed 68 | unaryCallWithDeadline(c, 1*time.Second, msg) 69 | } 70 | -------------------------------------------------------------------------------- /features/deadline/server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net" 10 | "time" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 13 | "google.golang.org/grpc" 14 | "google.golang.org/grpc/codes" 15 | "google.golang.org/grpc/status" 16 | ) 17 | 18 | // server is used to implement echopb.EchoServer. 19 | type server struct{} 20 | 21 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 22 | fmt.Printf("--- gRPC Unary RPC ---\n") 23 | fmt.Printf("request received: %v\n", req) 24 | 25 | // Suppose it takes a long time to process (3 seconds) 26 | for i := 0; i < 3; i++ { 27 | if ctx.Err() == context.Canceled { 28 | // the client canceled the request 29 | fmt.Println("The client canceled the request!") 30 | return nil, status.Errorf(codes.Canceled, "The client canceled the request") 31 | } 32 | time.Sleep(1 * time.Second) 33 | } 34 | return &pb.EchoResponse{Message: req.GetMessage()}, nil 35 | } 36 | 37 | func (s *server) ServerStreamingEcho(req *pb.EchoRequest, stream pb.Echo_ServerStreamingEchoServer) error { 38 | return status.Errorf(codes.Unimplemented, "method ServerStreamingEcho not implemented") 39 | } 40 | 41 | func (s *server) ClientStreamingEcho(stream pb.Echo_ClientStreamingEchoServer) error { 42 | return status.Errorf(codes.Unimplemented, "method ClientStreamingEcho not implemented") 43 | } 44 | 45 | func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { 46 | return status.Errorf(codes.Unimplemented, "method BidirectionalStreamingEcho not implemented") 47 | } 48 | 49 | func main() { 50 | port := flag.Int("port", 50051, "the port to serve on") 51 | flag.Parse() 52 | 53 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests 54 | if err != nil { 55 | log.Fatalf("failed to listen: %v", err) 56 | } 57 | fmt.Printf("server listening at %v\n", lis.Addr()) 58 | 59 | s := grpc.NewServer() // Create an instance of the gRPC server 60 | pb.RegisterEchoServer(s, &server{}) // Register our service implementation with the gRPC server 61 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 62 | log.Fatalf("failed to serve: %v", err) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /features/echopb/echo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package="echopb"; 4 | 5 | package echo; 6 | 7 | // EchoRequest is the request for echo. 8 | message EchoRequest { 9 | string message = 1; 10 | } 11 | 12 | // EchoResponse is the response for echo. 13 | message EchoResponse { 14 | string message = 1; 15 | } 16 | 17 | // Echo is the echo service. 18 | service Echo { 19 | // UnaryEcho is unary echo. 20 | rpc UnaryEcho(EchoRequest) returns (EchoResponse) {} 21 | // ServerStreamingEcho is server side streaming. 22 | rpc ServerStreamingEcho(EchoRequest) returns (stream EchoResponse) {} 23 | // ClientStreamingEcho is client side streaming. 24 | rpc ClientStreamingEcho(stream EchoRequest) returns (EchoResponse) {} 25 | // BidirectionalStreamingEcho is bidi streaming. 26 | rpc BidirectionalStreamingEcho(stream EchoRequest) returns (stream EchoResponse) {} 27 | } -------------------------------------------------------------------------------- /features/echopb/protoc-gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | protoc --go_out=plugins=grpc:. *.proto -------------------------------------------------------------------------------- /features/errors/client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "os" 10 | 11 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/codes" 14 | "google.golang.org/grpc/status" 15 | ) 16 | 17 | func main() { 18 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 19 | flag.Parse() 20 | 21 | // Set up a connection to the server. 22 | conn, err := grpc.Dial(*addr, grpc.WithInsecure()) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 23 | if err != nil { 24 | log.Fatalf("did not connect: %v", err) 25 | } 26 | defer conn.Close() 27 | 28 | c := pb.NewEchoClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewEchoClient method provided in the pb package we generated from our .proto. 29 | 30 | // Contact the server and print out its response. 31 | msg := "Madman" 32 | if len(os.Args) > 1 { 33 | msg = os.Args[1] 34 | } 35 | 36 | resp, err := c.UnaryEcho(context.Background(), &pb.EchoRequest{Message: msg}) // Now let’s look at how we call our service methods. Note that in gRPC-Go, RPCs operate in a blocking/synchronous mode, which means that the RPC call waits for the server to respond, and will either return a response or an error. 37 | if err != nil { 38 | // log.Fatalf("failed to call UnaryEcho: %v", err) 39 | errStatus, _ := status.FromError(err) 40 | fmt.Printf("Error Code: %v\n", errStatus.Code()) 41 | fmt.Printf("Error Description: %v\n\n", errStatus.Message()) 42 | 43 | if codes.InvalidArgument == errStatus.Code() { 44 | fmt.Println("You can take specific action based on specific error!") 45 | } 46 | } 47 | fmt.Printf("response:\n") 48 | fmt.Printf(" - %q\n", resp.GetMessage()) 49 | } 50 | -------------------------------------------------------------------------------- /features/errors/server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net" 10 | 11 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/codes" 14 | "google.golang.org/grpc/status" 15 | ) 16 | 17 | // server is used to implement echopb.EchoServer. 18 | type server struct{} 19 | 20 | /* 21 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 22 | return nil, status.Errorf(codes.Unimplemented, "method UnaryEcho not implemented") 23 | } 24 | */ 25 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 26 | fmt.Printf("--- gRPC Unary RPC ---\n") 27 | fmt.Printf("request received: %v\n", req) 28 | 29 | if len(req.GetMessage()) > 10 { 30 | return nil, status.Errorf(codes.InvalidArgument, "Length of `Message` cannot be more than 10 characters") 31 | } 32 | return &pb.EchoResponse{Message: req.GetMessage()}, nil 33 | } 34 | 35 | func (s *server) ServerStreamingEcho(req *pb.EchoRequest, stream pb.Echo_ServerStreamingEchoServer) error { 36 | return status.Errorf(codes.Unimplemented, "method ServerStreamingEcho not implemented") 37 | } 38 | 39 | func (s *server) ClientStreamingEcho(stream pb.Echo_ClientStreamingEchoServer) error { 40 | return status.Errorf(codes.Unimplemented, "method ClientStreamingEcho not implemented") 41 | } 42 | 43 | func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { 44 | return status.Errorf(codes.Unimplemented, "method BidirectionalStreamingEcho not implemented") 45 | } 46 | 47 | func main() { 48 | port := flag.Int("port", 50051, "the port to serve on") 49 | flag.Parse() 50 | 51 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests 52 | if err != nil { 53 | log.Fatalf("failed to listen: %v", err) 54 | } 55 | fmt.Printf("server listening at %v\n", lis.Addr()) 56 | 57 | s := grpc.NewServer() // Create an instance of the gRPC server 58 | pb.RegisterEchoServer(s, &server{}) // Register our service implementation with the gRPC server 59 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 60 | log.Fatalf("failed to serve: %v", err) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /features/interceptor/client/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3TCCAsWgAwIBAgIJAKoKZuLTxD+eMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD 3 | VQQGEwJDTjESMBAGA1UECAwJR3VhbmdEb25nMREwDwYDVQQHDAhTaGVuWmhlbjEd 4 | MBsGA1UECgwUTWFkbWFsbHMuY29tIENvLixMdGQxHTAbBgNVBAsMFE1hZG1hbGxz 5 | LmNvbSBSb290IENBMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDUyOTA3MjMwM1oX 6 | DTI5MDUyNjA3MjMwM1owgYQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0Rv 7 | bmcxETAPBgNVBAcMCFNoZW5aaGVuMR0wGwYDVQQKDBRNYWRtYWxscy5jb20gQ28u 8 | LEx0ZDEdMBsGA1UECwwUTWFkbWFsbHMuY29tIFJvb3QgQ0ExEDAOBgNVBAMMB1Jv 9 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsaZ3C8XzVTfir 10 | 2LZpJMMnxo2ZUSRcjrVRav3ATtcubqE94XT/nq5wrUClhvOWKeUIFaPZHY5dXSLm 11 | LAIRaCNW6t0cHcfCyd6ArVbjVCT8dbo1wwis2ATFRVRiVmuFjPSu2iLR/pm9m94Q 12 | C1uZe4ypXGW08TieivNiRTflfXNvjKzDENJriEQ6ukhwAixOLZcbYKZKi4wZXscl 13 | iaBcitcCZty0ODtH7TXQbrQNU2lkQBT/HmtABgL4ln5p+tiDUTNCJjvPeNBT4FTs 14 | KLtcnJM4NJspRwHO4YIGGHhmgJhneSPsHm5poE6pVu3DDRFHuforPv/k70cX9PGz 15 | OQlREfNRAgMBAAGjUDBOMB0GA1UdDgQWBBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAf 16 | BgNVHSMEGDAWgBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAMBgNVHRMEBTADAQH/MA0G 17 | CSqGSIb3DQEBCwUAA4IBAQCfcXII5evHWW5qcF3f2wMRCFhizOCojQ6CA2mwH9LN 18 | GsZRclTwniQ7OqH67b94ii3mF3dUK7klth0jbx6rJ9jYsGZ6dCuK6a1MWtCey8P4 19 | VEbiBA2qXwjJzeap1G9mdPz16YTRlbdLhJDgN7fxmTK+A4GOObiq8x2EX9DjjCym 20 | fI39D0Z9o/2i29KZGC25DWYvjnLrz3EjfGkct1QrQG9q93pwQNKm+SKHRoUIOExe 21 | KsmgS0jPxie/IUWSDHlkCY19tHSX2swRNjkiomt1pKOozN3eTu1qs29f2HA/tKAw 22 | 6/Hwdo0XD+ybVEHq/STsppCe4zMojmChxuDVTYo2de3C 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /features/interceptor/client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "time" 11 | 12 | "golang.org/x/oauth2" 13 | "google.golang.org/grpc/credentials/oauth" 14 | 15 | "google.golang.org/grpc/credentials" 16 | 17 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 18 | "google.golang.org/grpc" 19 | ) 20 | 21 | func unaryCall(client pb.EchoClient) { 22 | fmt.Printf("--- gRPC Unary RPC Call ---\n") 23 | 24 | // 设置 10 秒超时时长,可参考 https://madmalls.com/blog/post/grpc-deadline/#21-contextwithtimeout 25 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 26 | defer cancel() 27 | 28 | // 调用 Unary RPC 29 | req := &pb.EchoRequest{Message: "madmalls.com"} 30 | resp, err := client.UnaryEcho(ctx, req) 31 | if err != nil { 32 | log.Fatalf("failed to call UnaryEcho: %v", err) 33 | } 34 | 35 | fmt.Printf("response:\n") 36 | fmt.Printf(" - %q\n", resp.GetMessage()) 37 | } 38 | 39 | func bidirectionalStreamingCall(c pb.EchoClient) { 40 | fmt.Printf("--- gRPC Bidirectional Streaming RPC Call ---\n") 41 | 42 | // 设置 10 秒超时时长,可参考 https://madmalls.com/blog/post/grpc-deadline/#21-contextwithtimeout 43 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 44 | defer cancel() 45 | 46 | // Make bidirectional streaming RPC 47 | stream, err := c.BidirectionalStreamingEcho(ctx) 48 | if err != nil { 49 | log.Fatalf("failed to call BidirectionalStreamingEcho: %v", err) 50 | } 51 | 52 | // Send all requests to the server 53 | for i := 0; i < 5; i++ { 54 | if err := stream.Send(&pb.EchoRequest{Message: fmt.Sprintf("Request %d", i+1)}); err != nil { 55 | log.Fatalf("failed to send request due to error: %v", err) 56 | } 57 | } 58 | 59 | // closes the send direction of the stream 60 | stream.CloseSend() 61 | 62 | // Read all the responses 63 | var rpcStatus error 64 | fmt.Printf("response:\n") 65 | for { 66 | resp, err := stream.Recv() 67 | if err != nil { 68 | rpcStatus = err 69 | break 70 | } 71 | fmt.Printf(" - %q\n", resp.Message) 72 | } 73 | if rpcStatus != io.EOF { 74 | log.Fatalf("failed to finish server streaming: %v", rpcStatus) 75 | } 76 | } 77 | 78 | // client-side unary interceptor (For Authentication) 79 | func unaryAuthInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { 80 | opts = append(opts, grpc.PerRPCCredentials(oauth.NewOauthAccess(&oauth2.Token{ 81 | AccessToken: "some-oauth2-secret-token", 82 | }))) 83 | err := invoker(ctx, method, req, reply, cc, opts...) 84 | return err 85 | } 86 | 87 | // client-side unary interceptor (For Logging) 88 | func unaryLogInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { 89 | // Logic before invoking the invoker 90 | start := time.Now() 91 | // Calls the invoker to execute RPC 92 | err := invoker(ctx, method, req, reply, cc, opts...) 93 | // Logic after invoking the invoker 94 | log.Printf("Invoked RPC method: %s, Duration time: %s, Error: %v", method, time.Since(start), err) 95 | 96 | return err 97 | } 98 | 99 | // client-side streaming interceptor (For Authentication) 100 | func streamAuthInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { 101 | opts = append(opts, grpc.PerRPCCredentials(oauth.NewOauthAccess(&oauth2.Token{ 102 | AccessToken: "some-oauth2-secret-token", 103 | }))) 104 | s, err := streamer(ctx, desc, cc, method, opts...) 105 | if err != nil { 106 | return nil, err 107 | } 108 | return s, nil 109 | } 110 | 111 | func main() { 112 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 113 | certFile := flag.String("cacert", "cacert.pem", "CA root certificate") 114 | flag.Parse() 115 | 116 | creds, err := credentials.NewClientTLSFromFile(*certFile, "") 117 | if err != nil { 118 | log.Fatalf("failed to load CA root certificate: %v", err) 119 | } 120 | 121 | opts := []grpc.DialOption{ 122 | // 1. TLS Credential 123 | grpc.WithTransportCredentials(creds), 124 | // 2. Client Unary Interceptors 125 | grpc.WithChainUnaryInterceptor( 126 | unaryAuthInterceptor, 127 | unaryLogInterceptor, 128 | ), 129 | // 3. Client Streaming Interceptor 130 | grpc.WithStreamInterceptor(streamAuthInterceptor), 131 | } 132 | 133 | // Set up a connection to the server. 134 | conn, err := grpc.Dial(*addr, opts...) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 135 | if err != nil { 136 | log.Fatalf("did not connect: %v", err) 137 | } 138 | defer conn.Close() 139 | 140 | c := pb.NewEchoClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewEchoClient method provided in the pb package we generated from our .proto. 141 | 142 | // Contact the server and print out its response. 143 | // 1. Unary RPC Call 144 | unaryCall(c) 145 | 146 | // 2. Bidirectional Streaming RPC Call 147 | // bidirectionalStreamingCall(c) 148 | } 149 | -------------------------------------------------------------------------------- /features/interceptor/server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "net" 11 | "strings" 12 | "time" 13 | 14 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 15 | "google.golang.org/grpc" 16 | "google.golang.org/grpc/codes" 17 | "google.golang.org/grpc/credentials" 18 | "google.golang.org/grpc/metadata" 19 | "google.golang.org/grpc/status" 20 | 21 | grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" 22 | ) 23 | 24 | // server is used to implement echopb.EchoServer. 25 | type server struct{} 26 | 27 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 28 | fmt.Printf("--- gRPC Unary RPC ---\n") 29 | fmt.Printf("request received: %v\n", req) 30 | 31 | return &pb.EchoResponse{Message: req.GetMessage()}, nil 32 | } 33 | 34 | func (s *server) ServerStreamingEcho(req *pb.EchoRequest, stream pb.Echo_ServerStreamingEchoServer) error { 35 | return status.Errorf(codes.Unimplemented, "method ServerStreamingEcho not implemented") 36 | } 37 | 38 | func (s *server) ClientStreamingEcho(stream pb.Echo_ClientStreamingEchoServer) error { 39 | return status.Errorf(codes.Unimplemented, "method ClientStreamingEcho not implemented") 40 | } 41 | 42 | func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { 43 | fmt.Printf("--- gRPC Bidirectional Streaming RPC ---\n") 44 | 45 | for { 46 | in, err := stream.Recv() 47 | if err != nil { 48 | if err == io.EOF { 49 | fmt.Printf("Receiving client streaming data completed\n") 50 | return nil 51 | } 52 | fmt.Printf("Error while receiving client streaming data: %v", err) 53 | return err 54 | } 55 | fmt.Printf("request received: %q\n", in.Message) 56 | 57 | if err := stream.Send(&pb.EchoResponse{Message: in.Message}); err != nil { 58 | fmt.Printf("Error while sending streaming data to client: %v", err) 59 | } 60 | } 61 | } 62 | 63 | // server-side unary interceptor (For Authentication) 64 | func unaryAuthInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 65 | // md 的值类似于: map[:authority:[192.168.40.123:50051] authorization:[Bearer some-secret-token] content-type:[application/grpc] user-agent:[grpc-go/1.20.1]] 66 | md, ok := metadata.FromIncomingContext(ctx) 67 | if !ok { 68 | return nil, status.Errorf(codes.InvalidArgument, "missing metadata") 69 | } 70 | fmt.Printf("Type of 'metadata.MD' is %T, and its value is %v \n", md, md) 71 | 72 | // 1. 判断是否存在 authorization 请求头 73 | authorization, ok := md["authorization"] 74 | if !ok { 75 | return nil, status.Errorf(codes.Unauthenticated, `missing "Authorization" header`) 76 | } 77 | fmt.Printf("Type of 'authorization' is %T, and its value is %v \n", authorization, authorization) 78 | 79 | const prefix = "Bearer " 80 | 81 | // 2. 如果存在 authorization 请求头的话,则 md["authorization"] 是一个 []string 82 | if !strings.HasPrefix(authorization[0], prefix) { 83 | return nil, status.Errorf(codes.Unauthenticated, `missing "Bearer " prefix in "Authorization" header`) 84 | } 85 | 86 | // 3. 验证 token 是否一致 87 | if token := strings.TrimPrefix(authorization[0], prefix); token != "some-oauth2-secret-token" { 88 | return nil, status.Errorf(codes.Unauthenticated, "invalid token") 89 | } 90 | 91 | // 调用 RPC 92 | m, err := handler(ctx, req) 93 | if err != nil { 94 | log.Printf("failed to invoke Unary RPC: %v\n", err) 95 | } 96 | 97 | return m, err 98 | } 99 | 100 | // server-side unary interceptor (For Logging) 101 | func unaryLogInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 102 | // Logic before invoking the invoker 103 | start := time.Now() 104 | // Calls the handler to execute RPC 105 | m, err := handler(ctx, req) 106 | if err != nil { 107 | log.Printf("failed to handler Unary RPC: %v\n", err) 108 | } 109 | // Logic after invoking the invoker 110 | log.Printf("Handler RPC method: %s, Duration time: %s, Error: %v", info.FullMethod, time.Since(start), err) 111 | 112 | return m, err 113 | } 114 | 115 | // server-side streaming interceptor (For Authentication) 116 | func streamAuthInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 117 | // md 的值类似于: map[:authority:[192.168.40.123:50051] authorization:[Bearer some-secret-token] content-type:[application/grpc] user-agent:[grpc-go/1.20.1]] 118 | md, ok := metadata.FromIncomingContext(ss.Context()) 119 | if !ok { 120 | return status.Errorf(codes.InvalidArgument, "missing metadata") 121 | } 122 | fmt.Printf("Type of 'metadata.MD' is %T, and its value is %v \n", md, md) 123 | 124 | // 1. 判断是否存在 authorization 请求头 125 | authorization, ok := md["authorization"] 126 | if !ok { 127 | return status.Errorf(codes.Unauthenticated, `missing "Authorization" header`) 128 | } 129 | fmt.Printf("Type of 'authorization' is %T, and its value is %v \n", authorization, authorization) 130 | 131 | const prefix = "Bearer " 132 | 133 | // 2. 如果存在 authorization 请求头的话,则 md["authorization"] 是一个 []string 134 | if !strings.HasPrefix(authorization[0], prefix) { 135 | return status.Errorf(codes.Unauthenticated, `missing "Bearer " prefix in "Authorization" header`) 136 | } 137 | 138 | // 3. 验证 token 是否一致 139 | if token := strings.TrimPrefix(authorization[0], prefix); token != "some-oauth2-secret-token" { 140 | return status.Errorf(codes.Unauthenticated, "invalid token") 141 | } 142 | 143 | // 调用 RPC 144 | err := handler(srv, ss) 145 | if err != nil { 146 | log.Printf("failed to invoke Streaming RPC: %v\n", err) 147 | } 148 | return err 149 | } 150 | 151 | func main() { 152 | port := flag.Int("port", 50051, "the port to serve on") 153 | certFile := flag.String("certfile", "server.crt", "Server certificate") 154 | keyFile := flag.String("keyfile", "server.key", "Server private key") 155 | flag.Parse() 156 | 157 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests 158 | if err != nil { 159 | log.Fatalf("failed to listen: %v", err) 160 | } 161 | fmt.Printf("server listening at %v\n", lis.Addr()) 162 | 163 | creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) 164 | if err != nil { 165 | log.Fatalf("failed to load certificates: %v", err) 166 | } 167 | 168 | opts := []grpc.ServerOption{ 169 | // 1. TLS Credential 170 | grpc.Creds(creds), 171 | // 2. Server Unary Interceptors 172 | grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( 173 | unaryAuthInterceptor, 174 | unaryLogInterceptor, 175 | )), 176 | // 3. Server Streaming Interceptor 177 | grpc.StreamInterceptor(streamAuthInterceptor), 178 | } 179 | 180 | s := grpc.NewServer(opts...) // Create an instance of the gRPC server 181 | 182 | pb.RegisterEchoServer(s, &server{}) // Register our service implementation with the gRPC server 183 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 184 | log.Fatalf("failed to serve: %v", err) 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /features/interceptor/server/server.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 (0x5) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: C=CN, ST=GuangDong, L=ShenZhen, O=Madmalls.com Co.,Ltd, OU=Madmalls.com Root CA, CN=Root CA 7 | Validity 8 | Not Before: Jun 11 07:22:30 2019 GMT 9 | Not After : Jun 10 07:22:30 2020 GMT 10 | Subject: C=CN, ST=GuangDong, O=Madmalls.com Co.,Ltd, OU=gRPC Server, CN=*.wangy.com 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | Public-Key: (2048 bit) 14 | Modulus: 15 | 00:e2:93:8c:43:64:e9:c6:02:6b:d1:98:f3:79:47: 16 | d5:99:c6:46:a8:6d:24:cc:a0:d8:3b:ec:e4:d0:83: 17 | 79:42:f5:bf:23:e1:0e:34:b3:f1:35:c6:27:6e:44: 18 | 58:48:ca:91:42:8c:6c:2f:6c:9d:09:d0:1b:73:d8: 19 | 42:f4:70:71:6c:40:f7:6c:71:63:39:db:d1:f8:f6: 20 | d4:f8:b7:18:03:7a:7d:32:0c:3f:f4:fd:0a:4c:e3: 21 | ad:71:33:8b:e6:25:53:52:d8:72:67:9f:56:1b:7e: 22 | d4:b7:27:28:0e:1d:28:92:b6:94:09:4f:bd:51:ef: 23 | 06:bc:80:39:97:cb:d2:20:8c:0c:ff:cf:33:97:7d: 24 | 7a:0e:01:e7:90:a2:30:64:34:be:3e:78:d9:3f:19: 25 | 7b:27:fc:c1:b2:0d:07:34:8b:6e:2e:0b:50:f4:8a: 26 | be:07:63:98:25:24:00:97:89:d2:ad:96:cd:56:01: 27 | 84:df:dc:af:be:a8:12:ef:f8:77:75:46:2d:49:21: 28 | ae:1c:bc:b1:87:94:4d:8e:b7:5d:7c:01:0a:c6:e6: 29 | 32:32:47:c3:d1:38:ab:5d:02:4f:72:c5:10:9c:a2: 30 | 83:9d:ee:a1:37:ee:02:bb:50:8a:78:ba:bf:f7:f7: 31 | 69:b0:ff:17:31:14:68:a5:db:05:ef:ab:2e:53:ac: 32 | ae:df 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Subject Alternative Name: 36 | DNS:www.wangy.com, DNS:blog.wangy.com, IP Address:192.168.40.123 37 | Signature Algorithm: sha256WithRSAEncryption 38 | 42:83:8d:68:cf:9b:93:59:63:94:5d:62:a8:14:bb:79:f4:09: 39 | cf:6d:ea:e5:86:bd:9a:fc:28:e2:9b:c5:fa:df:e9:50:b0:ac: 40 | 28:4c:7a:ff:38:67:1a:9b:e6:aa:8d:bd:da:3d:6f:e8:49:fa: 41 | f2:7e:fa:dd:4b:a8:4d:06:ee:1c:47:ff:36:99:23:24:9b:4c: 42 | 19:d1:f4:1c:85:c7:94:84:54:19:b0:78:6f:d7:54:25:a8:da: 43 | b1:18:76:1b:71:15:ad:35:d5:64:b6:6f:ee:80:6d:82:98:71: 44 | b8:db:bb:e5:01:da:f7:f1:aa:97:a7:ae:f8:90:b6:f6:ac:38: 45 | fa:9e:a2:15:10:2f:ca:c5:eb:bf:91:67:bc:f7:ee:2a:0e:80: 46 | 35:ad:6d:03:20:98:4f:df:39:5b:ac:f2:ef:43:01:95:b9:0a: 47 | 7b:29:b4:6d:83:de:1f:94:a6:69:6b:42:18:ef:cb:e6:68:66: 48 | b2:79:59:f8:ae:9f:89:25:ec:ee:46:f9:3c:34:ab:74:7d:72: 49 | 0a:3f:39:6e:07:28:79:e5:8c:8b:3a:da:1f:b9:7c:af:0c:0a: 50 | 0c:71:3d:aa:76:87:69:4c:75:9a:0b:8f:8e:10:f8:d6:53:c9: 51 | 35:8b:10:42:0a:32:01:a5:83:e3:22:0e:86:c0:1d:87:75:af: 52 | cb:30:cb:1a 53 | -----BEGIN CERTIFICATE----- 54 | MIIDnjCCAoagAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMCQ04x 55 | EjAQBgNVBAgMCUd1YW5nRG9uZzERMA8GA1UEBwwIU2hlblpoZW4xHTAbBgNVBAoM 56 | FE1hZG1hbGxzLmNvbSBDby4sTHRkMR0wGwYDVQQLDBRNYWRtYWxscy5jb20gUm9v 57 | dCBDQTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xOTA2MTEwNzIyMzBaFw0yMDA2MTAw 58 | NzIyMzBaMGwxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxHTAbBgNV 59 | BAoMFE1hZG1hbGxzLmNvbSBDby4sTHRkMRQwEgYDVQQLDAtnUlBDIFNlcnZlcjEU 60 | MBIGA1UEAwwLKi53YW5neS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 61 | AoIBAQDik4xDZOnGAmvRmPN5R9WZxkaobSTMoNg77OTQg3lC9b8j4Q40s/E1xidu 62 | RFhIypFCjGwvbJ0J0Btz2EL0cHFsQPdscWM529H49tT4txgDen0yDD/0/QpM461x 63 | M4vmJVNS2HJnn1YbftS3JygOHSiStpQJT71R7wa8gDmXy9IgjAz/zzOXfXoOAeeQ 64 | ojBkNL4+eNk/GXsn/MGyDQc0i24uC1D0ir4HY5glJACXidKtls1WAYTf3K++qBLv 65 | +Hd1Ri1JIa4cvLGHlE2Ot118AQrG5jIyR8PROKtdAk9yxRCcooOd7qE37gK7UIp4 66 | ur/392mw/xcxFGil2wXvqy5TrK7fAgMBAAGjMjAwMC4GA1UdEQQnMCWCDXd3dy53 67 | YW5neS5jb22CDmJsb2cud2FuZ3kuY29thwTAqCh7MA0GCSqGSIb3DQEBCwUAA4IB 68 | AQBCg41oz5uTWWOUXWKoFLt59AnPberlhr2a/Cjim8X63+lQsKwoTHr/OGcam+aq 69 | jb3aPW/oSfryfvrdS6hNBu4cR/82mSMkm0wZ0fQchceUhFQZsHhv11QlqNqxGHYb 70 | cRWtNdVktm/ugG2CmHG427vlAdr38aqXp674kLb2rDj6nqIVEC/Kxeu/kWe89+4q 71 | DoA1rW0DIJhP3zlbrPLvQwGVuQp7KbRtg94flKZpa0IY78vmaGayeVn4rp+JJezu 72 | Rvk8NKt0fXIKPzluByh55YyLOtofuXyvDAoMcT2qdodpTHWaC4+OEPjWU8k1ixBC 73 | CjIBpYPjIg6GwB2Hda/LMMsa 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /features/interceptor/server/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpgIBAAKCAQEA4pOMQ2TpxgJr0ZjzeUfVmcZGqG0kzKDYO+zk0IN5QvW/I+EO 3 | NLPxNcYnbkRYSMqRQoxsL2ydCdAbc9hC9HBxbED3bHFjOdvR+PbU+LcYA3p9Mgw/ 4 | 9P0KTOOtcTOL5iVTUthyZ59WG37UtycoDh0okraUCU+9Ue8GvIA5l8vSIIwM/88z 5 | l316DgHnkKIwZDS+PnjZPxl7J/zBsg0HNItuLgtQ9Iq+B2OYJSQAl4nSrZbNVgGE 6 | 39yvvqgS7/h3dUYtSSGuHLyxh5RNjrddfAEKxuYyMkfD0TirXQJPcsUQnKKDne6h 7 | N+4Cu1CKeLq/9/dpsP8XMRRopdsF76suU6yu3wIDAQABAoIBAQCjrPDjco/KAc+/ 8 | ft1LnI/6YRiD7SxrQjpSt+PnmUJNE9e7ZIXtnpu+O+IaLvcTxnm++E/ixnR/NT3P 9 | psdfa6cUC65xQUvr7Rc24aCh9yo6wQ6Vy/Gb2fvJ5aNSpmkGnaoeq8uhfaInhKzH 10 | jlrKL1gy+//e5iKegKx+GacBODUYWpc8bVoROpvqgtSDg1wtuwUU/5fy6f8IIHuT 11 | 78cPm6t39ylcFkV3lt1yS4LhvUoW2Nlm5SHOicDc/cYVE8xqfJlARzN9xUfM+5LB 12 | sUdbR5UZOxOPsd24q1D1DZc1s34soj/wKXPVyyuhp0o7Db6vSsxQDfOetfuRUSR6 13 | +5zPMLqxAoGBAP6laEBin8SVThAvQmS4PoyqPK8Hb0GgjxPGxyXyrtSL3/Xy86E0 14 | vhOGTm8gh2oA0wZQ3KIg5TIhVa3G+UNMbZmIuYCQdeAQpl/sdMnpw66aNtHj6kt7 15 | gqzWzH1en4w2T3DxU7LtCQIx4q3Akv0oyZ/YovlNb5dkSHtuz4ZVRYkXAoGBAOPH 16 | 74KHzkD/IWkGf7/QG9XQ93hjggxmDS/WzeKwKYewPyZoWRvkvj7i7d+P1AUrCvgd 17 | YeB132S0S1JrSUP4Fn+DNwgj3Kyg2Fq2/HmwTKkqVPjOKlonTKlA+h3fNG/NbJDE 18 | E41yFkK+zjSbxxcAYLCgR2A9PCEyXPXyV20U8BV5AoGBAJnq5unL8yBC0u2Lc0kn 19 | 6H7jw0xUZRY482KTyvoQB0bnyRaDpGkzVRS+IJihA9i56NOverzwvzie14fzdeUM 20 | xE6CSwX/y5AE4FuotCr7hlD6W9pgNdUsMZ9BMlcxI6T/iuMMq3fCOKi/+HDnrrEg 21 | v0ZEDrY77RCICBu7repXjnE/AoGBAIdBvhOAmRU3apt25Hz+EslQoOK4FA1QvBvg 22 | LbmiacbM/XLNG7zYg6/MCPxr57Z57LWQnQIwfErMVL3IP2VA9/sX66HFydAoYtDb 23 | P+jyq1L4dCSaJ8QI+hi3IM6EMBsDnKgKBqJDULypmMDcj8g0zTWUt02Kjx4XTeQt 24 | 14RKnpXhAoGBAJkImhczkTucb/Yhaw1du2IkylPSb+HAJH6NFH9LFNvKYOIFfUaA 25 | id8ZkR5WGDJiK7vm3QT4n6j14/CnTXqrQ52W9ixxOIjbd1AHgHNwlbQGJodInT+l 26 | j3Iqa44peXerIk0MO8qYEjEGybHu7DhZhkbZoCn1Qqn+XpDbx2bbPe7w 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /features/tls/client/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3TCCAsWgAwIBAgIJAKoKZuLTxD+eMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD 3 | VQQGEwJDTjESMBAGA1UECAwJR3VhbmdEb25nMREwDwYDVQQHDAhTaGVuWmhlbjEd 4 | MBsGA1UECgwUTWFkbWFsbHMuY29tIENvLixMdGQxHTAbBgNVBAsMFE1hZG1hbGxz 5 | LmNvbSBSb290IENBMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDUyOTA3MjMwM1oX 6 | DTI5MDUyNjA3MjMwM1owgYQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0Rv 7 | bmcxETAPBgNVBAcMCFNoZW5aaGVuMR0wGwYDVQQKDBRNYWRtYWxscy5jb20gQ28u 8 | LEx0ZDEdMBsGA1UECwwUTWFkbWFsbHMuY29tIFJvb3QgQ0ExEDAOBgNVBAMMB1Jv 9 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsaZ3C8XzVTfir 10 | 2LZpJMMnxo2ZUSRcjrVRav3ATtcubqE94XT/nq5wrUClhvOWKeUIFaPZHY5dXSLm 11 | LAIRaCNW6t0cHcfCyd6ArVbjVCT8dbo1wwis2ATFRVRiVmuFjPSu2iLR/pm9m94Q 12 | C1uZe4ypXGW08TieivNiRTflfXNvjKzDENJriEQ6ukhwAixOLZcbYKZKi4wZXscl 13 | iaBcitcCZty0ODtH7TXQbrQNU2lkQBT/HmtABgL4ln5p+tiDUTNCJjvPeNBT4FTs 14 | KLtcnJM4NJspRwHO4YIGGHhmgJhneSPsHm5poE6pVu3DDRFHuforPv/k70cX9PGz 15 | OQlREfNRAgMBAAGjUDBOMB0GA1UdDgQWBBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAf 16 | BgNVHSMEGDAWgBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAMBgNVHRMEBTADAQH/MA0G 17 | CSqGSIb3DQEBCwUAA4IBAQCfcXII5evHWW5qcF3f2wMRCFhizOCojQ6CA2mwH9LN 18 | GsZRclTwniQ7OqH67b94ii3mF3dUK7klth0jbx6rJ9jYsGZ6dCuK6a1MWtCey8P4 19 | VEbiBA2qXwjJzeap1G9mdPz16YTRlbdLhJDgN7fxmTK+A4GOObiq8x2EX9DjjCym 20 | fI39D0Z9o/2i29KZGC25DWYvjnLrz3EjfGkct1QrQG9q93pwQNKm+SKHRoUIOExe 21 | KsmgS0jPxie/IUWSDHlkCY19tHSX2swRNjkiomt1pKOozN3eTu1qs29f2HA/tKAw 22 | 6/Hwdo0XD+ybVEHq/STsppCe4zMojmChxuDVTYo2de3C 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /features/tls/client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | 10 | "google.golang.org/grpc/credentials" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 13 | "google.golang.org/grpc" 14 | ) 15 | 16 | func main() { 17 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 18 | certFile := flag.String("cacert", "cacert.pem", "CA root certificate") 19 | flag.Parse() 20 | 21 | creds, err := credentials.NewClientTLSFromFile(*certFile, "") 22 | if err != nil { 23 | log.Fatalf("failed to load CA root certificate: %v", err) 24 | } 25 | 26 | // Set up a connection to the server. 27 | conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(creds)) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 28 | if err != nil { 29 | log.Fatalf("did not connect: %v", err) 30 | } 31 | defer conn.Close() 32 | 33 | c := pb.NewEchoClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewEchoClient method provided in the pb package we generated from our .proto. 34 | 35 | // Contact the server and print out its response. 36 | msg := "madmalls.com" 37 | resp, err := c.UnaryEcho(context.Background(), &pb.EchoRequest{Message: msg}) // Now let’s look at how we call our service methods. Note that in gRPC-Go, RPCs operate in a blocking/synchronous mode, which means that the RPC call waits for the server to respond, and will either return a response or an error. 38 | if err != nil { 39 | log.Fatalf("failed to call UnaryEcho: %v", err) 40 | } 41 | fmt.Printf("response:\n") 42 | fmt.Printf(" - %q\n", resp.GetMessage()) 43 | } 44 | -------------------------------------------------------------------------------- /features/tls/server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for Echo service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net" 10 | 11 | pb "github.com/wangy8961/grpc-go-tutorial/features/echopb" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/codes" 14 | "google.golang.org/grpc/credentials" 15 | "google.golang.org/grpc/status" 16 | ) 17 | 18 | // server is used to implement echopb.EchoServer. 19 | type server struct{} 20 | 21 | /* 22 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 23 | return nil, status.Errorf(codes.Unimplemented, "method UnaryEcho not implemented") 24 | } 25 | */ 26 | func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { 27 | fmt.Printf("--- gRPC Unary RPC ---\n") 28 | fmt.Printf("request received: %v\n", req) 29 | return &pb.EchoResponse{Message: req.GetMessage()}, nil 30 | } 31 | 32 | func (s *server) ServerStreamingEcho(req *pb.EchoRequest, stream pb.Echo_ServerStreamingEchoServer) error { 33 | return status.Errorf(codes.Unimplemented, "method ServerStreamingEcho not implemented") 34 | } 35 | 36 | func (s *server) ClientStreamingEcho(stream pb.Echo_ClientStreamingEchoServer) error { 37 | return status.Errorf(codes.Unimplemented, "method ClientStreamingEcho not implemented") 38 | } 39 | 40 | func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { 41 | return status.Errorf(codes.Unimplemented, "method BidirectionalStreamingEcho not implemented") 42 | } 43 | 44 | func main() { 45 | port := flag.Int("port", 50051, "the port to serve on") 46 | certFile := flag.String("certfile", "server.crt", "Server certificate") 47 | keyFile := flag.String("keyfile", "server.key", "Server private key") 48 | flag.Parse() 49 | 50 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests 51 | if err != nil { 52 | log.Fatalf("failed to listen: %v", err) 53 | } 54 | fmt.Printf("server listening at %v\n", lis.Addr()) 55 | 56 | creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) 57 | if err != nil { 58 | log.Fatalf("failed to load certificates: %v", err) 59 | } 60 | 61 | /* 或者使用下面的方法获取 creds 62 | cert, err := tls.LoadX509KeyPair(*certFile, *keyFile) 63 | if err != nil { 64 | log.Fatalf("failed to load key pair: %s", err) 65 | } 66 | creds := credentials.NewServerTLSFromCert(&cert) 67 | */ 68 | 69 | s := grpc.NewServer(grpc.Creds(creds)) // Create an instance of the gRPC server 70 | 71 | pb.RegisterEchoServer(s, &server{}) // Register our service implementation with the gRPC server 72 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 73 | log.Fatalf("failed to serve: %v", err) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /features/tls/server/server.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 (0x5) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: C=CN, ST=GuangDong, L=ShenZhen, O=Madmalls.com Co.,Ltd, OU=Madmalls.com Root CA, CN=Root CA 7 | Validity 8 | Not Before: Jun 11 07:22:30 2019 GMT 9 | Not After : Jun 10 07:22:30 2020 GMT 10 | Subject: C=CN, ST=GuangDong, O=Madmalls.com Co.,Ltd, OU=gRPC Server, CN=*.wangy.com 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | Public-Key: (2048 bit) 14 | Modulus: 15 | 00:e2:93:8c:43:64:e9:c6:02:6b:d1:98:f3:79:47: 16 | d5:99:c6:46:a8:6d:24:cc:a0:d8:3b:ec:e4:d0:83: 17 | 79:42:f5:bf:23:e1:0e:34:b3:f1:35:c6:27:6e:44: 18 | 58:48:ca:91:42:8c:6c:2f:6c:9d:09:d0:1b:73:d8: 19 | 42:f4:70:71:6c:40:f7:6c:71:63:39:db:d1:f8:f6: 20 | d4:f8:b7:18:03:7a:7d:32:0c:3f:f4:fd:0a:4c:e3: 21 | ad:71:33:8b:e6:25:53:52:d8:72:67:9f:56:1b:7e: 22 | d4:b7:27:28:0e:1d:28:92:b6:94:09:4f:bd:51:ef: 23 | 06:bc:80:39:97:cb:d2:20:8c:0c:ff:cf:33:97:7d: 24 | 7a:0e:01:e7:90:a2:30:64:34:be:3e:78:d9:3f:19: 25 | 7b:27:fc:c1:b2:0d:07:34:8b:6e:2e:0b:50:f4:8a: 26 | be:07:63:98:25:24:00:97:89:d2:ad:96:cd:56:01: 27 | 84:df:dc:af:be:a8:12:ef:f8:77:75:46:2d:49:21: 28 | ae:1c:bc:b1:87:94:4d:8e:b7:5d:7c:01:0a:c6:e6: 29 | 32:32:47:c3:d1:38:ab:5d:02:4f:72:c5:10:9c:a2: 30 | 83:9d:ee:a1:37:ee:02:bb:50:8a:78:ba:bf:f7:f7: 31 | 69:b0:ff:17:31:14:68:a5:db:05:ef:ab:2e:53:ac: 32 | ae:df 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Subject Alternative Name: 36 | DNS:www.wangy.com, DNS:blog.wangy.com, IP Address:192.168.40.123 37 | Signature Algorithm: sha256WithRSAEncryption 38 | 42:83:8d:68:cf:9b:93:59:63:94:5d:62:a8:14:bb:79:f4:09: 39 | cf:6d:ea:e5:86:bd:9a:fc:28:e2:9b:c5:fa:df:e9:50:b0:ac: 40 | 28:4c:7a:ff:38:67:1a:9b:e6:aa:8d:bd:da:3d:6f:e8:49:fa: 41 | f2:7e:fa:dd:4b:a8:4d:06:ee:1c:47:ff:36:99:23:24:9b:4c: 42 | 19:d1:f4:1c:85:c7:94:84:54:19:b0:78:6f:d7:54:25:a8:da: 43 | b1:18:76:1b:71:15:ad:35:d5:64:b6:6f:ee:80:6d:82:98:71: 44 | b8:db:bb:e5:01:da:f7:f1:aa:97:a7:ae:f8:90:b6:f6:ac:38: 45 | fa:9e:a2:15:10:2f:ca:c5:eb:bf:91:67:bc:f7:ee:2a:0e:80: 46 | 35:ad:6d:03:20:98:4f:df:39:5b:ac:f2:ef:43:01:95:b9:0a: 47 | 7b:29:b4:6d:83:de:1f:94:a6:69:6b:42:18:ef:cb:e6:68:66: 48 | b2:79:59:f8:ae:9f:89:25:ec:ee:46:f9:3c:34:ab:74:7d:72: 49 | 0a:3f:39:6e:07:28:79:e5:8c:8b:3a:da:1f:b9:7c:af:0c:0a: 50 | 0c:71:3d:aa:76:87:69:4c:75:9a:0b:8f:8e:10:f8:d6:53:c9: 51 | 35:8b:10:42:0a:32:01:a5:83:e3:22:0e:86:c0:1d:87:75:af: 52 | cb:30:cb:1a 53 | -----BEGIN CERTIFICATE----- 54 | MIIDnjCCAoagAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMCQ04x 55 | EjAQBgNVBAgMCUd1YW5nRG9uZzERMA8GA1UEBwwIU2hlblpoZW4xHTAbBgNVBAoM 56 | FE1hZG1hbGxzLmNvbSBDby4sTHRkMR0wGwYDVQQLDBRNYWRtYWxscy5jb20gUm9v 57 | dCBDQTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xOTA2MTEwNzIyMzBaFw0yMDA2MTAw 58 | NzIyMzBaMGwxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxHTAbBgNV 59 | BAoMFE1hZG1hbGxzLmNvbSBDby4sTHRkMRQwEgYDVQQLDAtnUlBDIFNlcnZlcjEU 60 | MBIGA1UEAwwLKi53YW5neS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 61 | AoIBAQDik4xDZOnGAmvRmPN5R9WZxkaobSTMoNg77OTQg3lC9b8j4Q40s/E1xidu 62 | RFhIypFCjGwvbJ0J0Btz2EL0cHFsQPdscWM529H49tT4txgDen0yDD/0/QpM461x 63 | M4vmJVNS2HJnn1YbftS3JygOHSiStpQJT71R7wa8gDmXy9IgjAz/zzOXfXoOAeeQ 64 | ojBkNL4+eNk/GXsn/MGyDQc0i24uC1D0ir4HY5glJACXidKtls1WAYTf3K++qBLv 65 | +Hd1Ri1JIa4cvLGHlE2Ot118AQrG5jIyR8PROKtdAk9yxRCcooOd7qE37gK7UIp4 66 | ur/392mw/xcxFGil2wXvqy5TrK7fAgMBAAGjMjAwMC4GA1UdEQQnMCWCDXd3dy53 67 | YW5neS5jb22CDmJsb2cud2FuZ3kuY29thwTAqCh7MA0GCSqGSIb3DQEBCwUAA4IB 68 | AQBCg41oz5uTWWOUXWKoFLt59AnPberlhr2a/Cjim8X63+lQsKwoTHr/OGcam+aq 69 | jb3aPW/oSfryfvrdS6hNBu4cR/82mSMkm0wZ0fQchceUhFQZsHhv11QlqNqxGHYb 70 | cRWtNdVktm/ugG2CmHG427vlAdr38aqXp674kLb2rDj6nqIVEC/Kxeu/kWe89+4q 71 | DoA1rW0DIJhP3zlbrPLvQwGVuQp7KbRtg94flKZpa0IY78vmaGayeVn4rp+JJezu 72 | Rvk8NKt0fXIKPzluByh55YyLOtofuXyvDAoMcT2qdodpTHWaC4+OEPjWU8k1ixBC 73 | CjIBpYPjIg6GwB2Hda/LMMsa 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /features/tls/server/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpgIBAAKCAQEA4pOMQ2TpxgJr0ZjzeUfVmcZGqG0kzKDYO+zk0IN5QvW/I+EO 3 | NLPxNcYnbkRYSMqRQoxsL2ydCdAbc9hC9HBxbED3bHFjOdvR+PbU+LcYA3p9Mgw/ 4 | 9P0KTOOtcTOL5iVTUthyZ59WG37UtycoDh0okraUCU+9Ue8GvIA5l8vSIIwM/88z 5 | l316DgHnkKIwZDS+PnjZPxl7J/zBsg0HNItuLgtQ9Iq+B2OYJSQAl4nSrZbNVgGE 6 | 39yvvqgS7/h3dUYtSSGuHLyxh5RNjrddfAEKxuYyMkfD0TirXQJPcsUQnKKDne6h 7 | N+4Cu1CKeLq/9/dpsP8XMRRopdsF76suU6yu3wIDAQABAoIBAQCjrPDjco/KAc+/ 8 | ft1LnI/6YRiD7SxrQjpSt+PnmUJNE9e7ZIXtnpu+O+IaLvcTxnm++E/ixnR/NT3P 9 | psdfa6cUC65xQUvr7Rc24aCh9yo6wQ6Vy/Gb2fvJ5aNSpmkGnaoeq8uhfaInhKzH 10 | jlrKL1gy+//e5iKegKx+GacBODUYWpc8bVoROpvqgtSDg1wtuwUU/5fy6f8IIHuT 11 | 78cPm6t39ylcFkV3lt1yS4LhvUoW2Nlm5SHOicDc/cYVE8xqfJlARzN9xUfM+5LB 12 | sUdbR5UZOxOPsd24q1D1DZc1s34soj/wKXPVyyuhp0o7Db6vSsxQDfOetfuRUSR6 13 | +5zPMLqxAoGBAP6laEBin8SVThAvQmS4PoyqPK8Hb0GgjxPGxyXyrtSL3/Xy86E0 14 | vhOGTm8gh2oA0wZQ3KIg5TIhVa3G+UNMbZmIuYCQdeAQpl/sdMnpw66aNtHj6kt7 15 | gqzWzH1en4w2T3DxU7LtCQIx4q3Akv0oyZ/YovlNb5dkSHtuz4ZVRYkXAoGBAOPH 16 | 74KHzkD/IWkGf7/QG9XQ93hjggxmDS/WzeKwKYewPyZoWRvkvj7i7d+P1AUrCvgd 17 | YeB132S0S1JrSUP4Fn+DNwgj3Kyg2Fq2/HmwTKkqVPjOKlonTKlA+h3fNG/NbJDE 18 | E41yFkK+zjSbxxcAYLCgR2A9PCEyXPXyV20U8BV5AoGBAJnq5unL8yBC0u2Lc0kn 19 | 6H7jw0xUZRY482KTyvoQB0bnyRaDpGkzVRS+IJihA9i56NOverzwvzie14fzdeUM 20 | xE6CSwX/y5AE4FuotCr7hlD6W9pgNdUsMZ9BMlcxI6T/iuMMq3fCOKi/+HDnrrEg 21 | v0ZEDrY77RCICBu7repXjnE/AoGBAIdBvhOAmRU3apt25Hz+EslQoOK4FA1QvBvg 22 | LbmiacbM/XLNG7zYg6/MCPxr57Z57LWQnQIwfErMVL3IP2VA9/sX66HFydAoYtDb 23 | P+jyq1L4dCSaJ8QI+hi3IM6EMBsDnKgKBqJDULypmMDcj8g0zTWUt02Kjx4XTeQt 24 | 14RKnpXhAoGBAJkImhczkTucb/Yhaw1du2IkylPSb+HAJH6NFH9LFNvKYOIFfUaA 25 | id8ZkR5WGDJiK7vm3QT4n6j14/CnTXqrQ52W9ixxOIjbd1AHgHNwlbQGJodInT+l 26 | j3Iqa44peXerIk0MO8qYEjEGybHu7DhZhkbZoCn1Qqn+XpDbx2bbPe7w 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wangy8961/grpc-go-tutorial 2 | 3 | go 1.12 4 | 5 | require ( 6 | cloud.google.com/go v0.40.0 // indirect 7 | github.com/elazarl/go-bindata-assetfs v1.0.0 8 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b 9 | github.com/golang/mock v1.3.1 // indirect 10 | github.com/golang/protobuf v1.3.1 11 | github.com/google/btree v1.0.0 // indirect 12 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f // indirect 13 | github.com/googleapis/gax-go/v2 v2.0.5 // indirect 14 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 15 | github.com/grpc-ecosystem/grpc-gateway v1.9.1 16 | github.com/kr/pty v1.1.5 // indirect 17 | github.com/philips/go-bindata-assetfs v0.0.0-20150624150248-3dcc96556217 18 | github.com/philips/grpc-gateway-example v0.0.0-20170619012617-a269bcb5931c 19 | go.opencensus.io v0.22.0 // indirect 20 | golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56 // indirect 21 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522 // indirect 22 | golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff // indirect 23 | golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88 // indirect 24 | golang.org/x/mod v0.1.0 // indirect 25 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 // indirect 26 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 27 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f // indirect 28 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect 29 | golang.org/x/tools v0.0.0-20190617190820-da514acc4774 // indirect 30 | google.golang.org/appengine v1.6.1 // indirect 31 | google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3 32 | google.golang.org/grpc v1.21.1 33 | honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.40.0 h1:FjSY7bOj+WzJe6TZRVtXI2b9kAYvtNg4lMbcH2+MUkk= 5 | cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro= 6 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 7 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 8 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 9 | github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= 10 | github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= 11 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 12 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 13 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 14 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 15 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 16 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 17 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 18 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 19 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 20 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 21 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 22 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 23 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 24 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 25 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 26 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 27 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 28 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 29 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 30 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= 31 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 32 | github.com/grpc-ecosystem/grpc-gateway v1.9.1 h1:LgwPEIdyJmF9Ug9nINVNspG6Z6P8/TM0yKdQ5h3VQaQ= 33 | github.com/grpc-ecosystem/grpc-gateway v1.9.1/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 34 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 35 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 36 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 37 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 38 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 39 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 40 | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 41 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 42 | github.com/philips/go-bindata-assetfs v0.0.0-20150624150248-3dcc96556217 h1:d0gvU83WNkaQFi/xS0KbHf8G8cXwowAFcoZnIYlmPiE= 43 | github.com/philips/go-bindata-assetfs v0.0.0-20150624150248-3dcc96556217/go.mod h1:fFa4wLHd7+D2mWRwlStkwVYMtIc/+odU35B0qn/x4WU= 44 | github.com/philips/grpc-gateway-example v0.0.0-20170619012617-a269bcb5931c h1:JI3Tfql2WN7MI3CQzJ93YptrW5GCM2Rn2cdBCZIRjmQ= 45 | github.com/philips/grpc-gateway-example v0.0.0-20170619012617-a269bcb5931c/go.mod h1:A1Gkiuqr8C81MmlrWwc/lqXIlmTVb/qP8geXw5Jt3X8= 46 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 47 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 48 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 49 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 50 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 51 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 52 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 53 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 54 | golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 55 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 56 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 57 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 58 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 59 | golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 60 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 61 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 62 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 63 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 64 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 65 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 66 | golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 67 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 68 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 69 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 70 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 71 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 72 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 73 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 74 | golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= 75 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 76 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 77 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 78 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 79 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 80 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= 81 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 82 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 83 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 84 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= 85 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 86 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 87 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 88 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 89 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 90 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 91 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 92 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 93 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= 94 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 95 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 96 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 97 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 98 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 99 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 100 | golang.org/x/sys v0.0.0-20190614084037-d442b75600c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 101 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 102 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 103 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 104 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 105 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 106 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 107 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 108 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 109 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 110 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 111 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 112 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 113 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 114 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 115 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 116 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 117 | golang.org/x/tools v0.0.0-20190530171427-2b03ca6e44eb/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 118 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 119 | golang.org/x/tools v0.0.0-20190614152001-1edc8e83c897/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 120 | golang.org/x/tools v0.0.0-20190617190820-da514acc4774 h1:CQVOmarCBFzTx0kbOU0ru54Cvot8SdSrNYjZPhQl+gk= 121 | golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 122 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 123 | google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= 124 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 125 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 126 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 127 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 128 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= 129 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 130 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 131 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 132 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 133 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 134 | google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 135 | google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3 h1:0LGHEA/u5XLibPOx6D7D8FBT/ax6wT57vNKY0QckCwo= 136 | google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 137 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 138 | google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= 139 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 140 | google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= 141 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 142 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 143 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 144 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 145 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 146 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 147 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 148 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 149 | honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b/go.mod h1:JlmFZigtG9vBVR3QGIQ9g/Usz4BzH+Xm6Z8iHQWRYUw= 150 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 151 | -------------------------------------------------------------------------------- /greet/greet_client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for Greeter service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "log" 7 | "os" 8 | "time" 9 | 10 | pb "github.com/wangy8961/grpc-go-tutorial/greet/greetpb" 11 | "google.golang.org/grpc" 12 | ) 13 | 14 | const ( 15 | address = "localhost:50051" 16 | defaultName = "world" 17 | ) 18 | 19 | func main() { 20 | // Set up a connection to the server. 21 | conn, err := grpc.Dial(address, grpc.WithInsecure()) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 22 | if err != nil { 23 | log.Fatalf("did not connect: %v", err) 24 | } 25 | defer conn.Close() 26 | c := pb.NewGreeterClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewGreeterClient method provided in the pb package we generated from our .proto. 27 | 28 | // Contact the server and print out its response. 29 | name := defaultName 30 | if len(os.Args) > 1 { 31 | name = os.Args[1] 32 | } 33 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 34 | defer cancel() 35 | r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) // Now let’s look at how we call our service methods. Note that in gRPC-Go, RPCs operate in a blocking/synchronous mode, which means that the RPC call waits for the server to respond, and will either return a response or an error. 36 | if err != nil { 37 | log.Fatalf("could not greet: %v", err) 38 | } 39 | log.Printf("Greeting: %s", r.Message) 40 | } 41 | -------------------------------------------------------------------------------- /greet/greet_server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for Greeter service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "log" 7 | "net" 8 | 9 | pb "github.com/wangy8961/grpc-go-tutorial/greet/greetpb" 10 | "google.golang.org/grpc" 11 | ) 12 | 13 | const ( 14 | port = ":50051" 15 | ) 16 | 17 | // server is used to implement greetpb.GreeterServer. 18 | type server struct{} 19 | 20 | // SayHello implements greetpb.GreeterServer 21 | func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { 22 | log.Printf("Received: %v", in.Name) 23 | return &pb.HelloReply{Message: "Hello " + in.Name}, nil 24 | } 25 | 26 | func main() { 27 | lis, err := net.Listen("tcp", port) // Specify the port we want to use to listen for client requests 28 | if err != nil { 29 | log.Fatalf("failed to listen: %v", err) 30 | } 31 | s := grpc.NewServer() // Create an instance of the gRPC server 32 | pb.RegisterGreeterServer(s, &server{}) // Register our service implementation with the gRPC server 33 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 34 | log.Fatalf("failed to serve: %v", err) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /greet/greetpb/greet.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: greet.proto 3 | 4 | package greetpb 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | proto "github.com/golang/protobuf/proto" 10 | grpc "google.golang.org/grpc" 11 | codes "google.golang.org/grpc/codes" 12 | status "google.golang.org/grpc/status" 13 | math "math" 14 | ) 15 | 16 | // Reference imports to suppress errors if they are not otherwise used. 17 | var _ = proto.Marshal 18 | var _ = fmt.Errorf 19 | var _ = math.Inf 20 | 21 | // This is a compile-time assertion to ensure that this generated file 22 | // is compatible with the proto package it is being compiled against. 23 | // A compilation error at this line likely means your copy of the 24 | // proto package needs to be updated. 25 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 26 | 27 | // The request message containing the user's name. 28 | type HelloRequest struct { 29 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 30 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 31 | XXX_unrecognized []byte `json:"-"` 32 | XXX_sizecache int32 `json:"-"` 33 | } 34 | 35 | func (m *HelloRequest) Reset() { *m = HelloRequest{} } 36 | func (m *HelloRequest) String() string { return proto.CompactTextString(m) } 37 | func (*HelloRequest) ProtoMessage() {} 38 | func (*HelloRequest) Descriptor() ([]byte, []int) { 39 | return fileDescriptor_32c0044392f32579, []int{0} 40 | } 41 | 42 | func (m *HelloRequest) XXX_Unmarshal(b []byte) error { 43 | return xxx_messageInfo_HelloRequest.Unmarshal(m, b) 44 | } 45 | func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 46 | return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic) 47 | } 48 | func (m *HelloRequest) XXX_Merge(src proto.Message) { 49 | xxx_messageInfo_HelloRequest.Merge(m, src) 50 | } 51 | func (m *HelloRequest) XXX_Size() int { 52 | return xxx_messageInfo_HelloRequest.Size(m) 53 | } 54 | func (m *HelloRequest) XXX_DiscardUnknown() { 55 | xxx_messageInfo_HelloRequest.DiscardUnknown(m) 56 | } 57 | 58 | var xxx_messageInfo_HelloRequest proto.InternalMessageInfo 59 | 60 | func (m *HelloRequest) GetName() string { 61 | if m != nil { 62 | return m.Name 63 | } 64 | return "" 65 | } 66 | 67 | // The response message containing the greetings 68 | type HelloReply struct { 69 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 70 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 71 | XXX_unrecognized []byte `json:"-"` 72 | XXX_sizecache int32 `json:"-"` 73 | } 74 | 75 | func (m *HelloReply) Reset() { *m = HelloReply{} } 76 | func (m *HelloReply) String() string { return proto.CompactTextString(m) } 77 | func (*HelloReply) ProtoMessage() {} 78 | func (*HelloReply) Descriptor() ([]byte, []int) { 79 | return fileDescriptor_32c0044392f32579, []int{1} 80 | } 81 | 82 | func (m *HelloReply) XXX_Unmarshal(b []byte) error { 83 | return xxx_messageInfo_HelloReply.Unmarshal(m, b) 84 | } 85 | func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 86 | return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic) 87 | } 88 | func (m *HelloReply) XXX_Merge(src proto.Message) { 89 | xxx_messageInfo_HelloReply.Merge(m, src) 90 | } 91 | func (m *HelloReply) XXX_Size() int { 92 | return xxx_messageInfo_HelloReply.Size(m) 93 | } 94 | func (m *HelloReply) XXX_DiscardUnknown() { 95 | xxx_messageInfo_HelloReply.DiscardUnknown(m) 96 | } 97 | 98 | var xxx_messageInfo_HelloReply proto.InternalMessageInfo 99 | 100 | func (m *HelloReply) GetMessage() string { 101 | if m != nil { 102 | return m.Message 103 | } 104 | return "" 105 | } 106 | 107 | func init() { 108 | proto.RegisterType((*HelloRequest)(nil), "greet.HelloRequest") 109 | proto.RegisterType((*HelloReply)(nil), "greet.HelloReply") 110 | } 111 | 112 | func init() { proto.RegisterFile("greet.proto", fileDescriptor_32c0044392f32579) } 113 | 114 | var fileDescriptor_32c0044392f32579 = []byte{ 115 | // 144 bytes of a gzipped FileDescriptorProto 116 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0x2f, 0x4a, 0x4d, 117 | 0x2d, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x73, 0x94, 0x94, 0xb8, 0x78, 0x3c, 118 | 0x52, 0x73, 0x72, 0xf2, 0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 119 | 0x12, 0x73, 0x53, 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x35, 0x2e, 0x2e, 120 | 0xa8, 0x9a, 0x82, 0x9c, 0x4a, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0x74, 0x98, 121 | 0x22, 0x18, 0xd7, 0xc8, 0x9e, 0x8b, 0xdd, 0x1d, 0x64, 0x68, 0x6a, 0x91, 0x90, 0x09, 0x17, 0x47, 122 | 0x70, 0x62, 0x25, 0x58, 0x97, 0x90, 0xb0, 0x1e, 0xc4, 0x5e, 0x64, 0x7b, 0xa4, 0x04, 0x51, 0x05, 123 | 0x0b, 0x72, 0x2a, 0x95, 0x18, 0x9c, 0x38, 0xa3, 0xd8, 0xc1, 0xa2, 0x05, 0x49, 0x49, 0x6c, 0x60, 124 | 0x57, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x21, 0x8c, 0xfc, 0xb4, 0x00, 0x00, 0x00, 125 | } 126 | 127 | // Reference imports to suppress errors if they are not otherwise used. 128 | var _ context.Context 129 | var _ grpc.ClientConn 130 | 131 | // This is a compile-time assertion to ensure that this generated file 132 | // is compatible with the grpc package it is being compiled against. 133 | const _ = grpc.SupportPackageIsVersion4 134 | 135 | // GreeterClient is the client API for Greeter service. 136 | // 137 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 138 | type GreeterClient interface { 139 | // Sends a greeting 140 | SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) 141 | } 142 | 143 | type greeterClient struct { 144 | cc *grpc.ClientConn 145 | } 146 | 147 | func NewGreeterClient(cc *grpc.ClientConn) GreeterClient { 148 | return &greeterClient{cc} 149 | } 150 | 151 | func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { 152 | out := new(HelloReply) 153 | err := c.cc.Invoke(ctx, "/greet.Greeter/SayHello", in, out, opts...) 154 | if err != nil { 155 | return nil, err 156 | } 157 | return out, nil 158 | } 159 | 160 | // GreeterServer is the server API for Greeter service. 161 | type GreeterServer interface { 162 | // Sends a greeting 163 | SayHello(context.Context, *HelloRequest) (*HelloReply, error) 164 | } 165 | 166 | // UnimplementedGreeterServer can be embedded to have forward compatible implementations. 167 | type UnimplementedGreeterServer struct { 168 | } 169 | 170 | func (*UnimplementedGreeterServer) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) { 171 | return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") 172 | } 173 | 174 | func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { 175 | s.RegisterService(&_Greeter_serviceDesc, srv) 176 | } 177 | 178 | func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 179 | in := new(HelloRequest) 180 | if err := dec(in); err != nil { 181 | return nil, err 182 | } 183 | if interceptor == nil { 184 | return srv.(GreeterServer).SayHello(ctx, in) 185 | } 186 | info := &grpc.UnaryServerInfo{ 187 | Server: srv, 188 | FullMethod: "/greet.Greeter/SayHello", 189 | } 190 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 191 | return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) 192 | } 193 | return interceptor(ctx, in, info, handler) 194 | } 195 | 196 | var _Greeter_serviceDesc = grpc.ServiceDesc{ 197 | ServiceName: "greet.Greeter", 198 | HandlerType: (*GreeterServer)(nil), 199 | Methods: []grpc.MethodDesc{ 200 | { 201 | MethodName: "SayHello", 202 | Handler: _Greeter_SayHello_Handler, 203 | }, 204 | }, 205 | Streams: []grpc.StreamDesc{}, 206 | Metadata: "greet.proto", 207 | } 208 | -------------------------------------------------------------------------------- /greet/greetpb/greet.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package="greetpb"; 4 | 5 | package greet; 6 | 7 | // The greeting service definition. 8 | service Greeter { 9 | // Sends a greeting 10 | rpc SayHello (HelloRequest) returns (HelloReply) {} 11 | } 12 | 13 | // The request message containing the user's name. 14 | message HelloRequest { 15 | string name = 1; 16 | } 17 | 18 | // The response message containing the greetings 19 | message HelloReply { 20 | string message = 1; 21 | } -------------------------------------------------------------------------------- /greet/greetpb/protoc-gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | protoc --go_out=plugins=grpc:. *.proto -------------------------------------------------------------------------------- /math/math_client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for Math service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "time" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/math/mathpb" 13 | "google.golang.org/grpc" 14 | ) 15 | 16 | func unaryCall(c pb.MathClient) { 17 | fmt.Printf("--- gRPC Unary RPC Call ---\n") 18 | // Make unary RPC 19 | req := &pb.SumRequest{ 20 | FirstNum: 10, 21 | SecondNum: 20, 22 | } 23 | resp, err := c.Sum(context.Background(), req) 24 | if err != nil { 25 | log.Fatalf("failed to call Sum: %v", err) 26 | } 27 | 28 | fmt.Printf("response:\n") 29 | fmt.Printf(" - %v\n", resp.Result) 30 | } 31 | 32 | func serverSideStreamingCall(c pb.MathClient) { 33 | fmt.Printf("--- gRPC Server-side Streaming RPC Call ---\n") 34 | // Make server-side streaming RPC 35 | req := &pb.PrimeFactorsRequest{Num: 48} 36 | stream, err := c.PrimeFactors(context.Background(), req) 37 | if err != nil { 38 | log.Fatalf("failed to call PrimeFactors: %v", err) 39 | } 40 | 41 | // Read all the responses 42 | var rpcStatus error 43 | fmt.Printf("response:\n") 44 | for { 45 | resp, err := stream.Recv() 46 | if err != nil { 47 | rpcStatus = err 48 | break 49 | } 50 | fmt.Printf(" - %v\n", resp.Result) 51 | } 52 | if rpcStatus != io.EOF { 53 | log.Fatalf("failed to finish server-side streaming: %v", rpcStatus) 54 | } 55 | } 56 | 57 | func clientSideStreamingCall(c pb.MathClient) { 58 | fmt.Printf("--- gRPC Client-side Streaming RPC Call ---\n") 59 | // Make client-side streaming RPC 60 | stream, err := c.Average(context.Background()) 61 | if err != nil { 62 | log.Fatalf("failed to call Average: %v", err) 63 | } 64 | 65 | // Send all requests to the server 66 | nums := []int32{10, 8, 2, 36, 24, 16, 98} 67 | for _, num := range nums { 68 | if err := stream.Send(&pb.AverageRequest{Num: num}); err != nil { 69 | log.Fatalf("failed to send streaming: %v", err) 70 | } 71 | } 72 | 73 | // Read the response 74 | resp, err := stream.CloseAndRecv() 75 | if err != nil { 76 | log.Fatalf("failed to CloseAndRecv: %v", err) 77 | } 78 | fmt.Printf("response:\n") 79 | fmt.Printf(" - %v\n", resp.Result) 80 | } 81 | 82 | func bidirectionalStreamingCall(c pb.MathClient) { 83 | fmt.Printf("--- gRPC Bidirectional Streaming RPC Call ---\n") 84 | // Make bidirectional streaming RPC 85 | stream, err := c.Maximum(context.Background()) 86 | if err != nil { 87 | log.Fatalf("failed to call Maximum: %v", err) 88 | } 89 | 90 | // goroutine: Send all requests to the server 91 | go func() { 92 | nums := []int32{2, 10, 8, 24, 16, 32, 98} 93 | for _, num := range nums { 94 | if err := stream.Send(&pb.MaximumRequest{Num: num}); err != nil { 95 | log.Fatalf("failed to send streaming: %v", err) 96 | } 97 | // Sleep 1 second 98 | time.Sleep(1 * time.Second) 99 | } 100 | // closes the send direction of the stream 101 | stream.CloseSend() 102 | }() 103 | 104 | // Read all the responses 105 | var rpcStatus error 106 | fmt.Printf("response:\n") 107 | for { 108 | resp, err := stream.Recv() 109 | if err != nil { 110 | rpcStatus = err 111 | break 112 | } 113 | fmt.Printf(" - %v\n", resp.Result) 114 | } 115 | if rpcStatus != io.EOF { 116 | log.Fatalf("failed to finish server streaming: %v", rpcStatus) 117 | } 118 | } 119 | 120 | func main() { 121 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 122 | flag.Parse() 123 | 124 | // Set up a connection to the server. 125 | conn, err := grpc.Dial(*addr, grpc.WithInsecure()) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 126 | if err != nil { 127 | log.Fatalf("did not connect: %v", err) 128 | } 129 | defer conn.Close() 130 | 131 | c := pb.NewMathClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewMathClient method provided in the pb package we generated from our .proto. 132 | 133 | // Contact the server and print out its response. 134 | // 1. Unary RPC Call 135 | // unaryCall(c) 136 | 137 | // 2. Server-side Streaming RPC Call 138 | // serverSideStreamingCall(c) 139 | 140 | // 3. Client-side Streaming RPC Call 141 | // clientSideStreamingCall(c) 142 | 143 | // 4. Bidirectional Streaming RPC Call 144 | bidirectionalStreamingCall(c) 145 | } 146 | -------------------------------------------------------------------------------- /math/math_server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for Math service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "net" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/math/mathpb" 13 | "google.golang.org/grpc" 14 | ) 15 | 16 | // server is used to implement mathpb.MathServer. 17 | type server struct{} 18 | 19 | // Sum implements mathpb.MathServer 20 | func (s *server) Sum(ctx context.Context, in *pb.SumRequest) (*pb.SumResponse, error) { 21 | fmt.Printf("--- gRPC Unary RPC ---\n") 22 | fmt.Printf("request received: %v\n", in) 23 | return &pb.SumResponse{Result: in.FirstNum + in.SecondNum}, nil 24 | } 25 | 26 | // PrimeFactors implements mathpb.MathServer 27 | func (s *server) PrimeFactors(in *pb.PrimeFactorsRequest, stream pb.Math_PrimeFactorsServer) error { 28 | fmt.Printf("--- gRPC Server-side Streaming RPC ---\n") 29 | fmt.Printf("request received: %v\n", in) 30 | 31 | num := in.Num 32 | factor := int64(2) 33 | 34 | for num > 1 { 35 | if num%factor == 0 { 36 | stream.Send(&pb.PrimeFactorsResponse{Result: factor}) 37 | num = num / factor 38 | continue 39 | } 40 | factor++ 41 | } 42 | 43 | return nil 44 | } 45 | 46 | // Average implements mathpb.MathServer 47 | func (s *server) Average(stream pb.Math_AverageServer) error { 48 | fmt.Printf("--- gRPC Client-side Streaming RPC ---\n") 49 | 50 | // Read requests and send responses 51 | var sum int32 52 | count := 0 53 | 54 | for { 55 | in, err := stream.Recv() 56 | 57 | if err == io.EOF { 58 | fmt.Printf("Receiving client streaming data completed\n") 59 | average := float64(sum) / float64(count) 60 | return stream.SendAndClose(&pb.AverageResponse{Result: average}) 61 | } 62 | 63 | fmt.Printf("request received: %v\n", in) 64 | 65 | if err != nil { 66 | log.Fatalf("Error while receiving client streaming data: %v", err) 67 | } 68 | 69 | sum += in.Num 70 | count++ 71 | } 72 | } 73 | 74 | // Maximum implements mathpb.MathServer 75 | func (s *server) Maximum(stream pb.Math_MaximumServer) error { 76 | fmt.Printf("--- gRPC Bidirectional Streaming RPC ---\n") 77 | 78 | // Read requests and send responses 79 | maximum := int32(0) 80 | 81 | for { 82 | in, err := stream.Recv() 83 | if err == io.EOF { 84 | fmt.Printf("Receiving client streaming data completed\n") 85 | return nil 86 | } 87 | if err != nil { 88 | log.Fatalf("Error while receiving client streaming data: %v", err) 89 | } 90 | 91 | num := in.Num 92 | fmt.Printf("request received: %v\n", in) 93 | 94 | if num > maximum { 95 | maximum = num 96 | if err := stream.Send(&pb.MaximumResponse{Result: maximum}); err != nil { 97 | log.Fatalf("Error while sending streaming data to client: %v", err) 98 | } 99 | } 100 | } 101 | } 102 | 103 | func main() { 104 | port := flag.Int("port", 50051, "the port to serve on") 105 | flag.Parse() 106 | 107 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests 108 | if err != nil { 109 | log.Fatalf("failed to listen: %v", err) 110 | } 111 | fmt.Printf("server listening at %v\n", lis.Addr()) 112 | 113 | s := grpc.NewServer() // Create an instance of the gRPC server 114 | pb.RegisterMathServer(s, &server{}) // Register our service implementation with the gRPC server 115 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 116 | log.Fatalf("failed to serve: %v", err) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /math/mathpb/math.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package="mathpb"; 4 | 5 | package math; 6 | 7 | // The math service definition. 8 | service Math { 9 | // Sum is unary RPC. 10 | rpc Sum(SumRequest) returns (SumResponse) {}; 11 | 12 | // PrimeFactors is server-side streaming RPC 13 | rpc PrimeFactors(PrimeFactorsRequest) returns (stream PrimeFactorsResponse) {}; 14 | 15 | // Average is client-side streaming RPC 16 | rpc Average(stream AverageRequest) returns (AverageResponse) {}; 17 | 18 | // Maximum is bi-directional streaming RPC 19 | rpc Maximum(stream MaximumRequest) returns (stream MaximumResponse) {}; 20 | } 21 | 22 | // The request message for Sum. 23 | message SumRequest { 24 | int32 first_num = 1; 25 | int32 second_num = 2; 26 | } 27 | 28 | // The response message for Sum. 29 | message SumResponse { 30 | int32 result = 1; 31 | } 32 | 33 | // The request message for PrimeFactors. 34 | message PrimeFactorsRequest { 35 | int64 num = 1; 36 | } 37 | 38 | // The response message for PrimeFactors. 39 | message PrimeFactorsResponse { 40 | int64 result = 1; 41 | } 42 | 43 | // The request message for Average. 44 | message AverageRequest { 45 | int32 num = 1; 46 | } 47 | 48 | // The response message for Average. 49 | message AverageResponse { 50 | double result = 1; 51 | } 52 | 53 | // The request message for Maximum. 54 | message MaximumRequest { 55 | int32 num = 1; 56 | } 57 | 58 | // The response message for Maximum. 59 | message MaximumResponse { 60 | int32 result = 1; 61 | } -------------------------------------------------------------------------------- /math/mathpb/protoc-gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | protoc --go_out=plugins=grpc:. *.proto -------------------------------------------------------------------------------- /restful-api-plus/client/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3TCCAsWgAwIBAgIJAKoKZuLTxD+eMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD 3 | VQQGEwJDTjESMBAGA1UECAwJR3VhbmdEb25nMREwDwYDVQQHDAhTaGVuWmhlbjEd 4 | MBsGA1UECgwUTWFkbWFsbHMuY29tIENvLixMdGQxHTAbBgNVBAsMFE1hZG1hbGxz 5 | LmNvbSBSb290IENBMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDUyOTA3MjMwM1oX 6 | DTI5MDUyNjA3MjMwM1owgYQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0Rv 7 | bmcxETAPBgNVBAcMCFNoZW5aaGVuMR0wGwYDVQQKDBRNYWRtYWxscy5jb20gQ28u 8 | LEx0ZDEdMBsGA1UECwwUTWFkbWFsbHMuY29tIFJvb3QgQ0ExEDAOBgNVBAMMB1Jv 9 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsaZ3C8XzVTfir 10 | 2LZpJMMnxo2ZUSRcjrVRav3ATtcubqE94XT/nq5wrUClhvOWKeUIFaPZHY5dXSLm 11 | LAIRaCNW6t0cHcfCyd6ArVbjVCT8dbo1wwis2ATFRVRiVmuFjPSu2iLR/pm9m94Q 12 | C1uZe4ypXGW08TieivNiRTflfXNvjKzDENJriEQ6ukhwAixOLZcbYKZKi4wZXscl 13 | iaBcitcCZty0ODtH7TXQbrQNU2lkQBT/HmtABgL4ln5p+tiDUTNCJjvPeNBT4FTs 14 | KLtcnJM4NJspRwHO4YIGGHhmgJhneSPsHm5poE6pVu3DDRFHuforPv/k70cX9PGz 15 | OQlREfNRAgMBAAGjUDBOMB0GA1UdDgQWBBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAf 16 | BgNVHSMEGDAWgBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAMBgNVHRMEBTADAQH/MA0G 17 | CSqGSIb3DQEBCwUAA4IBAQCfcXII5evHWW5qcF3f2wMRCFhizOCojQ6CA2mwH9LN 18 | GsZRclTwniQ7OqH67b94ii3mF3dUK7klth0jbx6rJ9jYsGZ6dCuK6a1MWtCey8P4 19 | VEbiBA2qXwjJzeap1G9mdPz16YTRlbdLhJDgN7fxmTK+A4GOObiq8x2EX9DjjCym 20 | fI39D0Z9o/2i29KZGC25DWYvjnLrz3EjfGkct1QrQG9q93pwQNKm+SKHRoUIOExe 21 | KsmgS0jPxie/IUWSDHlkCY19tHSX2swRNjkiomt1pKOozN3eTu1qs29f2HA/tKAw 22 | 6/Hwdo0XD+ybVEHq/STsppCe4zMojmChxuDVTYo2de3C 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /restful-api-plus/client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for User service. 2 | package main 3 | 4 | import ( 5 | "time" 6 | "context" 7 | "flag" 8 | "log" 9 | 10 | "google.golang.org/grpc/credentials" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/restful-api/userpb" 13 | "google.golang.org/grpc" 14 | ) 15 | 16 | func createUserCall(client pb.UserServiceClient, username, password string) { 17 | log.Println("--- gRPC Create RPC Call ---") 18 | 19 | // 设置 10 秒超时时长,可参考 https://madmalls.com/blog/post/grpc-deadline/#21-contextwithtimeout 20 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 21 | defer cancel() 22 | 23 | // 调用 Create RPC 24 | req := &pb.CreateRequest{ 25 | User: &pb.User{ 26 | Username: username, 27 | Password: password, 28 | }, 29 | } 30 | resp, err := client.Create(ctx, req) 31 | if err != nil { 32 | log.Fatalf("failed to call Create RPC: %v", err) 33 | } 34 | 35 | log.Println("response:") 36 | log.Printf(" - %q\n", resp) 37 | } 38 | 39 | func getUserCall(client pb.UserServiceClient, username string) { 40 | log.Println("--- gRPC Get RPC Call ---") 41 | 42 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 43 | defer cancel() 44 | 45 | // 调用 Get RPC 46 | req := &pb.GetRequest{ 47 | Username: username, 48 | } 49 | resp, err := client.Get(ctx, req) 50 | if err != nil { 51 | log.Fatalf("failed to call Get RPC: %v", err) 52 | } 53 | 54 | log.Println("response:") 55 | log.Printf(" - %q\n", resp) 56 | } 57 | 58 | func main() { 59 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 60 | certFile := flag.String("cacert", "cacert.pem", "CA root certificate") 61 | flag.Parse() 62 | 63 | creds, err := credentials.NewClientTLSFromFile(*certFile, "") 64 | if err != nil { 65 | log.Fatalf("failed to load CA root certificate: %v", err) 66 | } 67 | 68 | // Set up a connection to the server. 69 | conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(creds)) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 70 | if err != nil { 71 | log.Fatalf("did not connect: %v", err) 72 | } 73 | defer conn.Close() 74 | 75 | c := pb.NewUserServiceClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewEchoClient method provided in the pb package we generated from our .proto. 76 | 77 | // Contact the server and print out its response. 78 | // 1. Create RPC Call 79 | createUserCall(c, "Alice", "123") 80 | createUserCall(c, "Bob", "pass") 81 | 82 | // 2. Get RPC Call 83 | getUserCall(c, "Alice") 84 | } 85 | -------------------------------------------------------------------------------- /restful-api-plus/server/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3TCCAsWgAwIBAgIJAKoKZuLTxD+eMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD 3 | VQQGEwJDTjESMBAGA1UECAwJR3VhbmdEb25nMREwDwYDVQQHDAhTaGVuWmhlbjEd 4 | MBsGA1UECgwUTWFkbWFsbHMuY29tIENvLixMdGQxHTAbBgNVBAsMFE1hZG1hbGxz 5 | LmNvbSBSb290IENBMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDUyOTA3MjMwM1oX 6 | DTI5MDUyNjA3MjMwM1owgYQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0Rv 7 | bmcxETAPBgNVBAcMCFNoZW5aaGVuMR0wGwYDVQQKDBRNYWRtYWxscy5jb20gQ28u 8 | LEx0ZDEdMBsGA1UECwwUTWFkbWFsbHMuY29tIFJvb3QgQ0ExEDAOBgNVBAMMB1Jv 9 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsaZ3C8XzVTfir 10 | 2LZpJMMnxo2ZUSRcjrVRav3ATtcubqE94XT/nq5wrUClhvOWKeUIFaPZHY5dXSLm 11 | LAIRaCNW6t0cHcfCyd6ArVbjVCT8dbo1wwis2ATFRVRiVmuFjPSu2iLR/pm9m94Q 12 | C1uZe4ypXGW08TieivNiRTflfXNvjKzDENJriEQ6ukhwAixOLZcbYKZKi4wZXscl 13 | iaBcitcCZty0ODtH7TXQbrQNU2lkQBT/HmtABgL4ln5p+tiDUTNCJjvPeNBT4FTs 14 | KLtcnJM4NJspRwHO4YIGGHhmgJhneSPsHm5poE6pVu3DDRFHuforPv/k70cX9PGz 15 | OQlREfNRAgMBAAGjUDBOMB0GA1UdDgQWBBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAf 16 | BgNVHSMEGDAWgBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAMBgNVHRMEBTADAQH/MA0G 17 | CSqGSIb3DQEBCwUAA4IBAQCfcXII5evHWW5qcF3f2wMRCFhizOCojQ6CA2mwH9LN 18 | GsZRclTwniQ7OqH67b94ii3mF3dUK7klth0jbx6rJ9jYsGZ6dCuK6a1MWtCey8P4 19 | VEbiBA2qXwjJzeap1G9mdPz16YTRlbdLhJDgN7fxmTK+A4GOObiq8x2EX9DjjCym 20 | fI39D0Z9o/2i29KZGC25DWYvjnLrz3EjfGkct1QrQG9q93pwQNKm+SKHRoUIOExe 21 | KsmgS0jPxie/IUWSDHlkCY19tHSX2swRNjkiomt1pKOozN3eTu1qs29f2HA/tKAw 22 | 6/Hwdo0XD+ybVEHq/STsppCe4zMojmChxuDVTYo2de3C 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /restful-api-plus/server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for User service. 2 | package main 3 | 4 | import ( 5 | "mime" 6 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 7 | "strings" 8 | "net/http" 9 | "context" 10 | "flag" 11 | "fmt" 12 | "log" 13 | 14 | "google.golang.org/grpc/credentials" 15 | 16 | "github.com/golang/protobuf/ptypes/empty" 17 | "google.golang.org/grpc/codes" 18 | 19 | pb "github.com/wangy8961/grpc-go-tutorial/restful-api-plus/userpb" 20 | swagger "github.com/wangy8961/grpc-go-tutorial/restful-api-plus/go-bindata-assetfs" 21 | "github.com/elazarl/go-bindata-assetfs" 22 | "google.golang.org/grpc" 23 | ) 24 | 25 | // server is used to implement pb.UserServiceServer. 26 | type server struct { 27 | users map[string]pb.User 28 | } 29 | 30 | // NewServer creates User service 31 | func NewServer() pb.UserServiceServer { 32 | return &server{ 33 | users: make(map[string]pb.User), 34 | } 35 | } 36 | 37 | // Create a new user 38 | func (s *server) Create(ctx context.Context, req *pb.CreateRequest) (*empty.Empty, error) { 39 | log.Println("--- Creating new user... ---") 40 | log.Printf("request received: %v\n", req) 41 | 42 | user := req.GetUser() 43 | if user.Username == "" { 44 | return nil, grpc.Errorf(codes.InvalidArgument, "username cannot be empty") 45 | } 46 | if user.Password == "" { 47 | return nil, grpc.Errorf(codes.InvalidArgument, "password cannot be empty") 48 | } 49 | 50 | s.users[user.Username] = *user 51 | 52 | log.Println("--- User created! ---") 53 | return &empty.Empty{}, nil 54 | } 55 | 56 | // Get a specified user 57 | func (s *server) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) { 58 | log.Println("--- Getting user... ---") 59 | 60 | if req.Username == "" { 61 | return nil, grpc.Errorf(codes.InvalidArgument, "username cannot be empty") 62 | } 63 | 64 | u, ok := s.users[req.Username] 65 | if !ok { 66 | log.Println("--- User not found! ---") 67 | return nil, grpc.Errorf(codes.NotFound, "user not found") 68 | } 69 | 70 | log.Println("--- User found! ---") 71 | return &pb.GetResponse{User: &u}, nil 72 | } 73 | 74 | // grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC 75 | // connections or otherHandler otherwise. Copied from cockroachdb. 76 | func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler { 77 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 78 | // This is a partial recreation of gRPC's internal checks https://github.com/grpc/grpc-go/pull/514/files#diff-95e9a25b738459a2d3030e1e6fa2a718R61 79 | if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") { 80 | grpcServer.ServeHTTP(w, r) 81 | } else { 82 | otherHandler.ServeHTTP(w, r) 83 | } 84 | }) 85 | } 86 | 87 | func serveSwagger(mux *http.ServeMux) { 88 | mime.AddExtensionType(".svg", "image/svg+xml") 89 | 90 | // Expose files in third_party/swagger-ui/ on /swagger-ui 91 | fileServer := http.FileServer(&assetfs.AssetFS{ 92 | Asset: swagger.Asset, 93 | AssetDir: swagger.AssetDir, 94 | Prefix: "third_party/swagger-ui", 95 | }) 96 | prefix := "/swagger-ui/" 97 | mux.Handle(prefix, http.StripPrefix(prefix, fileServer)) 98 | } 99 | 100 | func main() { 101 | host := flag.String("host", "localhost", "gRPC Server Name or IP") 102 | port := flag.Int("port", 50051, "the port to serve on") 103 | certFile := flag.String("certfile", "server.crt", "Server certificate") 104 | keyFile := flag.String("keyfile", "server.key", "Server private key") 105 | caCertFile := flag.String("cacert", "cacert.pem", "CA root certificate") 106 | swaggerJSON := flag.String("swagger", "../userpb/service.swagger.json", "Swagger JSON file") 107 | flag.Parse() 108 | 109 | // gRPC 服务和反向代理服务共同监听的地址 110 | endpoint := fmt.Sprintf("%s:%d", *host, *port) 111 | 112 | // gRPC 服务端 113 | creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) 114 | if err != nil { 115 | log.Fatalf("failed to load certificates: %v", err) 116 | } 117 | grpcServer := grpc.NewServer(grpc.Creds(creds)) // Create an instance of the gRPC server 118 | pb.RegisterUserServiceServer(grpcServer, NewServer()) // Register our service implementation with the gRPC server 119 | 120 | // grpc-gateway 反向代理 121 | // 它相当于 gRPC 客户端,负责将 RESTful API 的客户端的请求转发给 gRPC 服务端 122 | ctx, cancel := context.WithCancel(context.Background()) 123 | defer cancel() 124 | gwCreds, gwErr := credentials.NewClientTLSFromFile(*caCertFile, "") 125 | if gwErr != nil { 126 | log.Fatalf("failed to load CA root certificate: %v", gwErr) 127 | } 128 | opts := []grpc.DialOption{grpc.WithTransportCredentials(gwCreds)} 129 | gwMux := runtime.NewServeMux() 130 | gwErr = pb.RegisterUserServiceHandlerFromEndpoint(ctx, gwMux, endpoint, opts) 131 | if gwErr != nil { 132 | log.Fatalf("failed to register grpc-gateway: %v", gwErr) 133 | } 134 | 135 | // 指定 gRPC-gateway 反向代理所有的 HTTP2 服务的路由 136 | mux := http.NewServeMux() 137 | mux.Handle("/", gwMux) 138 | // Swagger 139 | mux.HandleFunc("/swagger.json", func(w http.ResponseWriter, r *http.Request) { 140 | // io.Copy(w, strings.NewReader(*swaggerJSON)) 141 | http.ServeFile(w, r, *swaggerJSON) 142 | }) 143 | serveSwagger(mux) 144 | // 启动 HTTP2 服务器(需要指定服务器的数字证书和私钥) 145 | srv := &http.Server{ 146 | Addr: endpoint, 147 | Handler: grpcHandlerFunc(grpcServer, mux), // HTTP2 服务器接收到任何请求后,再由 grpcHandlerFunc 根据请求的协议判断是直接调用 gRPC 服务端还是由 gRPC-gateway 继续反向代理 148 | } 149 | 150 | log.Printf("gRPC server and gRPC-gateway listening at %v\n", endpoint) 151 | if httpErr := srv.ListenAndServeTLS(*certFile, *keyFile); httpErr != nil { 152 | log.Fatalf("failed to listen and serve: %v", httpErr) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /restful-api-plus/server/server.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 (0x5) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: C=CN, ST=GuangDong, L=ShenZhen, O=Madmalls.com Co.,Ltd, OU=Madmalls.com Root CA, CN=Root CA 7 | Validity 8 | Not Before: Jun 11 07:22:30 2019 GMT 9 | Not After : Jun 10 07:22:30 2020 GMT 10 | Subject: C=CN, ST=GuangDong, O=Madmalls.com Co.,Ltd, OU=gRPC Server, CN=*.wangy.com 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | Public-Key: (2048 bit) 14 | Modulus: 15 | 00:e2:93:8c:43:64:e9:c6:02:6b:d1:98:f3:79:47: 16 | d5:99:c6:46:a8:6d:24:cc:a0:d8:3b:ec:e4:d0:83: 17 | 79:42:f5:bf:23:e1:0e:34:b3:f1:35:c6:27:6e:44: 18 | 58:48:ca:91:42:8c:6c:2f:6c:9d:09:d0:1b:73:d8: 19 | 42:f4:70:71:6c:40:f7:6c:71:63:39:db:d1:f8:f6: 20 | d4:f8:b7:18:03:7a:7d:32:0c:3f:f4:fd:0a:4c:e3: 21 | ad:71:33:8b:e6:25:53:52:d8:72:67:9f:56:1b:7e: 22 | d4:b7:27:28:0e:1d:28:92:b6:94:09:4f:bd:51:ef: 23 | 06:bc:80:39:97:cb:d2:20:8c:0c:ff:cf:33:97:7d: 24 | 7a:0e:01:e7:90:a2:30:64:34:be:3e:78:d9:3f:19: 25 | 7b:27:fc:c1:b2:0d:07:34:8b:6e:2e:0b:50:f4:8a: 26 | be:07:63:98:25:24:00:97:89:d2:ad:96:cd:56:01: 27 | 84:df:dc:af:be:a8:12:ef:f8:77:75:46:2d:49:21: 28 | ae:1c:bc:b1:87:94:4d:8e:b7:5d:7c:01:0a:c6:e6: 29 | 32:32:47:c3:d1:38:ab:5d:02:4f:72:c5:10:9c:a2: 30 | 83:9d:ee:a1:37:ee:02:bb:50:8a:78:ba:bf:f7:f7: 31 | 69:b0:ff:17:31:14:68:a5:db:05:ef:ab:2e:53:ac: 32 | ae:df 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Subject Alternative Name: 36 | DNS:www.wangy.com, DNS:blog.wangy.com, IP Address:192.168.40.123 37 | Signature Algorithm: sha256WithRSAEncryption 38 | 42:83:8d:68:cf:9b:93:59:63:94:5d:62:a8:14:bb:79:f4:09: 39 | cf:6d:ea:e5:86:bd:9a:fc:28:e2:9b:c5:fa:df:e9:50:b0:ac: 40 | 28:4c:7a:ff:38:67:1a:9b:e6:aa:8d:bd:da:3d:6f:e8:49:fa: 41 | f2:7e:fa:dd:4b:a8:4d:06:ee:1c:47:ff:36:99:23:24:9b:4c: 42 | 19:d1:f4:1c:85:c7:94:84:54:19:b0:78:6f:d7:54:25:a8:da: 43 | b1:18:76:1b:71:15:ad:35:d5:64:b6:6f:ee:80:6d:82:98:71: 44 | b8:db:bb:e5:01:da:f7:f1:aa:97:a7:ae:f8:90:b6:f6:ac:38: 45 | fa:9e:a2:15:10:2f:ca:c5:eb:bf:91:67:bc:f7:ee:2a:0e:80: 46 | 35:ad:6d:03:20:98:4f:df:39:5b:ac:f2:ef:43:01:95:b9:0a: 47 | 7b:29:b4:6d:83:de:1f:94:a6:69:6b:42:18:ef:cb:e6:68:66: 48 | b2:79:59:f8:ae:9f:89:25:ec:ee:46:f9:3c:34:ab:74:7d:72: 49 | 0a:3f:39:6e:07:28:79:e5:8c:8b:3a:da:1f:b9:7c:af:0c:0a: 50 | 0c:71:3d:aa:76:87:69:4c:75:9a:0b:8f:8e:10:f8:d6:53:c9: 51 | 35:8b:10:42:0a:32:01:a5:83:e3:22:0e:86:c0:1d:87:75:af: 52 | cb:30:cb:1a 53 | -----BEGIN CERTIFICATE----- 54 | MIIDnjCCAoagAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMCQ04x 55 | EjAQBgNVBAgMCUd1YW5nRG9uZzERMA8GA1UEBwwIU2hlblpoZW4xHTAbBgNVBAoM 56 | FE1hZG1hbGxzLmNvbSBDby4sTHRkMR0wGwYDVQQLDBRNYWRtYWxscy5jb20gUm9v 57 | dCBDQTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xOTA2MTEwNzIyMzBaFw0yMDA2MTAw 58 | NzIyMzBaMGwxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxHTAbBgNV 59 | BAoMFE1hZG1hbGxzLmNvbSBDby4sTHRkMRQwEgYDVQQLDAtnUlBDIFNlcnZlcjEU 60 | MBIGA1UEAwwLKi53YW5neS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 61 | AoIBAQDik4xDZOnGAmvRmPN5R9WZxkaobSTMoNg77OTQg3lC9b8j4Q40s/E1xidu 62 | RFhIypFCjGwvbJ0J0Btz2EL0cHFsQPdscWM529H49tT4txgDen0yDD/0/QpM461x 63 | M4vmJVNS2HJnn1YbftS3JygOHSiStpQJT71R7wa8gDmXy9IgjAz/zzOXfXoOAeeQ 64 | ojBkNL4+eNk/GXsn/MGyDQc0i24uC1D0ir4HY5glJACXidKtls1WAYTf3K++qBLv 65 | +Hd1Ri1JIa4cvLGHlE2Ot118AQrG5jIyR8PROKtdAk9yxRCcooOd7qE37gK7UIp4 66 | ur/392mw/xcxFGil2wXvqy5TrK7fAgMBAAGjMjAwMC4GA1UdEQQnMCWCDXd3dy53 67 | YW5neS5jb22CDmJsb2cud2FuZ3kuY29thwTAqCh7MA0GCSqGSIb3DQEBCwUAA4IB 68 | AQBCg41oz5uTWWOUXWKoFLt59AnPberlhr2a/Cjim8X63+lQsKwoTHr/OGcam+aq 69 | jb3aPW/oSfryfvrdS6hNBu4cR/82mSMkm0wZ0fQchceUhFQZsHhv11QlqNqxGHYb 70 | cRWtNdVktm/ugG2CmHG427vlAdr38aqXp674kLb2rDj6nqIVEC/Kxeu/kWe89+4q 71 | DoA1rW0DIJhP3zlbrPLvQwGVuQp7KbRtg94flKZpa0IY78vmaGayeVn4rp+JJezu 72 | Rvk8NKt0fXIKPzluByh55YyLOtofuXyvDAoMcT2qdodpTHWaC4+OEPjWU8k1ixBC 73 | CjIBpYPjIg6GwB2Hda/LMMsa 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /restful-api-plus/server/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpgIBAAKCAQEA4pOMQ2TpxgJr0ZjzeUfVmcZGqG0kzKDYO+zk0IN5QvW/I+EO 3 | NLPxNcYnbkRYSMqRQoxsL2ydCdAbc9hC9HBxbED3bHFjOdvR+PbU+LcYA3p9Mgw/ 4 | 9P0KTOOtcTOL5iVTUthyZ59WG37UtycoDh0okraUCU+9Ue8GvIA5l8vSIIwM/88z 5 | l316DgHnkKIwZDS+PnjZPxl7J/zBsg0HNItuLgtQ9Iq+B2OYJSQAl4nSrZbNVgGE 6 | 39yvvqgS7/h3dUYtSSGuHLyxh5RNjrddfAEKxuYyMkfD0TirXQJPcsUQnKKDne6h 7 | N+4Cu1CKeLq/9/dpsP8XMRRopdsF76suU6yu3wIDAQABAoIBAQCjrPDjco/KAc+/ 8 | ft1LnI/6YRiD7SxrQjpSt+PnmUJNE9e7ZIXtnpu+O+IaLvcTxnm++E/ixnR/NT3P 9 | psdfa6cUC65xQUvr7Rc24aCh9yo6wQ6Vy/Gb2fvJ5aNSpmkGnaoeq8uhfaInhKzH 10 | jlrKL1gy+//e5iKegKx+GacBODUYWpc8bVoROpvqgtSDg1wtuwUU/5fy6f8IIHuT 11 | 78cPm6t39ylcFkV3lt1yS4LhvUoW2Nlm5SHOicDc/cYVE8xqfJlARzN9xUfM+5LB 12 | sUdbR5UZOxOPsd24q1D1DZc1s34soj/wKXPVyyuhp0o7Db6vSsxQDfOetfuRUSR6 13 | +5zPMLqxAoGBAP6laEBin8SVThAvQmS4PoyqPK8Hb0GgjxPGxyXyrtSL3/Xy86E0 14 | vhOGTm8gh2oA0wZQ3KIg5TIhVa3G+UNMbZmIuYCQdeAQpl/sdMnpw66aNtHj6kt7 15 | gqzWzH1en4w2T3DxU7LtCQIx4q3Akv0oyZ/YovlNb5dkSHtuz4ZVRYkXAoGBAOPH 16 | 74KHzkD/IWkGf7/QG9XQ93hjggxmDS/WzeKwKYewPyZoWRvkvj7i7d+P1AUrCvgd 17 | YeB132S0S1JrSUP4Fn+DNwgj3Kyg2Fq2/HmwTKkqVPjOKlonTKlA+h3fNG/NbJDE 18 | E41yFkK+zjSbxxcAYLCgR2A9PCEyXPXyV20U8BV5AoGBAJnq5unL8yBC0u2Lc0kn 19 | 6H7jw0xUZRY482KTyvoQB0bnyRaDpGkzVRS+IJihA9i56NOverzwvzie14fzdeUM 20 | xE6CSwX/y5AE4FuotCr7hlD6W9pgNdUsMZ9BMlcxI6T/iuMMq3fCOKi/+HDnrrEg 21 | v0ZEDrY77RCICBu7repXjnE/AoGBAIdBvhOAmRU3apt25Hz+EslQoOK4FA1QvBvg 22 | LbmiacbM/XLNG7zYg6/MCPxr57Z57LWQnQIwfErMVL3IP2VA9/sX66HFydAoYtDb 23 | P+jyq1L4dCSaJ8QI+hi3IM6EMBsDnKgKBqJDULypmMDcj8g0zTWUt02Kjx4XTeQt 24 | 14RKnpXhAoGBAJkImhczkTucb/Yhaw1du2IkylPSb+HAJH6NFH9LFNvKYOIFfUaA 25 | id8ZkR5WGDJiK7vm3QT4n6j14/CnTXqrQ52W9ixxOIjbd1AHgHNwlbQGJodInT+l 26 | j3Iqa44peXerIk0MO8qYEjEGybHu7DhZhkbZoCn1Qqn+XpDbx2bbPe7w 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /restful-api-plus/third_party/swagger-ui/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangy8961/grpc-go-tutorial/7605120c1b91d7cd4e47b63284027a64cdcc8a17/restful-api-plus/third_party/swagger-ui/favicon-16x16.png -------------------------------------------------------------------------------- /restful-api-plus/third_party/swagger-ui/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangy8961/grpc-go-tutorial/7605120c1b91d7cd4e47b63284027a64cdcc8a17/restful-api-plus/third_party/swagger-ui/favicon-32x32.png -------------------------------------------------------------------------------- /restful-api-plus/third_party/swagger-ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Swagger UI 7 | 8 | 9 | 10 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /restful-api-plus/third_party/swagger-ui/oauth2-redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 68 | -------------------------------------------------------------------------------- /restful-api-plus/third_party/swagger-ui/swagger-ui.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""} -------------------------------------------------------------------------------- /restful-api-plus/userpb/protoc-gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 1. Generate gRPC Golang stub 4 | protoc -I/usr/local/include -I. \ 5 | -I$GOPATH/src \ 6 | -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 7 | --go_out=plugins=grpc:. \ 8 | service.proto 9 | 10 | # 2. Generate a reverse-proxy server which translates a RESTful JSON API into gRPC 11 | protoc -I/usr/local/include -I. \ 12 | -I$GOPATH/src \ 13 | -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 14 | --grpc-gateway_out=logtostderr=true:. \ 15 | service.proto 16 | 17 | # 3. (Optional) Generate swagger definitions 18 | protoc -I/usr/local/include -I. \ 19 | -I$GOPATH/src \ 20 | -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 21 | --swagger_out=logtostderr=true:. \ 22 | service.proto -------------------------------------------------------------------------------- /restful-api-plus/userpb/service.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: service.proto 3 | 4 | package userpb 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | proto "github.com/golang/protobuf/proto" 10 | empty "github.com/golang/protobuf/ptypes/empty" 11 | _ "google.golang.org/genproto/googleapis/api/annotations" 12 | grpc "google.golang.org/grpc" 13 | codes "google.golang.org/grpc/codes" 14 | status "google.golang.org/grpc/status" 15 | math "math" 16 | ) 17 | 18 | // Reference imports to suppress errors if they are not otherwise used. 19 | var _ = proto.Marshal 20 | var _ = fmt.Errorf 21 | var _ = math.Inf 22 | 23 | // This is a compile-time assertion to ensure that this generated file 24 | // is compatible with the proto package it is being compiled against. 25 | // A compilation error at this line likely means your copy of the 26 | // proto package needs to be updated. 27 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 28 | 29 | // User define a user 30 | type User struct { 31 | Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` 32 | Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` 33 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 34 | XXX_unrecognized []byte `json:"-"` 35 | XXX_sizecache int32 `json:"-"` 36 | } 37 | 38 | func (m *User) Reset() { *m = User{} } 39 | func (m *User) String() string { return proto.CompactTextString(m) } 40 | func (*User) ProtoMessage() {} 41 | func (*User) Descriptor() ([]byte, []int) { 42 | return fileDescriptor_a0b84a42fa06f626, []int{0} 43 | } 44 | 45 | func (m *User) XXX_Unmarshal(b []byte) error { 46 | return xxx_messageInfo_User.Unmarshal(m, b) 47 | } 48 | func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 49 | return xxx_messageInfo_User.Marshal(b, m, deterministic) 50 | } 51 | func (m *User) XXX_Merge(src proto.Message) { 52 | xxx_messageInfo_User.Merge(m, src) 53 | } 54 | func (m *User) XXX_Size() int { 55 | return xxx_messageInfo_User.Size(m) 56 | } 57 | func (m *User) XXX_DiscardUnknown() { 58 | xxx_messageInfo_User.DiscardUnknown(m) 59 | } 60 | 61 | var xxx_messageInfo_User proto.InternalMessageInfo 62 | 63 | func (m *User) GetUsername() string { 64 | if m != nil { 65 | return m.Username 66 | } 67 | return "" 68 | } 69 | 70 | func (m *User) GetPassword() string { 71 | if m != nil { 72 | return m.Password 73 | } 74 | return "" 75 | } 76 | 77 | // CreateRequest is the request for creating a user. 78 | type CreateRequest struct { 79 | User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` 80 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 81 | XXX_unrecognized []byte `json:"-"` 82 | XXX_sizecache int32 `json:"-"` 83 | } 84 | 85 | func (m *CreateRequest) Reset() { *m = CreateRequest{} } 86 | func (m *CreateRequest) String() string { return proto.CompactTextString(m) } 87 | func (*CreateRequest) ProtoMessage() {} 88 | func (*CreateRequest) Descriptor() ([]byte, []int) { 89 | return fileDescriptor_a0b84a42fa06f626, []int{1} 90 | } 91 | 92 | func (m *CreateRequest) XXX_Unmarshal(b []byte) error { 93 | return xxx_messageInfo_CreateRequest.Unmarshal(m, b) 94 | } 95 | func (m *CreateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 96 | return xxx_messageInfo_CreateRequest.Marshal(b, m, deterministic) 97 | } 98 | func (m *CreateRequest) XXX_Merge(src proto.Message) { 99 | xxx_messageInfo_CreateRequest.Merge(m, src) 100 | } 101 | func (m *CreateRequest) XXX_Size() int { 102 | return xxx_messageInfo_CreateRequest.Size(m) 103 | } 104 | func (m *CreateRequest) XXX_DiscardUnknown() { 105 | xxx_messageInfo_CreateRequest.DiscardUnknown(m) 106 | } 107 | 108 | var xxx_messageInfo_CreateRequest proto.InternalMessageInfo 109 | 110 | func (m *CreateRequest) GetUser() *User { 111 | if m != nil { 112 | return m.User 113 | } 114 | return nil 115 | } 116 | 117 | // GetRequest is the request for getting a user. 118 | type GetRequest struct { 119 | Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` 120 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 121 | XXX_unrecognized []byte `json:"-"` 122 | XXX_sizecache int32 `json:"-"` 123 | } 124 | 125 | func (m *GetRequest) Reset() { *m = GetRequest{} } 126 | func (m *GetRequest) String() string { return proto.CompactTextString(m) } 127 | func (*GetRequest) ProtoMessage() {} 128 | func (*GetRequest) Descriptor() ([]byte, []int) { 129 | return fileDescriptor_a0b84a42fa06f626, []int{2} 130 | } 131 | 132 | func (m *GetRequest) XXX_Unmarshal(b []byte) error { 133 | return xxx_messageInfo_GetRequest.Unmarshal(m, b) 134 | } 135 | func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 136 | return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic) 137 | } 138 | func (m *GetRequest) XXX_Merge(src proto.Message) { 139 | xxx_messageInfo_GetRequest.Merge(m, src) 140 | } 141 | func (m *GetRequest) XXX_Size() int { 142 | return xxx_messageInfo_GetRequest.Size(m) 143 | } 144 | func (m *GetRequest) XXX_DiscardUnknown() { 145 | xxx_messageInfo_GetRequest.DiscardUnknown(m) 146 | } 147 | 148 | var xxx_messageInfo_GetRequest proto.InternalMessageInfo 149 | 150 | func (m *GetRequest) GetUsername() string { 151 | if m != nil { 152 | return m.Username 153 | } 154 | return "" 155 | } 156 | 157 | // GetRequest is the response for getting a user. 158 | type GetResponse struct { 159 | User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` 160 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 161 | XXX_unrecognized []byte `json:"-"` 162 | XXX_sizecache int32 `json:"-"` 163 | } 164 | 165 | func (m *GetResponse) Reset() { *m = GetResponse{} } 166 | func (m *GetResponse) String() string { return proto.CompactTextString(m) } 167 | func (*GetResponse) ProtoMessage() {} 168 | func (*GetResponse) Descriptor() ([]byte, []int) { 169 | return fileDescriptor_a0b84a42fa06f626, []int{3} 170 | } 171 | 172 | func (m *GetResponse) XXX_Unmarshal(b []byte) error { 173 | return xxx_messageInfo_GetResponse.Unmarshal(m, b) 174 | } 175 | func (m *GetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 176 | return xxx_messageInfo_GetResponse.Marshal(b, m, deterministic) 177 | } 178 | func (m *GetResponse) XXX_Merge(src proto.Message) { 179 | xxx_messageInfo_GetResponse.Merge(m, src) 180 | } 181 | func (m *GetResponse) XXX_Size() int { 182 | return xxx_messageInfo_GetResponse.Size(m) 183 | } 184 | func (m *GetResponse) XXX_DiscardUnknown() { 185 | xxx_messageInfo_GetResponse.DiscardUnknown(m) 186 | } 187 | 188 | var xxx_messageInfo_GetResponse proto.InternalMessageInfo 189 | 190 | func (m *GetResponse) GetUser() *User { 191 | if m != nil { 192 | return m.User 193 | } 194 | return nil 195 | } 196 | 197 | func init() { 198 | proto.RegisterType((*User)(nil), "user.User") 199 | proto.RegisterType((*CreateRequest)(nil), "user.CreateRequest") 200 | proto.RegisterType((*GetRequest)(nil), "user.GetRequest") 201 | proto.RegisterType((*GetResponse)(nil), "user.GetResponse") 202 | } 203 | 204 | func init() { proto.RegisterFile("service.proto", fileDescriptor_a0b84a42fa06f626) } 205 | 206 | var fileDescriptor_a0b84a42fa06f626 = []byte{ 207 | // 292 bytes of a gzipped FileDescriptorProto 208 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x50, 0xc1, 0x4a, 0x03, 0x31, 209 | 0x10, 0x65, 0x6b, 0x29, 0x75, 0x96, 0x05, 0x8d, 0x20, 0x4b, 0x14, 0x29, 0x39, 0x15, 0xc1, 0x04, 210 | 0xeb, 0xcd, 0x83, 0x07, 0x45, 0x7a, 0x11, 0x84, 0x15, 0x2f, 0xde, 0xb2, 0x3a, 0x96, 0x05, 0x9b, 211 | 0xc4, 0x24, 0x5b, 0x11, 0xf1, 0xe2, 0x2f, 0xf8, 0x1b, 0xfe, 0x8d, 0xbf, 0xe0, 0x87, 0x48, 0x12, 212 | 0x57, 0xed, 0x45, 0x8f, 0x6f, 0xde, 0x9b, 0x37, 0x6f, 0x1e, 0x14, 0x0e, 0xed, 0xa2, 0xb9, 0x46, 213 | 0x6e, 0xac, 0xf6, 0x9a, 0xf4, 0x5b, 0x87, 0x96, 0x6e, 0xcf, 0xb4, 0x9e, 0xdd, 0xa1, 0x90, 0xa6, 214 | 0x11, 0x52, 0x29, 0xed, 0xa5, 0x6f, 0xb4, 0x72, 0x49, 0x43, 0xb7, 0xbe, 0xd8, 0x88, 0xea, 0xf6, 215 | 0x56, 0xe0, 0xdc, 0xf8, 0xc7, 0x44, 0xb2, 0x23, 0xe8, 0x5f, 0x3a, 0xb4, 0x84, 0xc2, 0x30, 0x58, 216 | 0x29, 0x39, 0xc7, 0x32, 0x1b, 0x65, 0xe3, 0xd5, 0xea, 0x1b, 0x07, 0xce, 0x48, 0xe7, 0x1e, 0xb4, 217 | 0xbd, 0x29, 0x7b, 0x89, 0xeb, 0x30, 0x13, 0x50, 0x9c, 0x58, 0x94, 0x1e, 0x2b, 0xbc, 0x6f, 0xd1, 218 | 0x79, 0xb2, 0x03, 0x31, 0x53, 0x34, 0xc9, 0x27, 0xc0, 0x03, 0xe0, 0xe1, 0x44, 0x15, 0xe7, 0x6c, 219 | 0x0c, 0x30, 0x45, 0xdf, 0xa9, 0xff, 0x38, 0xcb, 0xf6, 0x20, 0x8f, 0x4a, 0x67, 0xb4, 0x72, 0xf8, 220 | 0x9f, 0xf1, 0xe4, 0x2d, 0x83, 0x3c, 0xc0, 0x8b, 0x54, 0x10, 0x39, 0x87, 0x41, 0x4a, 0x46, 0x36, 221 | 0x92, 0x76, 0x29, 0x27, 0xdd, 0xe4, 0xa9, 0x16, 0xde, 0xd5, 0xc2, 0x4f, 0x43, 0x2d, 0xac, 0x7c, 222 | 0x79, 0xff, 0x78, 0xed, 0x11, 0x56, 0xc4, 0x36, 0x17, 0xfb, 0x22, 0xec, 0xba, 0xc3, 0x6c, 0x97, 223 | 0x9c, 0xc1, 0xca, 0x14, 0x3d, 0x59, 0x4b, 0x6e, 0x3f, 0x4f, 0xd0, 0xf5, 0x5f, 0x93, 0x14, 0x96, 224 | 0x8d, 0xa2, 0x0b, 0x25, 0xe5, 0x92, 0x8b, 0x78, 0xea, 0x9e, 0x7b, 0x3e, 0x1e, 0x5e, 0x0d, 0x02, 225 | 0x30, 0x75, 0x3d, 0x88, 0x09, 0x0e, 0x3e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x06, 0x04, 0x7d, 0xf8, 226 | 0xdb, 0x01, 0x00, 0x00, 227 | } 228 | 229 | // Reference imports to suppress errors if they are not otherwise used. 230 | var _ context.Context 231 | var _ grpc.ClientConn 232 | 233 | // This is a compile-time assertion to ensure that this generated file 234 | // is compatible with the grpc package it is being compiled against. 235 | const _ = grpc.SupportPackageIsVersion4 236 | 237 | // UserServiceClient is the client API for UserService service. 238 | // 239 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 240 | type UserServiceClient interface { 241 | // Create a new user 242 | Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*empty.Empty, error) 243 | // Get a specified user 244 | Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) 245 | } 246 | 247 | type userServiceClient struct { 248 | cc *grpc.ClientConn 249 | } 250 | 251 | func NewUserServiceClient(cc *grpc.ClientConn) UserServiceClient { 252 | return &userServiceClient{cc} 253 | } 254 | 255 | func (c *userServiceClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*empty.Empty, error) { 256 | out := new(empty.Empty) 257 | err := c.cc.Invoke(ctx, "/user.UserService/Create", in, out, opts...) 258 | if err != nil { 259 | return nil, err 260 | } 261 | return out, nil 262 | } 263 | 264 | func (c *userServiceClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) { 265 | out := new(GetResponse) 266 | err := c.cc.Invoke(ctx, "/user.UserService/Get", in, out, opts...) 267 | if err != nil { 268 | return nil, err 269 | } 270 | return out, nil 271 | } 272 | 273 | // UserServiceServer is the server API for UserService service. 274 | type UserServiceServer interface { 275 | // Create a new user 276 | Create(context.Context, *CreateRequest) (*empty.Empty, error) 277 | // Get a specified user 278 | Get(context.Context, *GetRequest) (*GetResponse, error) 279 | } 280 | 281 | // UnimplementedUserServiceServer can be embedded to have forward compatible implementations. 282 | type UnimplementedUserServiceServer struct { 283 | } 284 | 285 | func (*UnimplementedUserServiceServer) Create(ctx context.Context, req *CreateRequest) (*empty.Empty, error) { 286 | return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") 287 | } 288 | func (*UnimplementedUserServiceServer) Get(ctx context.Context, req *GetRequest) (*GetResponse, error) { 289 | return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") 290 | } 291 | 292 | func RegisterUserServiceServer(s *grpc.Server, srv UserServiceServer) { 293 | s.RegisterService(&_UserService_serviceDesc, srv) 294 | } 295 | 296 | func _UserService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 297 | in := new(CreateRequest) 298 | if err := dec(in); err != nil { 299 | return nil, err 300 | } 301 | if interceptor == nil { 302 | return srv.(UserServiceServer).Create(ctx, in) 303 | } 304 | info := &grpc.UnaryServerInfo{ 305 | Server: srv, 306 | FullMethod: "/user.UserService/Create", 307 | } 308 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 309 | return srv.(UserServiceServer).Create(ctx, req.(*CreateRequest)) 310 | } 311 | return interceptor(ctx, in, info, handler) 312 | } 313 | 314 | func _UserService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 315 | in := new(GetRequest) 316 | if err := dec(in); err != nil { 317 | return nil, err 318 | } 319 | if interceptor == nil { 320 | return srv.(UserServiceServer).Get(ctx, in) 321 | } 322 | info := &grpc.UnaryServerInfo{ 323 | Server: srv, 324 | FullMethod: "/user.UserService/Get", 325 | } 326 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 327 | return srv.(UserServiceServer).Get(ctx, req.(*GetRequest)) 328 | } 329 | return interceptor(ctx, in, info, handler) 330 | } 331 | 332 | var _UserService_serviceDesc = grpc.ServiceDesc{ 333 | ServiceName: "user.UserService", 334 | HandlerType: (*UserServiceServer)(nil), 335 | Methods: []grpc.MethodDesc{ 336 | { 337 | MethodName: "Create", 338 | Handler: _UserService_Create_Handler, 339 | }, 340 | { 341 | MethodName: "Get", 342 | Handler: _UserService_Get_Handler, 343 | }, 344 | }, 345 | Streams: []grpc.StreamDesc{}, 346 | Metadata: "service.proto", 347 | } 348 | -------------------------------------------------------------------------------- /restful-api-plus/userpb/service.pb.gw.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. 2 | // source: service.proto 3 | 4 | /* 5 | Package userpb is a reverse proxy. 6 | 7 | It translates gRPC into RESTful JSON APIs. 8 | */ 9 | package userpb 10 | 11 | import ( 12 | "context" 13 | "io" 14 | "net/http" 15 | 16 | "github.com/golang/protobuf/proto" 17 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 18 | "github.com/grpc-ecosystem/grpc-gateway/utilities" 19 | "google.golang.org/grpc" 20 | "google.golang.org/grpc/codes" 21 | "google.golang.org/grpc/grpclog" 22 | "google.golang.org/grpc/status" 23 | ) 24 | 25 | var _ codes.Code 26 | var _ io.Reader 27 | var _ status.Status 28 | var _ = runtime.String 29 | var _ = utilities.NewDoubleArray 30 | 31 | func request_UserService_Create_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { 32 | var protoReq CreateRequest 33 | var metadata runtime.ServerMetadata 34 | 35 | newReader, berr := utilities.IOReaderFactory(req.Body) 36 | if berr != nil { 37 | return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) 38 | } 39 | if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { 40 | return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) 41 | } 42 | 43 | msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) 44 | return msg, metadata, err 45 | 46 | } 47 | 48 | func request_UserService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { 49 | var protoReq GetRequest 50 | var metadata runtime.ServerMetadata 51 | 52 | var ( 53 | val string 54 | ok bool 55 | err error 56 | _ = err 57 | ) 58 | 59 | val, ok = pathParams["username"] 60 | if !ok { 61 | return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username") 62 | } 63 | 64 | protoReq.Username, err = runtime.String(val) 65 | 66 | if err != nil { 67 | return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err) 68 | } 69 | 70 | msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) 71 | return msg, metadata, err 72 | 73 | } 74 | 75 | // RegisterUserServiceHandlerFromEndpoint is same as RegisterUserServiceHandler but 76 | // automatically dials to "endpoint" and closes the connection when "ctx" gets done. 77 | func RegisterUserServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { 78 | conn, err := grpc.Dial(endpoint, opts...) 79 | if err != nil { 80 | return err 81 | } 82 | defer func() { 83 | if err != nil { 84 | if cerr := conn.Close(); cerr != nil { 85 | grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) 86 | } 87 | return 88 | } 89 | go func() { 90 | <-ctx.Done() 91 | if cerr := conn.Close(); cerr != nil { 92 | grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) 93 | } 94 | }() 95 | }() 96 | 97 | return RegisterUserServiceHandler(ctx, mux, conn) 98 | } 99 | 100 | // RegisterUserServiceHandler registers the http handlers for service UserService to "mux". 101 | // The handlers forward requests to the grpc endpoint over "conn". 102 | func RegisterUserServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { 103 | return RegisterUserServiceHandlerClient(ctx, mux, NewUserServiceClient(conn)) 104 | } 105 | 106 | // RegisterUserServiceHandlerClient registers the http handlers for service UserService 107 | // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "UserServiceClient". 108 | // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "UserServiceClient" 109 | // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in 110 | // "UserServiceClient" to call the correct interceptors. 111 | func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client UserServiceClient) error { 112 | 113 | mux.Handle("POST", pattern_UserService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { 114 | ctx, cancel := context.WithCancel(req.Context()) 115 | defer cancel() 116 | inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) 117 | rctx, err := runtime.AnnotateContext(ctx, mux, req) 118 | if err != nil { 119 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) 120 | return 121 | } 122 | resp, md, err := request_UserService_Create_0(rctx, inboundMarshaler, client, req, pathParams) 123 | ctx = runtime.NewServerMetadataContext(ctx, md) 124 | if err != nil { 125 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) 126 | return 127 | } 128 | 129 | forward_UserService_Create_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) 130 | 131 | }) 132 | 133 | mux.Handle("GET", pattern_UserService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { 134 | ctx, cancel := context.WithCancel(req.Context()) 135 | defer cancel() 136 | inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) 137 | rctx, err := runtime.AnnotateContext(ctx, mux, req) 138 | if err != nil { 139 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) 140 | return 141 | } 142 | resp, md, err := request_UserService_Get_0(rctx, inboundMarshaler, client, req, pathParams) 143 | ctx = runtime.NewServerMetadataContext(ctx, md) 144 | if err != nil { 145 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) 146 | return 147 | } 148 | 149 | forward_UserService_Get_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) 150 | 151 | }) 152 | 153 | return nil 154 | } 155 | 156 | var ( 157 | pattern_UserService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) 158 | 159 | pattern_UserService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "users", "username"}, "")) 160 | ) 161 | 162 | var ( 163 | forward_UserService_Create_0 = runtime.ForwardResponseMessage 164 | 165 | forward_UserService_Get_0 = runtime.ForwardResponseMessage 166 | ) 167 | -------------------------------------------------------------------------------- /restful-api-plus/userpb/service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package="userpb"; 4 | 5 | package user; 6 | 7 | import "google/api/annotations.proto"; 8 | import "google/protobuf/empty.proto"; 9 | import "protoc-gen-swagger/options/annotations.proto"; 10 | 11 | option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { 12 | info: { 13 | title: "User service"; 14 | version: "1.0"; 15 | contact: { 16 | name: "grpc-go-tutorial"; 17 | url: "https://github.com/wangy8961/grpc-go-tutorial"; 18 | email: "wangy8961@163.com"; 19 | }; 20 | }; 21 | schemes: HTTP; 22 | schemes: HTTPS; 23 | consumes: "application/json"; 24 | produces: "application/json"; 25 | responses: { 26 | key: "404"; 27 | value: { 28 | description: "Returned when the resource does not exist."; 29 | schema: { 30 | json_schema: { 31 | type: STRING; 32 | } 33 | } 34 | } 35 | } 36 | }; 37 | 38 | // User define a user 39 | message User { 40 | string username = 1; 41 | string password = 2; 42 | } 43 | 44 | // CreateRequest is the request for creating a user. 45 | message CreateRequest { 46 | User user = 1; 47 | } 48 | 49 | // GetRequest is the request for getting a user. 50 | message GetRequest { 51 | string username = 1; 52 | } 53 | 54 | // GetRequest is the response for getting a user. 55 | message GetResponse { 56 | User user = 1; 57 | } 58 | 59 | // UserService is the user service. 60 | service UserService { 61 | // Create a new user 62 | rpc Create(CreateRequest) returns (google.protobuf.Empty) { 63 | option (google.api.http) = { 64 | post: "/api/v1/users" 65 | body: "*" 66 | }; 67 | } 68 | // Get a specified user 69 | rpc Get(GetRequest) returns (GetResponse) { 70 | option (google.api.http) = { 71 | get: "/api/v1/users/{username}" 72 | }; 73 | } 74 | } -------------------------------------------------------------------------------- /restful-api-plus/userpb/service.swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "User service", 5 | "version": "1.0", 6 | "contact": { 7 | "name": "grpc-go-tutorial", 8 | "url": "https://github.com/wangy8961/grpc-go-tutorial", 9 | "email": "wangy8961@163.com" 10 | } 11 | }, 12 | "schemes": [ 13 | "http", 14 | "https" 15 | ], 16 | "consumes": [ 17 | "application/json" 18 | ], 19 | "produces": [ 20 | "application/json" 21 | ], 22 | "paths": { 23 | "/api/v1/users": { 24 | "post": { 25 | "summary": "Create a new user", 26 | "operationId": "Create", 27 | "responses": { 28 | "200": { 29 | "description": "A successful response.", 30 | "schema": { 31 | "properties": {} 32 | } 33 | }, 34 | "404": { 35 | "description": "Returned when the resource does not exist.", 36 | "schema": { 37 | "format": "string" 38 | } 39 | } 40 | }, 41 | "parameters": [ 42 | { 43 | "name": "body", 44 | "in": "body", 45 | "required": true, 46 | "schema": { 47 | "$ref": "#/definitions/userCreateRequest" 48 | } 49 | } 50 | ], 51 | "tags": [ 52 | "UserService" 53 | ] 54 | } 55 | }, 56 | "/api/v1/users/{username}": { 57 | "get": { 58 | "summary": "Get a specified user", 59 | "operationId": "Get", 60 | "responses": { 61 | "200": { 62 | "description": "A successful response.", 63 | "schema": { 64 | "$ref": "#/definitions/userGetResponse" 65 | } 66 | }, 67 | "404": { 68 | "description": "Returned when the resource does not exist.", 69 | "schema": { 70 | "format": "string" 71 | } 72 | } 73 | }, 74 | "parameters": [ 75 | { 76 | "name": "username", 77 | "in": "path", 78 | "required": true, 79 | "type": "string" 80 | } 81 | ], 82 | "tags": [ 83 | "UserService" 84 | ] 85 | } 86 | } 87 | }, 88 | "definitions": { 89 | "userCreateRequest": { 90 | "type": "object", 91 | "properties": { 92 | "user": { 93 | "$ref": "#/definitions/userUser" 94 | } 95 | }, 96 | "description": "CreateRequest is the request for creating a user." 97 | }, 98 | "userGetResponse": { 99 | "type": "object", 100 | "properties": { 101 | "user": { 102 | "$ref": "#/definitions/userUser" 103 | } 104 | }, 105 | "description": "GetRequest is the response for getting a user." 106 | }, 107 | "userUser": { 108 | "type": "object", 109 | "properties": { 110 | "username": { 111 | "type": "string" 112 | }, 113 | "password": { 114 | "type": "string" 115 | } 116 | }, 117 | "title": "User define a user" 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /restful-api/client/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3TCCAsWgAwIBAgIJAKoKZuLTxD+eMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD 3 | VQQGEwJDTjESMBAGA1UECAwJR3VhbmdEb25nMREwDwYDVQQHDAhTaGVuWmhlbjEd 4 | MBsGA1UECgwUTWFkbWFsbHMuY29tIENvLixMdGQxHTAbBgNVBAsMFE1hZG1hbGxz 5 | LmNvbSBSb290IENBMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDUyOTA3MjMwM1oX 6 | DTI5MDUyNjA3MjMwM1owgYQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0Rv 7 | bmcxETAPBgNVBAcMCFNoZW5aaGVuMR0wGwYDVQQKDBRNYWRtYWxscy5jb20gQ28u 8 | LEx0ZDEdMBsGA1UECwwUTWFkbWFsbHMuY29tIFJvb3QgQ0ExEDAOBgNVBAMMB1Jv 9 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsaZ3C8XzVTfir 10 | 2LZpJMMnxo2ZUSRcjrVRav3ATtcubqE94XT/nq5wrUClhvOWKeUIFaPZHY5dXSLm 11 | LAIRaCNW6t0cHcfCyd6ArVbjVCT8dbo1wwis2ATFRVRiVmuFjPSu2iLR/pm9m94Q 12 | C1uZe4ypXGW08TieivNiRTflfXNvjKzDENJriEQ6ukhwAixOLZcbYKZKi4wZXscl 13 | iaBcitcCZty0ODtH7TXQbrQNU2lkQBT/HmtABgL4ln5p+tiDUTNCJjvPeNBT4FTs 14 | KLtcnJM4NJspRwHO4YIGGHhmgJhneSPsHm5poE6pVu3DDRFHuforPv/k70cX9PGz 15 | OQlREfNRAgMBAAGjUDBOMB0GA1UdDgQWBBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAf 16 | BgNVHSMEGDAWgBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAMBgNVHRMEBTADAQH/MA0G 17 | CSqGSIb3DQEBCwUAA4IBAQCfcXII5evHWW5qcF3f2wMRCFhizOCojQ6CA2mwH9LN 18 | GsZRclTwniQ7OqH67b94ii3mF3dUK7klth0jbx6rJ9jYsGZ6dCuK6a1MWtCey8P4 19 | VEbiBA2qXwjJzeap1G9mdPz16YTRlbdLhJDgN7fxmTK+A4GOObiq8x2EX9DjjCym 20 | fI39D0Z9o/2i29KZGC25DWYvjnLrz3EjfGkct1QrQG9q93pwQNKm+SKHRoUIOExe 21 | KsmgS0jPxie/IUWSDHlkCY19tHSX2swRNjkiomt1pKOozN3eTu1qs29f2HA/tKAw 22 | 6/Hwdo0XD+ybVEHq/STsppCe4zMojmChxuDVTYo2de3C 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /restful-api/client/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a client for User service. 2 | package main 3 | 4 | import ( 5 | "time" 6 | "context" 7 | "flag" 8 | "log" 9 | 10 | "google.golang.org/grpc/credentials" 11 | 12 | pb "github.com/wangy8961/grpc-go-tutorial/restful-api/userpb" 13 | "google.golang.org/grpc" 14 | ) 15 | 16 | func createUserCall(client pb.UserServiceClient, username, password string) { 17 | log.Println("--- gRPC Create RPC Call ---") 18 | 19 | // 设置 10 秒超时时长,可参考 https://madmalls.com/blog/post/grpc-deadline/#21-contextwithtimeout 20 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 21 | defer cancel() 22 | 23 | // 调用 Create RPC 24 | req := &pb.CreateRequest{ 25 | User: &pb.User{ 26 | Username: username, 27 | Password: password, 28 | }, 29 | } 30 | resp, err := client.Create(ctx, req) 31 | if err != nil { 32 | log.Fatalf("failed to call Create RPC: %v", err) 33 | } 34 | 35 | log.Println("response:") 36 | log.Printf(" - %q\n", resp) 37 | } 38 | 39 | func getUserCall(client pb.UserServiceClient, username string) { 40 | log.Println("--- gRPC Get RPC Call ---") 41 | 42 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 43 | defer cancel() 44 | 45 | // 调用 Get RPC 46 | req := &pb.GetRequest{ 47 | Username: username, 48 | } 49 | resp, err := client.Get(ctx, req) 50 | if err != nil { 51 | log.Fatalf("failed to call Get RPC: %v", err) 52 | } 53 | 54 | log.Println("response:") 55 | log.Printf(" - %q\n", resp) 56 | } 57 | 58 | func main() { 59 | addr := flag.String("addr", "localhost:50051", "the address to connect to") 60 | certFile := flag.String("cacert", "cacert.pem", "CA root certificate") 61 | flag.Parse() 62 | 63 | creds, err := credentials.NewClientTLSFromFile(*certFile, "") 64 | if err != nil { 65 | log.Fatalf("failed to load CA root certificate: %v", err) 66 | } 67 | 68 | // Set up a connection to the server. 69 | conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(creds)) // To call service methods, we first need to create a gRPC channel to communicate with the server. We create this by passing the server address and port number to grpc.Dial() 70 | if err != nil { 71 | log.Fatalf("did not connect: %v", err) 72 | } 73 | defer conn.Close() 74 | 75 | c := pb.NewUserServiceClient(conn) // Once the gRPC channel is setup, we need a client stub to perform RPCs. We get this using the NewEchoClient method provided in the pb package we generated from our .proto. 76 | 77 | // Contact the server and print out its response. 78 | // 1. Create RPC Call 79 | createUserCall(c, "Alice", "123") 80 | createUserCall(c, "Bob", "pass") 81 | 82 | // 2. Get RPC Call 83 | getUserCall(c, "Alice") 84 | } 85 | -------------------------------------------------------------------------------- /restful-api/reverse-proxy/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3TCCAsWgAwIBAgIJAKoKZuLTxD+eMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD 3 | VQQGEwJDTjESMBAGA1UECAwJR3VhbmdEb25nMREwDwYDVQQHDAhTaGVuWmhlbjEd 4 | MBsGA1UECgwUTWFkbWFsbHMuY29tIENvLixMdGQxHTAbBgNVBAsMFE1hZG1hbGxz 5 | LmNvbSBSb290IENBMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDUyOTA3MjMwM1oX 6 | DTI5MDUyNjA3MjMwM1owgYQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0Rv 7 | bmcxETAPBgNVBAcMCFNoZW5aaGVuMR0wGwYDVQQKDBRNYWRtYWxscy5jb20gQ28u 8 | LEx0ZDEdMBsGA1UECwwUTWFkbWFsbHMuY29tIFJvb3QgQ0ExEDAOBgNVBAMMB1Jv 9 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsaZ3C8XzVTfir 10 | 2LZpJMMnxo2ZUSRcjrVRav3ATtcubqE94XT/nq5wrUClhvOWKeUIFaPZHY5dXSLm 11 | LAIRaCNW6t0cHcfCyd6ArVbjVCT8dbo1wwis2ATFRVRiVmuFjPSu2iLR/pm9m94Q 12 | C1uZe4ypXGW08TieivNiRTflfXNvjKzDENJriEQ6ukhwAixOLZcbYKZKi4wZXscl 13 | iaBcitcCZty0ODtH7TXQbrQNU2lkQBT/HmtABgL4ln5p+tiDUTNCJjvPeNBT4FTs 14 | KLtcnJM4NJspRwHO4YIGGHhmgJhneSPsHm5poE6pVu3DDRFHuforPv/k70cX9PGz 15 | OQlREfNRAgMBAAGjUDBOMB0GA1UdDgQWBBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAf 16 | BgNVHSMEGDAWgBTGkzsVyxoUd6DJ/aGhBDhZ+sk7YDAMBgNVHRMEBTADAQH/MA0G 17 | CSqGSIb3DQEBCwUAA4IBAQCfcXII5evHWW5qcF3f2wMRCFhizOCojQ6CA2mwH9LN 18 | GsZRclTwniQ7OqH67b94ii3mF3dUK7klth0jbx6rJ9jYsGZ6dCuK6a1MWtCey8P4 19 | VEbiBA2qXwjJzeap1G9mdPz16YTRlbdLhJDgN7fxmTK+A4GOObiq8x2EX9DjjCym 20 | fI39D0Z9o/2i29KZGC25DWYvjnLrz3EjfGkct1QrQG9q93pwQNKm+SKHRoUIOExe 21 | KsmgS0jPxie/IUWSDHlkCY19tHSX2swRNjkiomt1pKOozN3eTu1qs29f2HA/tKAw 22 | 6/Hwdo0XD+ybVEHq/STsppCe4zMojmChxuDVTYo2de3C 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /restful-api/reverse-proxy/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a reverse proxy gateway for User service. 2 | package main 3 | 4 | import ( 5 | "google.golang.org/grpc/credentials" 6 | "context" 7 | "flag" 8 | "log" 9 | "net/http" 10 | 11 | "github.com/golang/glog" 12 | 13 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 14 | "google.golang.org/grpc" 15 | 16 | gw "github.com/wangy8961/grpc-go-tutorial/restful-api/userpb" 17 | ) 18 | 19 | var ( 20 | // gRPC server 监听的地址 21 | endpoint = flag.String("endpoint", "localhost:50051", "endpoint of User service") 22 | certFile = flag.String("cacert", "cacert.pem", "CA root certificate") 23 | ) 24 | 25 | func run() error { 26 | ctx, cancel := context.WithCancel(context.Background()) 27 | defer cancel() 28 | 29 | mux := runtime.NewServeMux() 30 | 31 | creds, err := credentials.NewClientTLSFromFile(*certFile, "") 32 | if err != nil { 33 | log.Fatalf("failed to load CA root certificate: %v", err) 34 | } 35 | opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)} 36 | 37 | err = gw.RegisterUserServiceHandlerFromEndpoint(ctx, mux, *endpoint, opts) 38 | if err != nil { 39 | log.Fatalf("failed to automatically dials to endpoint: %v", err) 40 | } 41 | 42 | // RESTful API 反向代理所监听的地址,并设置 HTTP 服务器的路由使用 mux 43 | log.Println("start to listen tcp on *:5000") 44 | return http.ListenAndServe(":5000", mux) 45 | } 46 | 47 | func main() { 48 | flag.Parse() 49 | defer glog.Flush() 50 | 51 | if err := run(); err != nil { 52 | glog.Fatal(err) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /restful-api/server/main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a server for User service. 2 | package main 3 | 4 | import ( 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net" 10 | 11 | "google.golang.org/grpc/credentials" 12 | 13 | "github.com/golang/protobuf/ptypes/empty" 14 | "google.golang.org/grpc/codes" 15 | 16 | pb "github.com/wangy8961/grpc-go-tutorial/restful-api/userpb" 17 | "google.golang.org/grpc" 18 | ) 19 | 20 | // server is used to implement pb.UserServiceServer. 21 | type server struct { 22 | users map[string]pb.User 23 | } 24 | 25 | // NewServer creates User service 26 | func NewServer() pb.UserServiceServer { 27 | return &server{ 28 | users: make(map[string]pb.User), 29 | } 30 | } 31 | 32 | // Create a new user 33 | func (s *server) Create(ctx context.Context, req *pb.CreateRequest) (*empty.Empty, error) { 34 | log.Println("--- Creating new user... ---") 35 | log.Printf("request received: %v\n", req) 36 | 37 | user := req.GetUser() 38 | if user.Username == "" { 39 | return nil, grpc.Errorf(codes.InvalidArgument, "username cannot be empty") 40 | } 41 | if user.Password == "" { 42 | return nil, grpc.Errorf(codes.InvalidArgument, "password cannot be empty") 43 | } 44 | 45 | s.users[user.Username] = *user 46 | 47 | log.Println("--- User created! ---") 48 | return &empty.Empty{}, nil 49 | } 50 | 51 | // Get a specified user 52 | func (s *server) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) { 53 | log.Println("--- Getting user... ---") 54 | 55 | if req.Username == "" { 56 | return nil, grpc.Errorf(codes.InvalidArgument, "username cannot be empty") 57 | } 58 | 59 | u, exists := s.users[req.Username] 60 | if !exists { 61 | return nil, grpc.Errorf(codes.NotFound, "user not found") 62 | } 63 | 64 | log.Println("--- User found! ---") 65 | return &pb.GetResponse{User: &u}, nil 66 | } 67 | 68 | func main() { 69 | port := flag.Int("port", 50051, "the port to serve on") 70 | certFile := flag.String("certfile", "server.crt", "Server certificate") 71 | keyFile := flag.String("keyfile", "server.key", "Server private key") 72 | flag.Parse() 73 | 74 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests 75 | if err != nil { 76 | log.Fatalf("failed to listen: %v", err) 77 | } 78 | fmt.Printf("server listening at %v\n", lis.Addr()) 79 | 80 | creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) 81 | if err != nil { 82 | log.Fatalf("failed to load certificates: %v", err) 83 | } 84 | 85 | s := grpc.NewServer(grpc.Creds(creds)) // Create an instance of the gRPC server 86 | 87 | pb.RegisterUserServiceServer(s, NewServer()) // Register our service implementation with the gRPC server 88 | if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. 89 | log.Fatalf("failed to serve: %v", err) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /restful-api/server/server.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 (0x5) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: C=CN, ST=GuangDong, L=ShenZhen, O=Madmalls.com Co.,Ltd, OU=Madmalls.com Root CA, CN=Root CA 7 | Validity 8 | Not Before: Jun 11 07:22:30 2019 GMT 9 | Not After : Jun 10 07:22:30 2020 GMT 10 | Subject: C=CN, ST=GuangDong, O=Madmalls.com Co.,Ltd, OU=gRPC Server, CN=*.wangy.com 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | Public-Key: (2048 bit) 14 | Modulus: 15 | 00:e2:93:8c:43:64:e9:c6:02:6b:d1:98:f3:79:47: 16 | d5:99:c6:46:a8:6d:24:cc:a0:d8:3b:ec:e4:d0:83: 17 | 79:42:f5:bf:23:e1:0e:34:b3:f1:35:c6:27:6e:44: 18 | 58:48:ca:91:42:8c:6c:2f:6c:9d:09:d0:1b:73:d8: 19 | 42:f4:70:71:6c:40:f7:6c:71:63:39:db:d1:f8:f6: 20 | d4:f8:b7:18:03:7a:7d:32:0c:3f:f4:fd:0a:4c:e3: 21 | ad:71:33:8b:e6:25:53:52:d8:72:67:9f:56:1b:7e: 22 | d4:b7:27:28:0e:1d:28:92:b6:94:09:4f:bd:51:ef: 23 | 06:bc:80:39:97:cb:d2:20:8c:0c:ff:cf:33:97:7d: 24 | 7a:0e:01:e7:90:a2:30:64:34:be:3e:78:d9:3f:19: 25 | 7b:27:fc:c1:b2:0d:07:34:8b:6e:2e:0b:50:f4:8a: 26 | be:07:63:98:25:24:00:97:89:d2:ad:96:cd:56:01: 27 | 84:df:dc:af:be:a8:12:ef:f8:77:75:46:2d:49:21: 28 | ae:1c:bc:b1:87:94:4d:8e:b7:5d:7c:01:0a:c6:e6: 29 | 32:32:47:c3:d1:38:ab:5d:02:4f:72:c5:10:9c:a2: 30 | 83:9d:ee:a1:37:ee:02:bb:50:8a:78:ba:bf:f7:f7: 31 | 69:b0:ff:17:31:14:68:a5:db:05:ef:ab:2e:53:ac: 32 | ae:df 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Subject Alternative Name: 36 | DNS:www.wangy.com, DNS:blog.wangy.com, IP Address:192.168.40.123 37 | Signature Algorithm: sha256WithRSAEncryption 38 | 42:83:8d:68:cf:9b:93:59:63:94:5d:62:a8:14:bb:79:f4:09: 39 | cf:6d:ea:e5:86:bd:9a:fc:28:e2:9b:c5:fa:df:e9:50:b0:ac: 40 | 28:4c:7a:ff:38:67:1a:9b:e6:aa:8d:bd:da:3d:6f:e8:49:fa: 41 | f2:7e:fa:dd:4b:a8:4d:06:ee:1c:47:ff:36:99:23:24:9b:4c: 42 | 19:d1:f4:1c:85:c7:94:84:54:19:b0:78:6f:d7:54:25:a8:da: 43 | b1:18:76:1b:71:15:ad:35:d5:64:b6:6f:ee:80:6d:82:98:71: 44 | b8:db:bb:e5:01:da:f7:f1:aa:97:a7:ae:f8:90:b6:f6:ac:38: 45 | fa:9e:a2:15:10:2f:ca:c5:eb:bf:91:67:bc:f7:ee:2a:0e:80: 46 | 35:ad:6d:03:20:98:4f:df:39:5b:ac:f2:ef:43:01:95:b9:0a: 47 | 7b:29:b4:6d:83:de:1f:94:a6:69:6b:42:18:ef:cb:e6:68:66: 48 | b2:79:59:f8:ae:9f:89:25:ec:ee:46:f9:3c:34:ab:74:7d:72: 49 | 0a:3f:39:6e:07:28:79:e5:8c:8b:3a:da:1f:b9:7c:af:0c:0a: 50 | 0c:71:3d:aa:76:87:69:4c:75:9a:0b:8f:8e:10:f8:d6:53:c9: 51 | 35:8b:10:42:0a:32:01:a5:83:e3:22:0e:86:c0:1d:87:75:af: 52 | cb:30:cb:1a 53 | -----BEGIN CERTIFICATE----- 54 | MIIDnjCCAoagAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMCQ04x 55 | EjAQBgNVBAgMCUd1YW5nRG9uZzERMA8GA1UEBwwIU2hlblpoZW4xHTAbBgNVBAoM 56 | FE1hZG1hbGxzLmNvbSBDby4sTHRkMR0wGwYDVQQLDBRNYWRtYWxscy5jb20gUm9v 57 | dCBDQTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xOTA2MTEwNzIyMzBaFw0yMDA2MTAw 58 | NzIyMzBaMGwxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxHTAbBgNV 59 | BAoMFE1hZG1hbGxzLmNvbSBDby4sTHRkMRQwEgYDVQQLDAtnUlBDIFNlcnZlcjEU 60 | MBIGA1UEAwwLKi53YW5neS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 61 | AoIBAQDik4xDZOnGAmvRmPN5R9WZxkaobSTMoNg77OTQg3lC9b8j4Q40s/E1xidu 62 | RFhIypFCjGwvbJ0J0Btz2EL0cHFsQPdscWM529H49tT4txgDen0yDD/0/QpM461x 63 | M4vmJVNS2HJnn1YbftS3JygOHSiStpQJT71R7wa8gDmXy9IgjAz/zzOXfXoOAeeQ 64 | ojBkNL4+eNk/GXsn/MGyDQc0i24uC1D0ir4HY5glJACXidKtls1WAYTf3K++qBLv 65 | +Hd1Ri1JIa4cvLGHlE2Ot118AQrG5jIyR8PROKtdAk9yxRCcooOd7qE37gK7UIp4 66 | ur/392mw/xcxFGil2wXvqy5TrK7fAgMBAAGjMjAwMC4GA1UdEQQnMCWCDXd3dy53 67 | YW5neS5jb22CDmJsb2cud2FuZ3kuY29thwTAqCh7MA0GCSqGSIb3DQEBCwUAA4IB 68 | AQBCg41oz5uTWWOUXWKoFLt59AnPberlhr2a/Cjim8X63+lQsKwoTHr/OGcam+aq 69 | jb3aPW/oSfryfvrdS6hNBu4cR/82mSMkm0wZ0fQchceUhFQZsHhv11QlqNqxGHYb 70 | cRWtNdVktm/ugG2CmHG427vlAdr38aqXp674kLb2rDj6nqIVEC/Kxeu/kWe89+4q 71 | DoA1rW0DIJhP3zlbrPLvQwGVuQp7KbRtg94flKZpa0IY78vmaGayeVn4rp+JJezu 72 | Rvk8NKt0fXIKPzluByh55YyLOtofuXyvDAoMcT2qdodpTHWaC4+OEPjWU8k1ixBC 73 | CjIBpYPjIg6GwB2Hda/LMMsa 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /restful-api/server/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpgIBAAKCAQEA4pOMQ2TpxgJr0ZjzeUfVmcZGqG0kzKDYO+zk0IN5QvW/I+EO 3 | NLPxNcYnbkRYSMqRQoxsL2ydCdAbc9hC9HBxbED3bHFjOdvR+PbU+LcYA3p9Mgw/ 4 | 9P0KTOOtcTOL5iVTUthyZ59WG37UtycoDh0okraUCU+9Ue8GvIA5l8vSIIwM/88z 5 | l316DgHnkKIwZDS+PnjZPxl7J/zBsg0HNItuLgtQ9Iq+B2OYJSQAl4nSrZbNVgGE 6 | 39yvvqgS7/h3dUYtSSGuHLyxh5RNjrddfAEKxuYyMkfD0TirXQJPcsUQnKKDne6h 7 | N+4Cu1CKeLq/9/dpsP8XMRRopdsF76suU6yu3wIDAQABAoIBAQCjrPDjco/KAc+/ 8 | ft1LnI/6YRiD7SxrQjpSt+PnmUJNE9e7ZIXtnpu+O+IaLvcTxnm++E/ixnR/NT3P 9 | psdfa6cUC65xQUvr7Rc24aCh9yo6wQ6Vy/Gb2fvJ5aNSpmkGnaoeq8uhfaInhKzH 10 | jlrKL1gy+//e5iKegKx+GacBODUYWpc8bVoROpvqgtSDg1wtuwUU/5fy6f8IIHuT 11 | 78cPm6t39ylcFkV3lt1yS4LhvUoW2Nlm5SHOicDc/cYVE8xqfJlARzN9xUfM+5LB 12 | sUdbR5UZOxOPsd24q1D1DZc1s34soj/wKXPVyyuhp0o7Db6vSsxQDfOetfuRUSR6 13 | +5zPMLqxAoGBAP6laEBin8SVThAvQmS4PoyqPK8Hb0GgjxPGxyXyrtSL3/Xy86E0 14 | vhOGTm8gh2oA0wZQ3KIg5TIhVa3G+UNMbZmIuYCQdeAQpl/sdMnpw66aNtHj6kt7 15 | gqzWzH1en4w2T3DxU7LtCQIx4q3Akv0oyZ/YovlNb5dkSHtuz4ZVRYkXAoGBAOPH 16 | 74KHzkD/IWkGf7/QG9XQ93hjggxmDS/WzeKwKYewPyZoWRvkvj7i7d+P1AUrCvgd 17 | YeB132S0S1JrSUP4Fn+DNwgj3Kyg2Fq2/HmwTKkqVPjOKlonTKlA+h3fNG/NbJDE 18 | E41yFkK+zjSbxxcAYLCgR2A9PCEyXPXyV20U8BV5AoGBAJnq5unL8yBC0u2Lc0kn 19 | 6H7jw0xUZRY482KTyvoQB0bnyRaDpGkzVRS+IJihA9i56NOverzwvzie14fzdeUM 20 | xE6CSwX/y5AE4FuotCr7hlD6W9pgNdUsMZ9BMlcxI6T/iuMMq3fCOKi/+HDnrrEg 21 | v0ZEDrY77RCICBu7repXjnE/AoGBAIdBvhOAmRU3apt25Hz+EslQoOK4FA1QvBvg 22 | LbmiacbM/XLNG7zYg6/MCPxr57Z57LWQnQIwfErMVL3IP2VA9/sX66HFydAoYtDb 23 | P+jyq1L4dCSaJ8QI+hi3IM6EMBsDnKgKBqJDULypmMDcj8g0zTWUt02Kjx4XTeQt 24 | 14RKnpXhAoGBAJkImhczkTucb/Yhaw1du2IkylPSb+HAJH6NFH9LFNvKYOIFfUaA 25 | id8ZkR5WGDJiK7vm3QT4n6j14/CnTXqrQ52W9ixxOIjbd1AHgHNwlbQGJodInT+l 26 | j3Iqa44peXerIk0MO8qYEjEGybHu7DhZhkbZoCn1Qqn+XpDbx2bbPe7w 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /restful-api/userpb/protoc-gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 1. Generate gRPC Golang stub 4 | protoc -I/usr/local/include -I. \ 5 | -I$GOPATH/src \ 6 | -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 7 | --go_out=plugins=grpc:. \ 8 | service.proto 9 | 10 | # 2. Generate a reverse-proxy server which translates a RESTful JSON API into gRPC 11 | protoc -I/usr/local/include -I. \ 12 | -I$GOPATH/src \ 13 | -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 14 | --grpc-gateway_out=logtostderr=true:. \ 15 | service.proto 16 | 17 | # 3. (Optional) Generate swagger definitions 18 | protoc -I/usr/local/include -I. \ 19 | -I$GOPATH/src \ 20 | -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 21 | -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway \ 22 | --swagger_out=logtostderr=true:. \ 23 | service.proto -------------------------------------------------------------------------------- /restful-api/userpb/service.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: service.proto 3 | 4 | package userpb 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | proto "github.com/golang/protobuf/proto" 10 | empty "github.com/golang/protobuf/ptypes/empty" 11 | _ "google.golang.org/genproto/googleapis/api/annotations" 12 | grpc "google.golang.org/grpc" 13 | codes "google.golang.org/grpc/codes" 14 | status "google.golang.org/grpc/status" 15 | math "math" 16 | ) 17 | 18 | // Reference imports to suppress errors if they are not otherwise used. 19 | var _ = proto.Marshal 20 | var _ = fmt.Errorf 21 | var _ = math.Inf 22 | 23 | // This is a compile-time assertion to ensure that this generated file 24 | // is compatible with the proto package it is being compiled against. 25 | // A compilation error at this line likely means your copy of the 26 | // proto package needs to be updated. 27 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 28 | 29 | // User define a user 30 | type User struct { 31 | Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` 32 | Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` 33 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 34 | XXX_unrecognized []byte `json:"-"` 35 | XXX_sizecache int32 `json:"-"` 36 | } 37 | 38 | func (m *User) Reset() { *m = User{} } 39 | func (m *User) String() string { return proto.CompactTextString(m) } 40 | func (*User) ProtoMessage() {} 41 | func (*User) Descriptor() ([]byte, []int) { 42 | return fileDescriptor_a0b84a42fa06f626, []int{0} 43 | } 44 | 45 | func (m *User) XXX_Unmarshal(b []byte) error { 46 | return xxx_messageInfo_User.Unmarshal(m, b) 47 | } 48 | func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 49 | return xxx_messageInfo_User.Marshal(b, m, deterministic) 50 | } 51 | func (m *User) XXX_Merge(src proto.Message) { 52 | xxx_messageInfo_User.Merge(m, src) 53 | } 54 | func (m *User) XXX_Size() int { 55 | return xxx_messageInfo_User.Size(m) 56 | } 57 | func (m *User) XXX_DiscardUnknown() { 58 | xxx_messageInfo_User.DiscardUnknown(m) 59 | } 60 | 61 | var xxx_messageInfo_User proto.InternalMessageInfo 62 | 63 | func (m *User) GetUsername() string { 64 | if m != nil { 65 | return m.Username 66 | } 67 | return "" 68 | } 69 | 70 | func (m *User) GetPassword() string { 71 | if m != nil { 72 | return m.Password 73 | } 74 | return "" 75 | } 76 | 77 | // CreateRequest is the request for creating a user. 78 | type CreateRequest struct { 79 | User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` 80 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 81 | XXX_unrecognized []byte `json:"-"` 82 | XXX_sizecache int32 `json:"-"` 83 | } 84 | 85 | func (m *CreateRequest) Reset() { *m = CreateRequest{} } 86 | func (m *CreateRequest) String() string { return proto.CompactTextString(m) } 87 | func (*CreateRequest) ProtoMessage() {} 88 | func (*CreateRequest) Descriptor() ([]byte, []int) { 89 | return fileDescriptor_a0b84a42fa06f626, []int{1} 90 | } 91 | 92 | func (m *CreateRequest) XXX_Unmarshal(b []byte) error { 93 | return xxx_messageInfo_CreateRequest.Unmarshal(m, b) 94 | } 95 | func (m *CreateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 96 | return xxx_messageInfo_CreateRequest.Marshal(b, m, deterministic) 97 | } 98 | func (m *CreateRequest) XXX_Merge(src proto.Message) { 99 | xxx_messageInfo_CreateRequest.Merge(m, src) 100 | } 101 | func (m *CreateRequest) XXX_Size() int { 102 | return xxx_messageInfo_CreateRequest.Size(m) 103 | } 104 | func (m *CreateRequest) XXX_DiscardUnknown() { 105 | xxx_messageInfo_CreateRequest.DiscardUnknown(m) 106 | } 107 | 108 | var xxx_messageInfo_CreateRequest proto.InternalMessageInfo 109 | 110 | func (m *CreateRequest) GetUser() *User { 111 | if m != nil { 112 | return m.User 113 | } 114 | return nil 115 | } 116 | 117 | // GetRequest is the request for getting a user. 118 | type GetRequest struct { 119 | Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` 120 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 121 | XXX_unrecognized []byte `json:"-"` 122 | XXX_sizecache int32 `json:"-"` 123 | } 124 | 125 | func (m *GetRequest) Reset() { *m = GetRequest{} } 126 | func (m *GetRequest) String() string { return proto.CompactTextString(m) } 127 | func (*GetRequest) ProtoMessage() {} 128 | func (*GetRequest) Descriptor() ([]byte, []int) { 129 | return fileDescriptor_a0b84a42fa06f626, []int{2} 130 | } 131 | 132 | func (m *GetRequest) XXX_Unmarshal(b []byte) error { 133 | return xxx_messageInfo_GetRequest.Unmarshal(m, b) 134 | } 135 | func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 136 | return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic) 137 | } 138 | func (m *GetRequest) XXX_Merge(src proto.Message) { 139 | xxx_messageInfo_GetRequest.Merge(m, src) 140 | } 141 | func (m *GetRequest) XXX_Size() int { 142 | return xxx_messageInfo_GetRequest.Size(m) 143 | } 144 | func (m *GetRequest) XXX_DiscardUnknown() { 145 | xxx_messageInfo_GetRequest.DiscardUnknown(m) 146 | } 147 | 148 | var xxx_messageInfo_GetRequest proto.InternalMessageInfo 149 | 150 | func (m *GetRequest) GetUsername() string { 151 | if m != nil { 152 | return m.Username 153 | } 154 | return "" 155 | } 156 | 157 | // GetRequest is the response for getting a user. 158 | type GetResponse struct { 159 | User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` 160 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 161 | XXX_unrecognized []byte `json:"-"` 162 | XXX_sizecache int32 `json:"-"` 163 | } 164 | 165 | func (m *GetResponse) Reset() { *m = GetResponse{} } 166 | func (m *GetResponse) String() string { return proto.CompactTextString(m) } 167 | func (*GetResponse) ProtoMessage() {} 168 | func (*GetResponse) Descriptor() ([]byte, []int) { 169 | return fileDescriptor_a0b84a42fa06f626, []int{3} 170 | } 171 | 172 | func (m *GetResponse) XXX_Unmarshal(b []byte) error { 173 | return xxx_messageInfo_GetResponse.Unmarshal(m, b) 174 | } 175 | func (m *GetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 176 | return xxx_messageInfo_GetResponse.Marshal(b, m, deterministic) 177 | } 178 | func (m *GetResponse) XXX_Merge(src proto.Message) { 179 | xxx_messageInfo_GetResponse.Merge(m, src) 180 | } 181 | func (m *GetResponse) XXX_Size() int { 182 | return xxx_messageInfo_GetResponse.Size(m) 183 | } 184 | func (m *GetResponse) XXX_DiscardUnknown() { 185 | xxx_messageInfo_GetResponse.DiscardUnknown(m) 186 | } 187 | 188 | var xxx_messageInfo_GetResponse proto.InternalMessageInfo 189 | 190 | func (m *GetResponse) GetUser() *User { 191 | if m != nil { 192 | return m.User 193 | } 194 | return nil 195 | } 196 | 197 | func init() { 198 | proto.RegisterType((*User)(nil), "user.User") 199 | proto.RegisterType((*CreateRequest)(nil), "user.CreateRequest") 200 | proto.RegisterType((*GetRequest)(nil), "user.GetRequest") 201 | proto.RegisterType((*GetResponse)(nil), "user.GetResponse") 202 | } 203 | 204 | func init() { proto.RegisterFile("service.proto", fileDescriptor_a0b84a42fa06f626) } 205 | 206 | var fileDescriptor_a0b84a42fa06f626 = []byte{ 207 | // 292 bytes of a gzipped FileDescriptorProto 208 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x50, 0xc1, 0x4a, 0x03, 0x31, 209 | 0x10, 0x65, 0x6b, 0x29, 0x75, 0x96, 0x05, 0x8d, 0x20, 0x4b, 0x14, 0x29, 0x39, 0x15, 0xc1, 0x04, 210 | 0xeb, 0xcd, 0x83, 0x07, 0x45, 0x7a, 0x11, 0x84, 0x15, 0x2f, 0xde, 0xb2, 0x3a, 0x96, 0x05, 0x9b, 211 | 0xc4, 0x24, 0x5b, 0x11, 0xf1, 0xe2, 0x2f, 0xf8, 0x1b, 0xfe, 0x8d, 0xbf, 0xe0, 0x87, 0x48, 0x12, 212 | 0x57, 0xed, 0x45, 0x8f, 0x6f, 0xde, 0x9b, 0x37, 0x6f, 0x1e, 0x14, 0x0e, 0xed, 0xa2, 0xb9, 0x46, 213 | 0x6e, 0xac, 0xf6, 0x9a, 0xf4, 0x5b, 0x87, 0x96, 0x6e, 0xcf, 0xb4, 0x9e, 0xdd, 0xa1, 0x90, 0xa6, 214 | 0x11, 0x52, 0x29, 0xed, 0xa5, 0x6f, 0xb4, 0x72, 0x49, 0x43, 0xb7, 0xbe, 0xd8, 0x88, 0xea, 0xf6, 215 | 0x56, 0xe0, 0xdc, 0xf8, 0xc7, 0x44, 0xb2, 0x23, 0xe8, 0x5f, 0x3a, 0xb4, 0x84, 0xc2, 0x30, 0x58, 216 | 0x29, 0x39, 0xc7, 0x32, 0x1b, 0x65, 0xe3, 0xd5, 0xea, 0x1b, 0x07, 0xce, 0x48, 0xe7, 0x1e, 0xb4, 217 | 0xbd, 0x29, 0x7b, 0x89, 0xeb, 0x30, 0x13, 0x50, 0x9c, 0x58, 0x94, 0x1e, 0x2b, 0xbc, 0x6f, 0xd1, 218 | 0x79, 0xb2, 0x03, 0x31, 0x53, 0x34, 0xc9, 0x27, 0xc0, 0x03, 0xe0, 0xe1, 0x44, 0x15, 0xe7, 0x6c, 219 | 0x0c, 0x30, 0x45, 0xdf, 0xa9, 0xff, 0x38, 0xcb, 0xf6, 0x20, 0x8f, 0x4a, 0x67, 0xb4, 0x72, 0xf8, 220 | 0x9f, 0xf1, 0xe4, 0x2d, 0x83, 0x3c, 0xc0, 0x8b, 0x54, 0x10, 0x39, 0x87, 0x41, 0x4a, 0x46, 0x36, 221 | 0x92, 0x76, 0x29, 0x27, 0xdd, 0xe4, 0xa9, 0x16, 0xde, 0xd5, 0xc2, 0x4f, 0x43, 0x2d, 0xac, 0x7c, 222 | 0x79, 0xff, 0x78, 0xed, 0x11, 0x56, 0xc4, 0x36, 0x17, 0xfb, 0x22, 0xec, 0xba, 0xc3, 0x6c, 0x97, 223 | 0x9c, 0xc1, 0xca, 0x14, 0x3d, 0x59, 0x4b, 0x6e, 0x3f, 0x4f, 0xd0, 0xf5, 0x5f, 0x93, 0x14, 0x96, 224 | 0x8d, 0xa2, 0x0b, 0x25, 0xe5, 0x92, 0x8b, 0x78, 0xea, 0x9e, 0x7b, 0x3e, 0x1e, 0x5e, 0x0d, 0x02, 225 | 0x30, 0x75, 0x3d, 0x88, 0x09, 0x0e, 0x3e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x06, 0x04, 0x7d, 0xf8, 226 | 0xdb, 0x01, 0x00, 0x00, 227 | } 228 | 229 | // Reference imports to suppress errors if they are not otherwise used. 230 | var _ context.Context 231 | var _ grpc.ClientConn 232 | 233 | // This is a compile-time assertion to ensure that this generated file 234 | // is compatible with the grpc package it is being compiled against. 235 | const _ = grpc.SupportPackageIsVersion4 236 | 237 | // UserServiceClient is the client API for UserService service. 238 | // 239 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 240 | type UserServiceClient interface { 241 | // Create a new user 242 | Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*empty.Empty, error) 243 | // Get a specified user 244 | Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) 245 | } 246 | 247 | type userServiceClient struct { 248 | cc *grpc.ClientConn 249 | } 250 | 251 | func NewUserServiceClient(cc *grpc.ClientConn) UserServiceClient { 252 | return &userServiceClient{cc} 253 | } 254 | 255 | func (c *userServiceClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*empty.Empty, error) { 256 | out := new(empty.Empty) 257 | err := c.cc.Invoke(ctx, "/user.UserService/Create", in, out, opts...) 258 | if err != nil { 259 | return nil, err 260 | } 261 | return out, nil 262 | } 263 | 264 | func (c *userServiceClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) { 265 | out := new(GetResponse) 266 | err := c.cc.Invoke(ctx, "/user.UserService/Get", in, out, opts...) 267 | if err != nil { 268 | return nil, err 269 | } 270 | return out, nil 271 | } 272 | 273 | // UserServiceServer is the server API for UserService service. 274 | type UserServiceServer interface { 275 | // Create a new user 276 | Create(context.Context, *CreateRequest) (*empty.Empty, error) 277 | // Get a specified user 278 | Get(context.Context, *GetRequest) (*GetResponse, error) 279 | } 280 | 281 | // UnimplementedUserServiceServer can be embedded to have forward compatible implementations. 282 | type UnimplementedUserServiceServer struct { 283 | } 284 | 285 | func (*UnimplementedUserServiceServer) Create(ctx context.Context, req *CreateRequest) (*empty.Empty, error) { 286 | return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") 287 | } 288 | func (*UnimplementedUserServiceServer) Get(ctx context.Context, req *GetRequest) (*GetResponse, error) { 289 | return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") 290 | } 291 | 292 | func RegisterUserServiceServer(s *grpc.Server, srv UserServiceServer) { 293 | s.RegisterService(&_UserService_serviceDesc, srv) 294 | } 295 | 296 | func _UserService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 297 | in := new(CreateRequest) 298 | if err := dec(in); err != nil { 299 | return nil, err 300 | } 301 | if interceptor == nil { 302 | return srv.(UserServiceServer).Create(ctx, in) 303 | } 304 | info := &grpc.UnaryServerInfo{ 305 | Server: srv, 306 | FullMethod: "/user.UserService/Create", 307 | } 308 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 309 | return srv.(UserServiceServer).Create(ctx, req.(*CreateRequest)) 310 | } 311 | return interceptor(ctx, in, info, handler) 312 | } 313 | 314 | func _UserService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 315 | in := new(GetRequest) 316 | if err := dec(in); err != nil { 317 | return nil, err 318 | } 319 | if interceptor == nil { 320 | return srv.(UserServiceServer).Get(ctx, in) 321 | } 322 | info := &grpc.UnaryServerInfo{ 323 | Server: srv, 324 | FullMethod: "/user.UserService/Get", 325 | } 326 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 327 | return srv.(UserServiceServer).Get(ctx, req.(*GetRequest)) 328 | } 329 | return interceptor(ctx, in, info, handler) 330 | } 331 | 332 | var _UserService_serviceDesc = grpc.ServiceDesc{ 333 | ServiceName: "user.UserService", 334 | HandlerType: (*UserServiceServer)(nil), 335 | Methods: []grpc.MethodDesc{ 336 | { 337 | MethodName: "Create", 338 | Handler: _UserService_Create_Handler, 339 | }, 340 | { 341 | MethodName: "Get", 342 | Handler: _UserService_Get_Handler, 343 | }, 344 | }, 345 | Streams: []grpc.StreamDesc{}, 346 | Metadata: "service.proto", 347 | } 348 | -------------------------------------------------------------------------------- /restful-api/userpb/service.pb.gw.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. 2 | // source: service.proto 3 | 4 | /* 5 | Package userpb is a reverse proxy. 6 | 7 | It translates gRPC into RESTful JSON APIs. 8 | */ 9 | package userpb 10 | 11 | import ( 12 | "context" 13 | "io" 14 | "net/http" 15 | 16 | "github.com/golang/protobuf/proto" 17 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 18 | "github.com/grpc-ecosystem/grpc-gateway/utilities" 19 | "google.golang.org/grpc" 20 | "google.golang.org/grpc/codes" 21 | "google.golang.org/grpc/grpclog" 22 | "google.golang.org/grpc/status" 23 | ) 24 | 25 | var _ codes.Code 26 | var _ io.Reader 27 | var _ status.Status 28 | var _ = runtime.String 29 | var _ = utilities.NewDoubleArray 30 | 31 | func request_UserService_Create_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { 32 | var protoReq CreateRequest 33 | var metadata runtime.ServerMetadata 34 | 35 | newReader, berr := utilities.IOReaderFactory(req.Body) 36 | if berr != nil { 37 | return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) 38 | } 39 | if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { 40 | return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) 41 | } 42 | 43 | msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) 44 | return msg, metadata, err 45 | 46 | } 47 | 48 | func request_UserService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { 49 | var protoReq GetRequest 50 | var metadata runtime.ServerMetadata 51 | 52 | var ( 53 | val string 54 | ok bool 55 | err error 56 | _ = err 57 | ) 58 | 59 | val, ok = pathParams["username"] 60 | if !ok { 61 | return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username") 62 | } 63 | 64 | protoReq.Username, err = runtime.String(val) 65 | 66 | if err != nil { 67 | return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err) 68 | } 69 | 70 | msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) 71 | return msg, metadata, err 72 | 73 | } 74 | 75 | // RegisterUserServiceHandlerFromEndpoint is same as RegisterUserServiceHandler but 76 | // automatically dials to "endpoint" and closes the connection when "ctx" gets done. 77 | func RegisterUserServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { 78 | conn, err := grpc.Dial(endpoint, opts...) 79 | if err != nil { 80 | return err 81 | } 82 | defer func() { 83 | if err != nil { 84 | if cerr := conn.Close(); cerr != nil { 85 | grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) 86 | } 87 | return 88 | } 89 | go func() { 90 | <-ctx.Done() 91 | if cerr := conn.Close(); cerr != nil { 92 | grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) 93 | } 94 | }() 95 | }() 96 | 97 | return RegisterUserServiceHandler(ctx, mux, conn) 98 | } 99 | 100 | // RegisterUserServiceHandler registers the http handlers for service UserService to "mux". 101 | // The handlers forward requests to the grpc endpoint over "conn". 102 | func RegisterUserServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { 103 | return RegisterUserServiceHandlerClient(ctx, mux, NewUserServiceClient(conn)) 104 | } 105 | 106 | // RegisterUserServiceHandlerClient registers the http handlers for service UserService 107 | // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "UserServiceClient". 108 | // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "UserServiceClient" 109 | // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in 110 | // "UserServiceClient" to call the correct interceptors. 111 | func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client UserServiceClient) error { 112 | 113 | mux.Handle("POST", pattern_UserService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { 114 | ctx, cancel := context.WithCancel(req.Context()) 115 | defer cancel() 116 | inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) 117 | rctx, err := runtime.AnnotateContext(ctx, mux, req) 118 | if err != nil { 119 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) 120 | return 121 | } 122 | resp, md, err := request_UserService_Create_0(rctx, inboundMarshaler, client, req, pathParams) 123 | ctx = runtime.NewServerMetadataContext(ctx, md) 124 | if err != nil { 125 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) 126 | return 127 | } 128 | 129 | forward_UserService_Create_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) 130 | 131 | }) 132 | 133 | mux.Handle("GET", pattern_UserService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { 134 | ctx, cancel := context.WithCancel(req.Context()) 135 | defer cancel() 136 | inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) 137 | rctx, err := runtime.AnnotateContext(ctx, mux, req) 138 | if err != nil { 139 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) 140 | return 141 | } 142 | resp, md, err := request_UserService_Get_0(rctx, inboundMarshaler, client, req, pathParams) 143 | ctx = runtime.NewServerMetadataContext(ctx, md) 144 | if err != nil { 145 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) 146 | return 147 | } 148 | 149 | forward_UserService_Get_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) 150 | 151 | }) 152 | 153 | return nil 154 | } 155 | 156 | var ( 157 | pattern_UserService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) 158 | 159 | pattern_UserService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "users", "username"}, "")) 160 | ) 161 | 162 | var ( 163 | forward_UserService_Create_0 = runtime.ForwardResponseMessage 164 | 165 | forward_UserService_Get_0 = runtime.ForwardResponseMessage 166 | ) 167 | -------------------------------------------------------------------------------- /restful-api/userpb/service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package="userpb"; 4 | 5 | package user; 6 | 7 | import "google/api/annotations.proto"; 8 | import "google/protobuf/empty.proto"; 9 | import "protoc-gen-swagger/options/annotations.proto"; 10 | 11 | option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { 12 | info: { 13 | title: "User service"; 14 | version: "1.0"; 15 | contact: { 16 | name: "grpc-go-tutorial"; 17 | url: "https://github.com/wangy8961/grpc-go-tutorial"; 18 | email: "wangy8961@163.com"; 19 | }; 20 | }; 21 | schemes: HTTPS; 22 | schemes: HTTP; 23 | consumes: "application/json"; 24 | produces: "application/json"; 25 | responses: { 26 | key: "404"; 27 | value: { 28 | description: "Returned when the resource does not exist."; 29 | schema: { 30 | json_schema: { 31 | type: STRING; 32 | } 33 | } 34 | } 35 | } 36 | }; 37 | 38 | // User define a user 39 | message User { 40 | string username = 1; 41 | string password = 2; 42 | } 43 | 44 | // CreateRequest is the request for creating a user. 45 | message CreateRequest { 46 | User user = 1; 47 | } 48 | 49 | // GetRequest is the request for getting a user. 50 | message GetRequest { 51 | string username = 1; 52 | } 53 | 54 | // GetRequest is the response for getting a user. 55 | message GetResponse { 56 | User user = 1; 57 | } 58 | 59 | // UserService is the user service. 60 | service UserService { 61 | // Create a new user 62 | rpc Create(CreateRequest) returns (google.protobuf.Empty) { 63 | option (google.api.http) = { 64 | post: "/api/v1/users" 65 | body: "*" 66 | }; 67 | } 68 | // Get a specified user 69 | rpc Get(GetRequest) returns (GetResponse) { 70 | option (google.api.http) = { 71 | get: "/api/v1/users/{username}" 72 | }; 73 | } 74 | } --------------------------------------------------------------------------------