OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package prpc |
| 6 |
| 7 import ( |
| 8 "fmt" |
| 9 "io/ioutil" |
| 10 "net/http" |
| 11 "net/http/httptest" |
| 12 "strconv" |
| 13 "strings" |
| 14 "testing" |
| 15 "time" |
| 16 |
| 17 "github.com/golang/protobuf/proto" |
| 18 "golang.org/x/net/context" |
| 19 "google.golang.org/grpc" |
| 20 "google.golang.org/grpc/codes" |
| 21 |
| 22 "github.com/luci/luci-go/common/logging" |
| 23 "github.com/luci/luci-go/common/logging/memlogger" |
| 24 "github.com/luci/luci-go/common/retry" |
| 25 |
| 26 . "github.com/smartystreets/goconvey/convey" |
| 27 ) |
| 28 |
| 29 func sayHello(c C) http.HandlerFunc { |
| 30 return func(w http.ResponseWriter, r *http.Request) { |
| 31 c.So(r.Method, ShouldEqual, "POST") |
| 32 c.So(r.URL.Path, ShouldEqual, "/prpc/prpc.Greeter/SayHello") |
| 33 c.So(r.Header.Get("Accept"), ShouldEqual, "application/prpc") |
| 34 c.So(r.Header.Get("Content-Type"), ShouldEqual, "application/prp
c") |
| 35 c.So(r.Header.Get("User-Agent"), ShouldEqual, "prpc-test") |
| 36 |
| 37 reqBody, err := ioutil.ReadAll(r.Body) |
| 38 c.So(err, ShouldBeNil) |
| 39 |
| 40 var req HelloRequest |
| 41 err = proto.Unmarshal(reqBody, &req) |
| 42 c.So(err, ShouldBeNil) |
| 43 |
| 44 res := HelloReply{"Hello " + req.Name} |
| 45 buf, err := proto.Marshal(&res) |
| 46 c.So(err, ShouldBeNil) |
| 47 |
| 48 w.Header().Set(HeaderGRPCCode, strconv.Itoa(int(codes.OK))) |
| 49 _, err = w.Write(buf) |
| 50 c.So(err, ShouldBeNil) |
| 51 } |
| 52 } |
| 53 |
| 54 func transientErrors(count int, then http.Handler) http.HandlerFunc { |
| 55 return func(w http.ResponseWriter, r *http.Request) { |
| 56 if count > 0 { |
| 57 count-- |
| 58 w.Header().Set(HeaderGRPCCode, strconv.Itoa(int(codes.In
ternal))) |
| 59 w.WriteHeader(http.StatusInternalServerError) |
| 60 fmt.Fprintln(w, "Server misbehaved") |
| 61 return |
| 62 } |
| 63 then.ServeHTTP(w, r) |
| 64 } |
| 65 } |
| 66 |
| 67 func shouldHaveMessagesLike(actual interface{}, expected ...interface{}) string
{ |
| 68 log := actual.(*memlogger.MemLogger) |
| 69 msgs := log.Messages() |
| 70 |
| 71 So(msgs, ShouldHaveLength, len(expected)) |
| 72 for i, actual := range msgs { |
| 73 expected := expected[i].(memlogger.LogEntry) |
| 74 So(actual.Level, ShouldEqual, expected.Level) |
| 75 So(actual.Msg, ShouldContainSubstring, expected.Msg) |
| 76 } |
| 77 return "" |
| 78 } |
| 79 |
| 80 func TestClient(t *testing.T) { |
| 81 t.Parallel() |
| 82 |
| 83 Convey("Client", t, func() { |
| 84 setUp := func(h http.HandlerFunc) (*Client, *httptest.Server) { |
| 85 server := httptest.NewServer(h) |
| 86 client := &Client{ |
| 87 Host: strings.TrimPrefix(server.URL, "http://"), |
| 88 Options: &Options{ |
| 89 Retry: func() retry.Iterator { |
| 90 return &retry.ExponentialBackoff
{ |
| 91 Limited: retry.Limited{ |
| 92 Retries: 3, |
| 93 Delay: time.Mi
llisecond, |
| 94 }, |
| 95 } |
| 96 }, |
| 97 Insecure: true, |
| 98 UserAgent: "prpc-test", |
| 99 }, |
| 100 } |
| 101 return client, server |
| 102 } |
| 103 |
| 104 ctx := memlogger.Use(context.Background()) |
| 105 log := logging.Get(ctx).(*memlogger.MemLogger) |
| 106 expectedCallLogEntry := func(c *Client) memlogger.LogEntry { |
| 107 return memlogger.LogEntry{ |
| 108 Level: logging.Debug, |
| 109 Msg: fmt.Sprintf("RPC %s/prpc.Greeter.SayHello
", c.Host), |
| 110 } |
| 111 } |
| 112 |
| 113 req := &HelloRequest{"John"} |
| 114 res := &HelloReply{} |
| 115 |
| 116 Convey("Call", func() { |
| 117 Convey("Works", func(c C) { |
| 118 client, server := setUp(sayHello(c)) |
| 119 defer server.Close() |
| 120 |
| 121 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 122 So(err, ShouldBeNil) |
| 123 So(res.Message, ShouldEqual, "Hello John") |
| 124 |
| 125 So(log, shouldHaveMessagesLike, expectedCallLogE
ntry(client)) |
| 126 }) |
| 127 |
| 128 Convey("HTTP 500 x2", func(c C) { |
| 129 client, server := setUp(transientErrors(2, sayHe
llo(c))) |
| 130 defer server.Close() |
| 131 |
| 132 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 133 So(err, ShouldBeNil) |
| 134 So(res.Message, ShouldEqual, "Hello John") |
| 135 |
| 136 So(log, shouldHaveMessagesLike, |
| 137 expectedCallLogEntry(client), |
| 138 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 1ms"}, |
| 139 |
| 140 expectedCallLogEntry(client), |
| 141 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 2ms"}, |
| 142 |
| 143 expectedCallLogEntry(client), |
| 144 ) |
| 145 }) |
| 146 |
| 147 Convey("HTTP 500 many", func(c C) { |
| 148 client, server := setUp(transientErrors(10, sayH
ello(c))) |
| 149 defer server.Close() |
| 150 |
| 151 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 152 So(grpc.Code(err), ShouldEqual, codes.Internal) |
| 153 So(grpc.ErrorDesc(err), ShouldEqual, "Server mis
behaved") |
| 154 |
| 155 So(log, shouldHaveMessagesLike, |
| 156 expectedCallLogEntry(client), |
| 157 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 1ms"}, |
| 158 |
| 159 expectedCallLogEntry(client), |
| 160 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 2ms"}, |
| 161 |
| 162 expectedCallLogEntry(client), |
| 163 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 4ms"}, |
| 164 |
| 165 expectedCallLogEntry(client), |
| 166 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed permanently"}, |
| 167 ) |
| 168 }) |
| 169 |
| 170 Convey("Forbidden", func(c C) { |
| 171 client, server := setUp(func(w http.ResponseWrit
er, r *http.Request) { |
| 172 w.Header().Set(HeaderGRPCCode, strconv.I
toa(int(codes.PermissionDenied))) |
| 173 w.WriteHeader(http.StatusForbidden) |
| 174 fmt.Fprintln(w, "Access denied") |
| 175 }) |
| 176 defer server.Close() |
| 177 |
| 178 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 179 So(grpc.Code(err), ShouldEqual, codes.Permission
Denied) |
| 180 So(grpc.ErrorDesc(err), ShouldEqual, "Access den
ied") |
| 181 |
| 182 So(log, shouldHaveMessagesLike, |
| 183 expectedCallLogEntry(client), |
| 184 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed permanently"}, |
| 185 ) |
| 186 }) |
| 187 |
| 188 Convey(HeaderGRPCCode, func(c C) { |
| 189 client, server := setUp(func(w http.ResponseWrit
er, r *http.Request) { |
| 190 w.Header().Set(HeaderGRPCCode, strconv.I
toa(int(codes.Canceled))) |
| 191 w.WriteHeader(http.StatusBadRequest) |
| 192 }) |
| 193 defer server.Close() |
| 194 |
| 195 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 196 So(grpc.Code(err), ShouldEqual, codes.Canceled) |
| 197 }) |
| 198 }) |
| 199 }) |
| 200 } |
OLD | NEW |