proxygen
DownstreamTransactionTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree. An additional grant
7  * of patent rights can be found in the PATENTS file in the same directory.
8  *
9  */
18 
19 
20 
21 using namespace proxygen;
22 using namespace testing;
23 
24 using folly::IOBuf;
25 using folly::IOBufQueue;
26 using std::string;
27 using std::unique_ptr;
28 using std::vector;
29 
31  public:
33 
34  void SetUp() override {
35  EXPECT_CALL(transport_, describe(_))
36  .WillRepeatedly(Return());
37  }
38 
40  bool delayResponse=false) {
41  EXPECT_CALL(handler_, setTransaction(txn));
42  EXPECT_CALL(handler_, detachTransaction());
43  EXPECT_CALL(transport_, detach(txn));
44  if (delayResponse) {
45  EXPECT_CALL(handler_, onHeadersComplete(_));
46  } else {
47  EXPECT_CALL(handler_, onHeadersComplete(_))
48  .WillOnce(Invoke([=](std::shared_ptr<HTTPMessage> /*msg*/) {
49  auto response = makeResponse(200);
50  txn->sendHeaders(*response.get());
51  txn->sendBody(makeBuf(size));
52  txn->sendEOM();
53  }));
54  }
55  EXPECT_CALL(transport_, sendHeaders(txn, _, _, _))
56  .WillOnce(Invoke([=](Unused, const HTTPMessage& headers, Unused, Unused) {
57  EXPECT_EQ(headers.getStatusCode(), 200);
58  }));
59  EXPECT_CALL(transport_, sendBody(txn, _, false, false))
60  .WillRepeatedly(Invoke([=](Unused, std::shared_ptr<folly::IOBuf> body,
61  Unused, Unused) {
62  auto cur = body->computeChainDataLength();
63  sent_ += cur;
64  return cur;
65  }));
66  if (delayResponse) {
67  EXPECT_CALL(transport_, sendEOM(txn, _));
68  } else {
69  EXPECT_CALL(transport_, sendEOM(txn, _))
70  .WillOnce(InvokeWithoutArgs([=]() {
71  CHECK_EQ(sent_, size);
72  txn->onIngressBody(makeBuf(size), 0);
73  txn->onIngressEOM();
74  return 5;
75  }));
76  }
77  EXPECT_CALL(handler_, onBody(_))
78  .WillRepeatedly(Invoke([=](std::shared_ptr<folly::IOBuf> body) {
79  received_ += body->computeChainDataLength();
80  }));
81  EXPECT_CALL(handler_, onEOM())
82  .WillOnce(InvokeWithoutArgs([=] {
83  CHECK_EQ(received_, size);
84  }));
85  EXPECT_CALL(transport_, notifyPendingEgress())
86  .WillOnce(InvokeWithoutArgs([=] {
87  txn->onWriteReady(size, 1);
88  }))
89  .WillOnce(DoDefault()); // The second call is for sending the eom
90 
91  txn->setHandler(&handler_);
92  }
93 
94  protected:
96  folly::HHWheelTimer::UniquePtr transactionTimeouts_{
98  &eventBase_,
99  std::chrono::milliseconds(folly::HHWheelTimer::DEFAULT_TICK_INTERVAL),
101  std::chrono::milliseconds(500))};
105  uint32_t received_{0};
106  uint32_t sent_{0};
107 };
108 
113 TEST_F(DownstreamTransactionTest, SimpleCallbackForwarding) {
114  // flow control is disabled
115  HTTPTransaction txn(
118  txnEgressQueue_, transactionTimeouts_.get(),
119  std::chrono::milliseconds(500));
120  setupRequestResponseFlow(&txn, 100);
121 
122  txn.onIngressHeadersComplete(makeGetRequest());
123  eventBase_.loop();
124 }
125 
129 TEST_F(DownstreamTransactionTest, RegularWindowUpdate) {
130  HTTPTransaction txn(
133  txnEgressQueue_, transactionTimeouts_.get(),
134  std::chrono::milliseconds(500),
135  nullptr,
136  true, // flow control enabled
137  400,
139  uint32_t reqBodySize = 220;
140  setupRequestResponseFlow(&txn, reqBodySize);
141 
142  // test that the window update is generated
143  EXPECT_CALL(transport_, sendWindowUpdate(_, reqBodySize));
144 
145  // run the test
146  txn.onIngressHeadersComplete(makeGetRequest());
147  eventBase_.loop();
148 }
149 
151  HTTPTransaction txn(
154  txnEgressQueue_, transactionTimeouts_.get(),
155  std::chrono::milliseconds(500),
156  nullptr,
157  true, // flow control enabled
158  450, // more than 2x req size
160  uint32_t reqBodySize = 220;
161  setupRequestResponseFlow(&txn, reqBodySize, true);
162 
163  EXPECT_CALL(transport_, sendWindowUpdate(_, reqBodySize))
164  .Times(0);
165 
166  // run the test
167  txn.onIngressHeadersComplete(makeGetRequest());
168  txn.onIngressBody(makeBuf(reqBodySize), 0);
169  txn.onIngressEOM();
170  auto response = makeResponse(200);
171  txn.sendHeaders(*response.get());
172  txn.sendBody(makeBuf(reqBodySize));
173  txn.sendEOM();
174  eventBase_.loop();
175 }
176 
182  // set initial window size higher than per-stream window
183  HTTPTransaction txn(
186  txnEgressQueue_, transactionTimeouts_.get(),
187  std::chrono::milliseconds(500),
188  nullptr,
189  true, // flow control enabled
192  uint32_t reqSize = 500;
193  setupRequestResponseFlow(&txn, reqSize);
194 
195  // we expect the difference from the per stream window and the initial window,
196  // together with the bytes sent in the request
197  uint32_t perStreamWindow = spdy::kInitialWindow + 1024 * 1024;
198  uint32_t expectedWindowUpdate =
199  perStreamWindow - spdy::kInitialWindow;
200  EXPECT_CALL(transport_, sendWindowUpdate(_, expectedWindowUpdate));
201 
202  // use a higher window
203  txn.setReceiveWindow(perStreamWindow);
204 
205  txn.onIngressHeadersComplete(makeGetRequest());
206  eventBase_.loop();
207 }
208 
214  // set initial window size higher than per-stream window
215  HTTPTransaction txn(
218  txnEgressQueue_, transactionTimeouts_.get(),
219  std::chrono::milliseconds(500),
220  nullptr,
221  true, // flow control enabled
224  setupRequestResponseFlow(&txn, 500);
225 
226  // in this case, there should be no window update, as we decrease the window
227  // below the number of bytes we're sending
228  EXPECT_CALL(transport_, sendWindowUpdate(_, _)).Times(0);
229 
230  // use a smaller window
231  uint32_t perStreamWindow = spdy::kInitialWindow - 1000;
232  txn.setReceiveWindow(perStreamWindow);
233 
234  txn.onIngressHeadersComplete(makeGetRequest());
235  eventBase_.loop();
236 }
237 
239  // Test where the transaction gets on parse error and then a body
240  // callback. This is possible because codecs are stateless between
241  // frames.
242 
243  HTTPTransaction txn(
246  txnEgressQueue_, transactionTimeouts_.get(),
247  std::chrono::milliseconds(500));
248 
250  err.setHttpStatusCode(400);
251 
253 
254  EXPECT_CALL(handler_, setTransaction(&txn));
255  EXPECT_CALL(handler_, onError(_))
256  .WillOnce(Invoke([] (const HTTPException& ex) {
258  ASSERT_EQ(std::string(ex.what()), "test");
259  }));
260  // onBody() is suppressed since ingress is complete after ingress onError()
261  // onEOM() is suppressed since ingress is complete after ingress onError()
262  EXPECT_CALL(transport_, sendAbort(_, _));
263  EXPECT_CALL(handler_, detachTransaction());
264  EXPECT_CALL(transport_, detach(&txn));
265 
266  txn.setHandler(&handler_);
267  txn.onError(err);
268  // Since the transaction is already closed for ingress, giving it
269  // ingress body causes the transaction to be aborted and closed
270  // immediately.
271  txn.onIngressBody(makeBuf(10), 0);
272 
273  eventBase_.loop();
274 }
275 
276 TEST_F(DownstreamTransactionTest, DetachFromNotify) {
277  unique_ptr<StrictMock<MockHTTPHandler>> handler(
279 
280  HTTPTransaction txn(
283  txnEgressQueue_, transactionTimeouts_.get(),
284  std::chrono::milliseconds(500));
285 
287 
288  EXPECT_CALL(*handler, setTransaction(&txn));
289  EXPECT_CALL(*handler, onHeadersComplete(_))
290  .WillOnce(Invoke([&](std::shared_ptr<HTTPMessage> /*msg*/) {
291  auto response = makeResponse(200);
292  txn.sendHeaders(*response.get());
293  txn.sendBody(makeBuf(10));
294  }));
295  EXPECT_CALL(transport_, sendHeaders(&txn, _, _, _))
296  .WillOnce(Invoke([&](Unused, const HTTPMessage& headers, Unused, Unused) {
297  EXPECT_EQ(headers.getStatusCode(), 200);
298  }));
299  EXPECT_CALL(transport_, notifyEgressBodyBuffered(10));
300  EXPECT_CALL(transport_, notifyEgressBodyBuffered(-10))
301  .WillOnce(InvokeWithoutArgs([&] () {
302  txn.setHandler(nullptr);
303  handler.reset();
304  }));
305  EXPECT_CALL(transport_, detach(&txn));
306 
308 
309  txn.setHandler(handler.get());
310  txn.onIngressHeadersComplete(makeGetRequest());
311  txn.onError(err);
312 }
313 
315  EXPECT_CALL(transport_, describe(_))
316  .WillRepeatedly(Return());
317  EXPECT_CALL(transport_, notifyPendingEgress())
318  .WillRepeatedly(Return());
319 
320  HTTPTransaction txn(
323  txnEgressQueue_, transactionTimeouts_.get(),
324  std::chrono::milliseconds(500),
325  nullptr, true, 10, 10);
326 
328 
329  EXPECT_CALL(handler_, setTransaction(&txn));
330  EXPECT_CALL(handler_, onHeadersComplete(_))
331  .WillOnce(Invoke([&](std::shared_ptr<HTTPMessage> /*msg*/) {
332  auto response = makeResponse(200);
333  txn.sendHeaders(*response.get());
334  txn.sendBody(makeBuf(10));
335  txn.sendBody(makeBuf(20));
336  }));
337  EXPECT_CALL(transport_, sendHeaders(&txn, _, _, _))
338  .WillOnce(Invoke([&](Unused, const HTTPMessage& headers, Unused, Unused) {
339  EXPECT_EQ(headers.getStatusCode(), 200);
340  }));
341 
342  // when enqueued
343  EXPECT_CALL(transport_, notifyEgressBodyBuffered(10));
344  EXPECT_CALL(handler_, onEgressPaused());
345  // sendBody
346  EXPECT_CALL(transport_, notifyEgressBodyBuffered(20));
347 
348  txn.setHandler(&handler_);
349  txn.onIngressHeadersComplete(makeGetRequest());
350 
351  // onWriteReady, send, then dequeue (SPDY window now full)
352  EXPECT_CALL(transport_, notifyEgressBodyBuffered(-20));
353 
354  EXPECT_EQ(txn.onWriteReady(20, 1), false);
355 
356  // enqueued after window update
357  EXPECT_CALL(transport_, notifyEgressBodyBuffered(20));
358 
359  txn.onIngressWindowUpdate(20);
360 
361  // Buffer released on error
362  EXPECT_CALL(transport_, notifyEgressBodyBuffered(-20));
363  EXPECT_CALL(handler_, onError(_));
364  EXPECT_CALL(handler_, detachTransaction());
365  EXPECT_CALL(transport_, detach(&txn));
366 
368  txn.onError(err);
369 }
370 
372  unique_ptr<StrictMock<MockHTTPHandler>> handler(
374 
375  HTTPTransaction txn(
378  txnEgressQueue_, transactionTimeouts_.get(),
379  std::chrono::milliseconds(500));
380 
382 
383  EXPECT_CALL(*handler, setTransaction(&txn));
384  EXPECT_CALL(*handler, onHeadersComplete(_))
385  .WillOnce(Invoke([&](std::shared_ptr<HTTPMessage> /*msg*/) {
386  auto response = makeResponse(200);
387  txn.sendHeaders(*response.get());
388  }));
389  EXPECT_CALL(transport_, sendHeaders(&txn, _, _, _))
390  .WillOnce(Invoke([&](Unused, const HTTPMessage& headers, Unused, Unused) {
391  EXPECT_EQ(headers.getStatusCode(), 200);
392  }));
394  EXPECT_CALL(*handler, detachTransaction());
395  EXPECT_CALL(transport_, detach(&txn));
396 
398 
399  txn.setHandler(handler.get());
400  txn.onIngressHeadersComplete(makeGetRequest());
401  txn.sendAbort();
402 }
403 
404 TEST_F(DownstreamTransactionTest, UnpausedFlowControlViolation) {
406 
407  InSequence enforceOrder;
408  HTTPTransaction txn(
411  txnEgressQueue_, transactionTimeouts_.get(),
412  std::chrono::milliseconds(500),
413  nullptr,
414  true, // flow control enabled
415  400,
417 
418  EXPECT_CALL(handler, setTransaction(&txn));
419  EXPECT_CALL(handler, onHeadersComplete(_));
421  EXPECT_CALL(handler, detachTransaction());
422  EXPECT_CALL(transport_, detach(&txn));
423 
424  txn.setHandler(&handler);
425  txn.onIngressHeadersComplete(makePostRequest(401));
426  txn.onIngressBody(makeBuf(401), 0);
427 }
virtual void setHandler(Handler *handler)
MockHTTPTransactionTransport transport_
bool onWriteReady(uint32_t maxEgress, double ratio)
std::unique_ptr< folly::IOBuf > makeBuf(uint32_t size)
Definition: TestUtils.cpp:36
#define ASSERT_EQ(val1, val2)
Definition: gtest.h:1956
uint16_t getStatusCode() const
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
void onIngressBody(std::unique_ptr< folly::IOBuf > chain, uint16_t padding)
static int DEFAULT_TICK_INTERVAL
Definition: HHWheelTimer.h:163
std::unique_ptr< HTTPMessage > makePostRequest(uint32_t contentLength)
Definition: TestUtils.cpp:124
PolymorphicAction< internal::InvokeWithoutArgsAction< FunctionImpl > > InvokeWithoutArgs(FunctionImpl function_impl)
void handler(int, siginfo_t *, void *)
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
static UniquePtr newTimer(Args &&...args)
Definition: HHWheelTimer.h:61
Direction getDirection() const
Definition: HTTPException.h:67
PolymorphicAction< internal::InvokeAction< FunctionImpl > > Invoke(FunctionImpl function_impl)
void dummy()
virtual void sendBody(std::unique_ptr< folly::IOBuf > body)
virtual void sendHeaders(const HTTPMessage &headers)
std::unique_ptr< HHWheelTimer, Destructor > UniquePtr
Definition: HHWheelTimer.h:57
std::unique_ptr< HTTPMessage > makeResponse(uint16_t statusCode)
Definition: TestUtils.cpp:147
void setupRequestResponseFlow(HTTPTransaction *txn, uint32_t size, bool delayResponse=false)
internal::DoDefaultAction DoDefault()
StrictMock< MockHTTPHandler > handler_
const char * string
Definition: Conv.cpp:212
AsyncFizzClient::UniquePtr transport_
std::unique_ptr< HTTPMessage > makeGetRequest()
Definition: TestUtils.cpp:98
#define EXPECT_CALL(obj, call)
uint64_t StreamID
Definition: HTTPCodec.h:49
const internal::AnythingMatcher _
TEST_F(HeaderTableTests, IndexTranslation)
const char * what(void) const noexceptoverride
Definition: Exception.cpp:26
internal::ReturnAction< R > Return(R value)
const uint32_t kInitialWindow