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/luci/luci-go/common/testing/assertions" |
| 27 . "github.com/smartystreets/goconvey/convey" |
| 28 ) |
| 29 |
| 30 func sayHello(c C) http.HandlerFunc { |
| 31 return func(w http.ResponseWriter, r *http.Request) { |
| 32 c.So(r.Method, ShouldEqual, "POST") |
| 33 c.So(r.URL.Path, ShouldEqual, "/prpc/prpc.Greeter/SayHello") |
| 34 c.So(r.Header.Get("Accept"), ShouldEqual, "application/prpc") |
| 35 c.So(r.Header.Get("Content-Type"), ShouldEqual, "application/prp
c") |
| 36 c.So(r.Header.Get("User-Agent"), ShouldEqual, "prpc-test") |
| 37 |
| 38 reqBody, err := ioutil.ReadAll(r.Body) |
| 39 c.So(err, ShouldBeNil) |
| 40 |
| 41 var req HelloRequest |
| 42 err = proto.Unmarshal(reqBody, &req) |
| 43 c.So(err, ShouldBeNil) |
| 44 |
| 45 res := HelloReply{"Hello " + req.Name} |
| 46 buf, err := proto.Marshal(&res) |
| 47 c.So(err, ShouldBeNil) |
| 48 |
| 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.WriteHeader(http.StatusInternalServerError) |
| 59 fmt.Fprintln(w, "Server misbehaved") |
| 60 return |
| 61 } |
| 62 then.ServeHTTP(w, r) |
| 63 } |
| 64 } |
| 65 |
| 66 func shouldHaveMessagesLike(actual interface{}, expected ...interface{}) string
{ |
| 67 log := actual.(*memlogger.MemLogger) |
| 68 msgs := log.Messages() |
| 69 |
| 70 So(msgs, ShouldHaveLength, len(expected)) |
| 71 for i, actual := range msgs { |
| 72 expected := expected[i].(memlogger.LogEntry) |
| 73 So(actual.Level, ShouldEqual, expected.Level) |
| 74 So(actual.Msg, ShouldContainSubstring, expected.Msg) |
| 75 } |
| 76 return "" |
| 77 } |
| 78 |
| 79 func TestClient(t *testing.T) { |
| 80 t.Parallel() |
| 81 |
| 82 Convey("Client", t, func() { |
| 83 setUp := func(h http.HandlerFunc) (*Client, *httptest.Server) { |
| 84 server := httptest.NewServer(h) |
| 85 client := &Client{ |
| 86 Host: strings.TrimPrefix(server.URL, "http://"), |
| 87 Options: &Options{ |
| 88 Retry: func() retry.Iterator { |
| 89 return &retry.ExponentialBackoff
{ |
| 90 Limited: retry.Limited{ |
| 91 Retries: 3, |
| 92 Delay: time.Mi
llisecond, |
| 93 }, |
| 94 } |
| 95 }, |
| 96 Insecure: true, |
| 97 UserAgent: "prpc-test", |
| 98 }, |
| 99 } |
| 100 return client, server |
| 101 } |
| 102 |
| 103 ctx := memlogger.Use(context.Background()) |
| 104 log := logging.Get(ctx).(*memlogger.MemLogger) |
| 105 expectedCallLogEntry := func(c *Client) memlogger.LogEntry { |
| 106 return memlogger.LogEntry{ |
| 107 Level: logging.Debug, |
| 108 Msg: fmt.Sprintf("RPC %s/prpc.Greeter.SayHello
", c.Host), |
| 109 } |
| 110 } |
| 111 |
| 112 req := &HelloRequest{"John"} |
| 113 res := &HelloReply{} |
| 114 |
| 115 Convey("Call", func() { |
| 116 Convey("Works", func(c C) { |
| 117 client, server := setUp(sayHello(c)) |
| 118 defer server.Close() |
| 119 |
| 120 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 121 So(err, ShouldBeNil) |
| 122 So(res.Message, ShouldEqual, "Hello John") |
| 123 |
| 124 So(log, shouldHaveMessagesLike, expectedCallLogE
ntry(client)) |
| 125 }) |
| 126 |
| 127 Convey("HTTP 500 x2", func(c C) { |
| 128 client, server := setUp(transientErrors(2, sayHe
llo(c))) |
| 129 defer server.Close() |
| 130 |
| 131 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 132 So(err, ShouldBeNil) |
| 133 So(res.Message, ShouldEqual, "Hello John") |
| 134 |
| 135 So(log, shouldHaveMessagesLike, |
| 136 expectedCallLogEntry(client), |
| 137 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 1ms"}, |
| 138 |
| 139 expectedCallLogEntry(client), |
| 140 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 2ms"}, |
| 141 |
| 142 expectedCallLogEntry(client), |
| 143 ) |
| 144 }) |
| 145 |
| 146 Convey("HTTP 500 many", func(c C) { |
| 147 client, server := setUp(transientErrors(10, sayH
ello(c))) |
| 148 defer server.Close() |
| 149 |
| 150 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 151 So(err, ShouldErrLike, "HTTP 500 Internal Server
Error: Server misbehaved") |
| 152 So(grpc.Code(err), ShouldEqual, codes.Internal) |
| 153 |
| 154 So(log, shouldHaveMessagesLike, |
| 155 expectedCallLogEntry(client), |
| 156 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 1ms"}, |
| 157 |
| 158 expectedCallLogEntry(client), |
| 159 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 2ms"}, |
| 160 |
| 161 expectedCallLogEntry(client), |
| 162 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 4ms"}, |
| 163 |
| 164 expectedCallLogEntry(client), |
| 165 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed permanently"}, |
| 166 ) |
| 167 }) |
| 168 |
| 169 Convey("HTTP 403 x10", func(c C) { |
| 170 client, server := setUp(func(w http.ResponseWrit
er, r *http.Request) { |
| 171 w.WriteHeader(http.StatusForbidden) |
| 172 }) |
| 173 defer server.Close() |
| 174 |
| 175 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 176 So(err, ShouldErrLike, "HTTP 403") |
| 177 So(grpc.Code(err), ShouldEqual, codes.Permission
Denied) |
| 178 |
| 179 So(log, shouldHaveMessagesLike, |
| 180 expectedCallLogEntry(client), |
| 181 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed permanently"}, |
| 182 ) |
| 183 }) |
| 184 |
| 185 Convey(HeaderGrpcCode, func(c C) { |
| 186 client, server := setUp(func(w http.ResponseWrit
er, r *http.Request) { |
| 187 w.Header().Set(HeaderGrpcCode, strconv.I
toa(int(codes.Canceled))) |
| 188 w.WriteHeader(http.StatusBadRequest) |
| 189 }) |
| 190 defer server.Close() |
| 191 |
| 192 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 193 So(grpc.Code(err), ShouldEqual, codes.Canceled) |
| 194 }) |
| 195 }) |
| 196 }) |
| 197 } |
OLD | NEW |