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/clock" |
| 23 "github.com/luci/luci-go/common/clock/testclock" |
| 24 "github.com/luci/luci-go/common/logging" |
| 25 "github.com/luci/luci-go/common/logging/memlogger" |
| 26 "github.com/luci/luci-go/common/retry" |
| 27 |
| 28 . "github.com/smartystreets/goconvey/convey" |
| 29 ) |
| 30 |
| 31 func sayHello(c C) http.HandlerFunc { |
| 32 return func(w http.ResponseWriter, r *http.Request) { |
| 33 c.So(r.Method, ShouldEqual, "POST") |
| 34 c.So(r.URL.Path, ShouldEqual, "/prpc/prpc.Greeter/SayHello") |
| 35 c.So(r.Header.Get("Accept"), ShouldEqual, "application/prpc") |
| 36 c.So(r.Header.Get("Content-Type"), ShouldEqual, "application/prp
c") |
| 37 c.So(r.Header.Get("User-Agent"), ShouldEqual, "prpc-test") |
| 38 |
| 39 if timeout := r.Header.Get(HeaderTimeout); timeout != "" { |
| 40 c.So(timeout, ShouldEqual, "10000000u") |
| 41 } |
| 42 |
| 43 reqBody, err := ioutil.ReadAll(r.Body) |
| 44 c.So(err, ShouldBeNil) |
| 45 |
| 46 var req HelloRequest |
| 47 err = proto.Unmarshal(reqBody, &req) |
| 48 c.So(err, ShouldBeNil) |
| 49 |
| 50 if req.Name == "TOO BIG" { |
| 51 w.Header().Set("Content-Length", "999999999999") |
| 52 } |
| 53 |
| 54 res := HelloReply{"Hello " + req.Name} |
| 55 buf, err := proto.Marshal(&res) |
| 56 c.So(err, ShouldBeNil) |
| 57 |
| 58 w.Header().Set(HeaderGRPCCode, strconv.Itoa(int(codes.OK))) |
| 59 _, err = w.Write(buf) |
| 60 c.So(err, ShouldBeNil) |
| 61 } |
| 62 } |
| 63 |
| 64 func doPanicHandler(w http.ResponseWriter, r *http.Request) { |
| 65 panic("test panic") |
| 66 } |
| 67 |
| 68 func transientErrors(count int, then http.Handler) http.HandlerFunc { |
| 69 return func(w http.ResponseWriter, r *http.Request) { |
| 70 if count > 0 { |
| 71 count-- |
| 72 w.Header().Set(HeaderGRPCCode, strconv.Itoa(int(codes.In
ternal))) |
| 73 w.WriteHeader(http.StatusInternalServerError) |
| 74 fmt.Fprintln(w, "Server misbehaved") |
| 75 return |
| 76 } |
| 77 then.ServeHTTP(w, r) |
| 78 } |
| 79 } |
| 80 |
| 81 func shouldHaveMessagesLike(actual interface{}, expected ...interface{}) string
{ |
| 82 log := actual.(*memlogger.MemLogger) |
| 83 msgs := log.Messages() |
| 84 |
| 85 So(msgs, ShouldHaveLength, len(expected)) |
| 86 for i, actual := range msgs { |
| 87 expected := expected[i].(memlogger.LogEntry) |
| 88 So(actual.Level, ShouldEqual, expected.Level) |
| 89 So(actual.Msg, ShouldContainSubstring, expected.Msg) |
| 90 } |
| 91 return "" |
| 92 } |
| 93 |
| 94 func TestClient(t *testing.T) { |
| 95 t.Parallel() |
| 96 |
| 97 Convey("Client", t, func() { |
| 98 setUp := func(h http.HandlerFunc) (*Client, *httptest.Server) { |
| 99 server := httptest.NewServer(h) |
| 100 client := &Client{ |
| 101 Host: strings.TrimPrefix(server.URL, "http://"), |
| 102 Options: &Options{ |
| 103 Retry: func() retry.Iterator { |
| 104 return &retry.Limited{ |
| 105 Retries: 3, |
| 106 Delay: 0, |
| 107 } |
| 108 }, |
| 109 Insecure: true, |
| 110 UserAgent: "prpc-test", |
| 111 }, |
| 112 } |
| 113 return client, server |
| 114 } |
| 115 |
| 116 ctx, _ := testclock.UseTime(context.Background(), testclock.Test
TimeLocal) |
| 117 ctx = memlogger.Use(ctx) |
| 118 log := logging.Get(ctx).(*memlogger.MemLogger) |
| 119 expectedCallLogEntry := func(c *Client) memlogger.LogEntry { |
| 120 return memlogger.LogEntry{ |
| 121 Level: logging.Debug, |
| 122 Msg: fmt.Sprintf("RPC %s/prpc.Greeter.SayHello
", c.Host), |
| 123 } |
| 124 } |
| 125 |
| 126 req := &HelloRequest{"John"} |
| 127 res := &HelloReply{} |
| 128 |
| 129 Convey("Call", func() { |
| 130 Convey("Works", func(c C) { |
| 131 client, server := setUp(sayHello(c)) |
| 132 defer server.Close() |
| 133 |
| 134 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 135 So(err, ShouldBeNil) |
| 136 So(res.Message, ShouldEqual, "Hello John") |
| 137 |
| 138 So(log, shouldHaveMessagesLike, expectedCallLogE
ntry(client)) |
| 139 }) |
| 140 |
| 141 Convey("With a deadline <= now, does not execute.", func
(c C) { |
| 142 client, server := setUp(doPanicHandler) |
| 143 defer server.Close() |
| 144 |
| 145 ctx, _ = context.WithDeadline(ctx, clock.Now(ctx
)) |
| 146 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 147 So(err, ShouldEqual, context.DeadlineExceeded) |
| 148 }) |
| 149 |
| 150 Convey("With a deadline in the future, sets the deadline
header.", func(c C) { |
| 151 client, server := setUp(sayHello(c)) |
| 152 defer server.Close() |
| 153 |
| 154 ctx, _ = clock.WithDeadline(ctx, clock.Now(ctx).
Add(10*time.Second)) |
| 155 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 156 So(err, ShouldBeNil) |
| 157 So(res.Message, ShouldEqual, "Hello John") |
| 158 |
| 159 So(log, shouldHaveMessagesLike, expectedCallLogE
ntry(client)) |
| 160 }) |
| 161 |
| 162 Convey(`With a maximum content length smaller than the r
esponse, returns "ErrResponseTooBig".`, func(c C) { |
| 163 client, server := setUp(sayHello(c)) |
| 164 defer server.Close() |
| 165 |
| 166 client.MaxContentLength = 8 |
| 167 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 168 So(err, ShouldEqual, ErrResponseTooBig) |
| 169 }) |
| 170 |
| 171 Convey(`When the response returns a huge Content Length,
returns "ErrResponseTooBig".`, func(c C) { |
| 172 client, server := setUp(sayHello(c)) |
| 173 defer server.Close() |
| 174 |
| 175 req.Name = "TOO BIG" |
| 176 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 177 So(err, ShouldEqual, ErrResponseTooBig) |
| 178 }) |
| 179 |
| 180 Convey("HTTP 500 x2", func(c C) { |
| 181 client, server := setUp(transientErrors(2, sayHe
llo(c))) |
| 182 defer server.Close() |
| 183 |
| 184 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 185 So(err, ShouldBeNil) |
| 186 So(res.Message, ShouldEqual, "Hello John") |
| 187 |
| 188 So(log, shouldHaveMessagesLike, |
| 189 expectedCallLogEntry(client), |
| 190 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 0"}, |
| 191 |
| 192 expectedCallLogEntry(client), |
| 193 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 0"}, |
| 194 |
| 195 expectedCallLogEntry(client), |
| 196 ) |
| 197 }) |
| 198 |
| 199 Convey("HTTP 500 many", func(c C) { |
| 200 client, server := setUp(transientErrors(10, sayH
ello(c))) |
| 201 defer server.Close() |
| 202 |
| 203 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 204 So(grpc.Code(err), ShouldEqual, codes.Internal) |
| 205 So(grpc.ErrorDesc(err), ShouldEqual, "Server mis
behaved") |
| 206 |
| 207 So(log, shouldHaveMessagesLike, |
| 208 expectedCallLogEntry(client), |
| 209 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 0"}, |
| 210 |
| 211 expectedCallLogEntry(client), |
| 212 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 0"}, |
| 213 |
| 214 expectedCallLogEntry(client), |
| 215 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed transiently. Will retry in 0"}, |
| 216 |
| 217 expectedCallLogEntry(client), |
| 218 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed permanently"}, |
| 219 ) |
| 220 }) |
| 221 |
| 222 Convey("Forbidden", func(c C) { |
| 223 client, server := setUp(func(w http.ResponseWrit
er, r *http.Request) { |
| 224 w.Header().Set(HeaderGRPCCode, strconv.I
toa(int(codes.PermissionDenied))) |
| 225 w.WriteHeader(http.StatusForbidden) |
| 226 fmt.Fprintln(w, "Access denied") |
| 227 }) |
| 228 defer server.Close() |
| 229 |
| 230 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 231 So(grpc.Code(err), ShouldEqual, codes.Permission
Denied) |
| 232 So(grpc.ErrorDesc(err), ShouldEqual, "Access den
ied") |
| 233 |
| 234 So(log, shouldHaveMessagesLike, |
| 235 expectedCallLogEntry(client), |
| 236 memlogger.LogEntry{Level: logging.Warnin
g, Msg: "RPC failed permanently"}, |
| 237 ) |
| 238 }) |
| 239 |
| 240 Convey(HeaderGRPCCode, func(c C) { |
| 241 client, server := setUp(func(w http.ResponseWrit
er, r *http.Request) { |
| 242 w.Header().Set(HeaderGRPCCode, strconv.I
toa(int(codes.Canceled))) |
| 243 w.WriteHeader(http.StatusBadRequest) |
| 244 }) |
| 245 defer server.Close() |
| 246 |
| 247 err := client.Call(ctx, "prpc.Greeter", "SayHell
o", req, res) |
| 248 So(grpc.Code(err), ShouldEqual, codes.Canceled) |
| 249 }) |
| 250 }) |
| 251 }) |
| 252 } |
OLD | NEW |