1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
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.
7 */
8
9 #include <proxygen/lib/http/session/HTTPUpstreamSession.h>
10
11 #include <folly/io/Cursor.h>
12 #include <folly/io/async/EventBase.h>
13 #include <folly/io/async/TimeoutManager.h>
14 #include <folly/io/async/test/MockAsyncTransport.h>
15 #include <folly/portability/GTest.h>
16 #include <proxygen/lib/http/codec/HTTPCodecFactory.h>
17 #include <proxygen/lib/http/codec/test/MockHTTPCodec.h>
18 #include <proxygen/lib/http/codec/test/TestUtils.h>
19 #include <proxygen/lib/http/session/test/HTTPSessionMocks.h>
20 #include <proxygen/lib/http/session/test/HTTPSessionTest.h>
21 #include <proxygen/lib/http/session/test/MockByteEventTracker.h>
22 #include <proxygen/lib/http/session/test/TestUtils.h>
23 #include <proxygen/lib/test/TestAsyncTransport.h>
24 #include <string>
25 #include <vector>
26 #include <wangle/acceptor/ConnectionManager.h>
27
28 using folly::test::MockAsyncTransport;
29
30 using namespace folly;
31 using namespace proxygen;
32 using namespace testing;
33
34 using std::string;
35 using std::unique_ptr;
36
37 class TestPriorityMapBuilder : public HTTPUpstreamSession::PriorityMapFactory {
38 public:
39 std::unique_ptr<HTTPUpstreamSession::PriorityAdapter> createVirtualStreams(
40 HTTPPriorityMapFactoryProvider* session) const override;
41 ~TestPriorityMapBuilder() override = default;
42
43 uint8_t hiPriWeight_{18};
44 uint8_t hiPriLevel_{0};
45 uint8_t loPriWeight_{2};
46 uint8_t loPriLevel_{2};
47 };
48
49 class TestPriorityAdapter : public HTTPUpstreamSession::PriorityAdapter {
50 public:
getHTTPPriority(uint8_t level)51 folly::Optional<const HTTPMessage::HTTP2Priority> getHTTPPriority(
52 uint8_t level) override {
53 if (priorityMap_.empty()) {
54 return folly::none;
55 }
56 auto it = priorityMap_.find(level);
57 if (it == priorityMap_.end()) {
58 return minPriority_;
59 }
60 return it->second;
61 }
62
63 ~TestPriorityAdapter() override = default;
64
65 std::map<uint8_t, HTTPMessage::HTTP2Priority> priorityMap_;
66 HTTPMessage::HTTP2Priority minPriority_{std::make_tuple(0, false, 0)};
67 HTTPCodec::StreamID parentId_{0};
68 HTTPCodec::StreamID hiPriId_{0};
69 HTTPCodec::StreamID loPriId_{0};
70 HTTPMessage::HTTP2Priority hiPri_{std::make_tuple(0, false, 0)};
71 HTTPMessage::HTTP2Priority loPri_{std::make_tuple(0, false, 0)};
72 };
73
74 std::unique_ptr<HTTPUpstreamSession::PriorityAdapter>
createVirtualStreams(HTTPPriorityMapFactoryProvider * session) const75 TestPriorityMapBuilder::createVirtualStreams(
76 HTTPPriorityMapFactoryProvider* session) const {
77 std::unique_ptr<TestPriorityAdapter> a =
78 std::make_unique<TestPriorityAdapter>();
79 a->parentId_ = session->sendPriority({0, false, 1});
80
81 std::get<0>(a->hiPri_) = a->parentId_;
82 std::get<2>(a->hiPri_) = hiPriWeight_;
83 a->hiPriId_ = session->sendPriority({a->parentId_, false, hiPriWeight_});
84 a->priorityMap_[hiPriLevel_] = a->hiPri_;
85
86 std::get<0>(a->loPri_) = a->parentId_;
87 std::get<2>(a->loPri_) = loPriWeight_;
88 a->loPriId_ = session->sendPriority({a->parentId_, false, loPriWeight_});
89 a->priorityMap_[loPriLevel_] = a->loPri_;
90
91 a->minPriority_ = a->loPri_;
92
93 return std::move(a);
94 }
95
96 namespace {
getUpgradePostRequest(uint32_t bodyLen,const std::string & upgradeHeader,bool expect100=false)97 HTTPMessage getUpgradePostRequest(uint32_t bodyLen,
98 const std::string& upgradeHeader,
99 bool expect100 = false) {
100 HTTPMessage req = getPostRequest(bodyLen);
101 req.getHeaders().set(HTTP_HEADER_UPGRADE, upgradeHeader);
102 if (expect100) {
103 req.getHeaders().add(HTTP_HEADER_EXPECT, "100-continue");
104 }
105 return req;
106 }
107
getResponseBuf(CodecProtocol protocol,HTTPCodec::StreamID id,uint32_t code,uint32_t bodyLen,bool include100=false)108 std::unique_ptr<folly::IOBuf> getResponseBuf(CodecProtocol protocol,
109 HTTPCodec::StreamID id,
110 uint32_t code,
111 uint32_t bodyLen,
112 bool include100 = false) {
113 auto egressCodec =
114 HTTPCodecFactory::getCodec(protocol, TransportDirection::DOWNSTREAM);
115 folly::IOBufQueue respBufQ{folly::IOBufQueue::cacheChainLength()};
116 egressCodec->generateSettings(respBufQ);
117 if (include100) {
118 HTTPMessage msg;
119 msg.setStatusCode(100);
120 msg.setStatusMessage("continue");
121 egressCodec->generateHeader(respBufQ, id, msg);
122 }
123 HTTPMessage resp = getResponse(code, bodyLen);
124 egressCodec->generateHeader(respBufQ, id, resp);
125 if (bodyLen > 0) {
126 auto buf = makeBuf(bodyLen);
127 egressCodec->generateBody(
128 respBufQ, id, std::move(buf), HTTPCodec::NoPadding, true /* eom */);
129 }
130 return respBufQ.move();
131 }
132
133 } // namespace
134
135 template <class C>
136 class HTTPUpstreamTest
137 : public testing::Test
138 , public HTTPSessionBase::InfoCallback {
139 public:
HTTPUpstreamTest(std::vector<int64_t> flowControl={-1, -1, -1})140 explicit HTTPUpstreamTest(std::vector<int64_t> flowControl = {-1, -1, -1})
141 : eventBase_(),
142 transport_(new NiceMock<MockAsyncTransport>()),
143 transactionTimeouts_(folly::HHWheelTimer::newTimer(
144 &eventBase_,
145 std::chrono::milliseconds(
146 folly::HHWheelTimer::DEFAULT_TICK_INTERVAL),
147 TimeoutManager::InternalEnum::INTERNAL,
148 std::chrono::milliseconds(500))),
149 flowControl_(flowControl) {
150 }
151
resumeWrites()152 void resumeWrites() {
153 std::vector<folly::AsyncTransport::WriteCallback*> cbs;
154 std::swap(cbs, cbs_);
155 pauseWrites_ = false;
156 for (auto cb : cbs) {
157 handleWrite(cb);
158 }
159 }
160
onWriteChain(folly::AsyncTransport::WriteCallback * callback,std::shared_ptr<IOBuf> iob,WriteFlags)161 virtual void onWriteChain(folly::AsyncTransport::WriteCallback* callback,
162 std::shared_ptr<IOBuf> iob,
163 WriteFlags) {
164 if (pauseWrites_) {
165 cbs_.push_back(callback);
166 return; // let write requests timeout
167 }
168 auto mybuf = iob->clone();
169 mybuf->unshare();
170 writes_.append(std::move(mybuf));
171 handleWrite(callback);
172 }
173
handleWrite(folly::AsyncTransport::WriteCallback * callback)174 void handleWrite(folly::AsyncTransport::WriteCallback* callback) {
175 if (failWrites_) {
176 AsyncSocketException ex(AsyncSocketException::UNKNOWN, "");
177 callback->writeErr(0, ex);
178 } else {
179 if (writeInLoop_) {
180 eventBase_.runInLoop([&] { callback->writeSuccess(); });
181 } else {
182 callback->writeSuccess();
183 }
184 }
185 }
186
SetUp()187 void SetUp() override {
188 commonSetUp(makeClientCodec<typename C::Codec>(C::version));
189 }
190
commonSetUp(unique_ptr<HTTPCodec> codec)191 void commonSetUp(unique_ptr<HTTPCodec> codec) {
192 HTTPSession::setDefaultReadBufferLimit(65536);
193 HTTPSession::setDefaultWriteBufferLimit(65536);
194 HTTPTransaction::setEgressBufferLimit(65536);
195 EXPECT_CALL(*transport_, writeChain(_, _, _))
196 .WillRepeatedly(Invoke(this, &HTTPUpstreamTest<C>::onWriteChain));
197 EXPECT_CALL(*transport_, setReadCB(_))
198 .WillRepeatedly(SaveArg<0>(&readCallback_));
199 EXPECT_CALL(*transport_, getReadCB()).WillRepeatedly(Return(readCallback_));
200 EXPECT_CALL(*transport_, getEventBase())
201 .WillRepeatedly(ReturnPointee(&eventBasePtr_));
202 EXPECT_CALL(*transport_, good())
203 .WillRepeatedly(ReturnPointee(&transportGood_));
204 EXPECT_CALL(*transport_, closeNow())
205 .WillRepeatedly(Assign(&transportGood_, false));
206 EXPECT_CALL(*transport_, shutdownWriteNow())
207 .WillRepeatedly(Assign(&transportGood_, false));
208 EXPECT_CALL(*transport_, isReplaySafe()).WillOnce(Return(false));
209 EXPECT_CALL(*transport_, setReplaySafetyCallback(_))
210 .WillRepeatedly(SaveArg<0>(&replaySafetyCallback_));
211 EXPECT_CALL(*transport_, attachEventBase(_))
212 .WillRepeatedly(SaveArg<0>(&eventBasePtr_));
213
214 for (auto& param : flowControl_) {
215 if (param < 0) {
216 param = codec->getDefaultWindowSize();
217 }
218 }
219 httpSession_ = new HTTPUpstreamSession(
220 transactionTimeouts_.get(),
221 std::move(AsyncTransport::UniquePtr(transport_)),
222 localAddr_,
223 peerAddr_,
224 std::move(codec),
225 mockTransportInfo_,
226 this);
227 httpSession_->setFlowControl(
228 flowControl_[0], flowControl_[1], flowControl_[2]);
229 httpSession_->setMaxConcurrentOutgoingStreams(10);
230 httpSession_->setEgressSettings({{SettingsId::ENABLE_EX_HEADERS, 1}});
231 httpSession_->startNow();
232 eventBase_.loop();
233 ASSERT_EQ(this->sessionDestroyed_, false);
234 }
235
makeServerCodec()236 unique_ptr<typename C::Codec> makeServerCodec() {
237 return ::makeServerCodec<typename C::Codec>(C::version);
238 }
239
enableExHeader(typename C::Codec * serverCodec)240 void enableExHeader(typename C::Codec* serverCodec) {
241 if (!serverCodec || serverCodec->getProtocol() != CodecProtocol::HTTP_2) {
242 return;
243 }
244
245 auto clientCodec = makeClientCodec<HTTP2Codec>(2);
246 folly::IOBufQueue c2s{IOBufQueue::cacheChainLength()};
247 clientCodec->getEgressSettings()->setSetting(SettingsId::ENABLE_EX_HEADERS,
248 1);
249 clientCodec->generateConnectionPreface(c2s);
250 clientCodec->generateSettings(c2s);
251
252 // set ENABLE_EX_HEADERS to 1 in egressSettings
253 serverCodec->getEgressSettings()->setSetting(SettingsId::ENABLE_EX_HEADERS,
254 1);
255 // set ENABLE_EX_HEADERS to 1 in ingressSettings
256 auto setup = c2s.move();
257 serverCodec->onIngress(*setup);
258 }
259
parseOutput(HTTPCodec & serverCodec)260 void parseOutput(HTTPCodec& serverCodec) {
261 uint32_t consumed = std::numeric_limits<uint32_t>::max();
262 while (!writes_.empty() && consumed > 0) {
263 consumed = serverCodec.onIngress(*writes_.front());
264 writes_.split(consumed);
265 }
266 EXPECT_TRUE(writes_.empty());
267 }
268
readAndLoop(const std::string & input)269 void readAndLoop(const std::string& input) {
270 readAndLoop((const uint8_t*)input.data(), input.length());
271 }
272
readAndLoop(IOBuf * buf)273 void readAndLoop(IOBuf* buf) {
274 buf->coalesce();
275 readAndLoop(buf->data(), buf->length());
276 }
277
readAndLoop(const uint8_t * input,size_t length)278 void readAndLoop(const uint8_t* input, size_t length) {
279 CHECK_NOTNULL(readCallback_);
280 void* buf;
281 size_t bufSize;
282 while (length > 0) {
283 readCallback_->getReadBuffer(&buf, &bufSize);
284 // This is somewhat specific to our implementation, but currently we
285 // always return at least some space from getReadBuffer
286 CHECK_GT(bufSize, 0);
287 bufSize = std::min(bufSize, length);
288 memcpy(buf, input, bufSize);
289 readCallback_->readDataAvailable(bufSize);
290 eventBasePtr_->loop();
291 length -= bufSize;
292 input += bufSize;
293 }
294 }
295
296 void testBasicRequest();
297 void testBasicRequestHttp10(bool keepalive);
298
299 // HTTPSession::InfoCallback interface
onCreate(const HTTPSessionBase &)300 void onCreate(const HTTPSessionBase&) override {
301 sessionCreated_ = true;
302 }
onDestroy(const HTTPSessionBase &)303 void onDestroy(const HTTPSessionBase&) override {
304 sessionDestroyed_ = true;
305 }
onSettingsOutgoingStreamsFull(const HTTPSessionBase &)306 void onSettingsOutgoingStreamsFull(const HTTPSessionBase&) override {
307 transactionsFull_ = true;
308 }
onSettingsOutgoingStreamsNotFull(const HTTPSessionBase &)309 void onSettingsOutgoingStreamsNotFull(const HTTPSessionBase&) override {
310 transactionsFull_ = false;
311 }
312
TearDown()313 void TearDown() override {
314 AsyncSocketException ex(AsyncSocketException::UNKNOWN, "");
315 for (auto& cb : cbs_) {
316 cb->writeErr(0, ex);
317 }
318 }
319
openTransaction(bool expectStartPaused=false)320 std::unique_ptr<StrictMock<MockHTTPHandler>> openTransaction(
321 bool expectStartPaused = false) {
322 // Returns a mock handler with txn_ field set in it
323 auto handler = std::make_unique<StrictMock<MockHTTPHandler>>();
324 handler->expectTransaction();
325 if (expectStartPaused) {
326 handler->expectEgressPaused();
327 }
328 auto txn = httpSession_->newTransaction(handler.get());
329 EXPECT_EQ(txn, handler->txn_);
330 return handler;
331 }
332
openNiceTransaction(bool expectStartPaused=false)333 std::unique_ptr<NiceMock<MockHTTPHandler>> openNiceTransaction(
334 bool expectStartPaused = false) {
335 // Returns a mock handler with txn_ field set in it
336 auto handler = std::make_unique<NiceMock<MockHTTPHandler>>();
337 handler->expectTransaction();
338 if (expectStartPaused) {
339 handler->expectEgressPaused();
340 }
341 auto txn = httpSession_->newTransaction(handler.get());
342 EXPECT_EQ(txn, handler->txn_);
343 return handler;
344 }
345
346 void testSimpleUpgrade(const std::string& upgradeReqHeader,
347 const std::string& upgradeRespHeader,
348 CodecProtocol respCodecVersion);
349
setMockByteEventTracker()350 MockByteEventTracker* setMockByteEventTracker() {
351 auto byteEventTracker = new MockByteEventTracker(nullptr);
352 httpSession_->setByteEventTracker(
353 std::unique_ptr<ByteEventTracker>(byteEventTracker));
354 EXPECT_CALL(*byteEventTracker, preSend(_, _, _, _))
355 .WillRepeatedly(Return(0));
356 EXPECT_CALL(*byteEventTracker, drainByteEvents()).WillRepeatedly(Return(0));
357 EXPECT_CALL(*byteEventTracker, processByteEvents(_, _))
358 .WillRepeatedly(Invoke([](std::shared_ptr<ByteEventTracker> self,
359 uint64_t bytesWritten) {
360 return self->ByteEventTracker::processByteEvents(self, bytesWritten);
361 }));
362
363 return byteEventTracker;
364 }
365
366 protected:
367 bool sessionCreated_{false};
368 bool sessionDestroyed_{false};
369
370 bool transactionsFull_{false};
371 bool transportGood_{true};
372
373 EventBase eventBase_;
374 EventBase* eventBasePtr_{&eventBase_};
375 MockAsyncTransport* transport_; // invalid once httpSession_ is destroyed
376 folly::AsyncTransport::ReadCallback* readCallback_{nullptr};
377 folly::AsyncTransport::ReplaySafetyCallback* replaySafetyCallback_{nullptr};
378 folly::HHWheelTimer::UniquePtr transactionTimeouts_;
379 std::vector<int64_t> flowControl_;
380 wangle::TransportInfo mockTransportInfo_;
381 SocketAddress localAddr_{"127.0.0.1", 80};
382 SocketAddress peerAddr_{"127.0.0.1", 12345};
383 HTTPUpstreamSession* httpSession_{nullptr};
384 IOBufQueue writes_{IOBufQueue::cacheChainLength()};
385 std::vector<folly::AsyncTransport::WriteCallback*> cbs_;
386 bool failWrites_{false};
387 bool pauseWrites_{false};
388 bool writeInLoop_{false};
389 };
390 TYPED_TEST_CASE_P(HTTPUpstreamTest);
391
392 template <class C>
393 class TimeoutableHTTPUpstreamTest : public HTTPUpstreamTest<C> {
394 public:
TimeoutableHTTPUpstreamTest()395 TimeoutableHTTPUpstreamTest() : HTTPUpstreamTest<C>() {
396 // make it non-internal for this test class
397 HTTPUpstreamTest<C>::transactionTimeouts_ = folly::HHWheelTimer::newTimer(
398 &this->HTTPUpstreamTest<C>::eventBase_,
399 std::chrono::milliseconds(folly::HHWheelTimer::DEFAULT_TICK_INTERVAL),
400 folly::AsyncTimeout::InternalEnum::NORMAL,
401 std::chrono::milliseconds(500));
402 }
403 };
404
405 using HTTPUpstreamSessionTest = HTTPUpstreamTest<HTTP1xCodecPair>;
406 using SPDY3UpstreamSessionTest = HTTPUpstreamTest<SPDY3CodecPair>;
407 using HTTP2UpstreamSessionTest = HTTPUpstreamTest<HTTP2CodecPair>;
408
TEST_F(SPDY3UpstreamSessionTest,ServerPush)409 TEST_F(SPDY3UpstreamSessionTest, ServerPush) {
410 SPDYCodec egressCodec(TransportDirection::DOWNSTREAM, SPDYVersion::SPDY3);
411 folly::IOBufQueue output(folly::IOBufQueue::cacheChainLength());
412
413 HTTPMessage push;
414 push.getHeaders().set("HOST", "www.foo.com");
415 push.setURL("https://www.foo.com/");
416 egressCodec.generatePushPromise(output, 2, push, 1, false, nullptr);
417 auto buf = makeBuf(100);
418 egressCodec.generateBody(
419 output, 2, std::move(buf), HTTPCodec::NoPadding, true /* eom */);
420
421 HTTPMessage resp;
422 resp.setStatusCode(200);
423 resp.setStatusMessage("Ohai");
424 egressCodec.generateHeader(output, 1, resp, false, nullptr);
425 buf = makeBuf(100);
426 egressCodec.generateBody(
427 output, 1, std::move(buf), HTTPCodec::NoPadding, true /* eom */);
428
429 std::unique_ptr<folly::IOBuf> input = output.move();
430 input->coalesce();
431
432 MockHTTPHandler pushHandler;
433
434 InSequence enforceOrder;
435
436 auto handler = openTransaction();
437 EXPECT_CALL(*handler, onPushedTransaction(_))
438 .WillOnce(Invoke([&pushHandler](HTTPTransaction* pushTxn) {
439 pushTxn->setHandler(&pushHandler);
440 }));
441 EXPECT_CALL(pushHandler, setTransaction(_));
442 EXPECT_CALL(pushHandler, onHeadersComplete(_))
443 .WillOnce(Invoke([&](std::shared_ptr<HTTPMessage> msg) {
444 EXPECT_EQ(httpSession_->getNumIncomingStreams(), 1);
445 EXPECT_TRUE(msg->getIsChunked());
446 EXPECT_FALSE(msg->getIsUpgraded());
447 EXPECT_EQ(msg->getPathAsStringPiece(), "/");
448 EXPECT_EQ(msg->getHeaders().getSingleOrEmpty(HTTP_HEADER_HOST),
449 "www.foo.com");
450 }));
451 EXPECT_CALL(pushHandler, onBodyWithOffset(_, _));
452 EXPECT_CALL(pushHandler, onEOM());
453 EXPECT_CALL(pushHandler, detachTransaction());
454
455 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
456 EXPECT_FALSE(msg->getIsUpgraded());
457 EXPECT_EQ(200, msg->getStatusCode());
458 });
459 EXPECT_CALL(*handler, onBodyWithOffset(_, _));
460 handler->expectEOM();
461 handler->expectDetachTransaction();
462
463 handler->sendRequest();
464 readAndLoop(input->data(), input->length());
465
466 EXPECT_EQ(httpSession_->getNumIncomingStreams(), 0);
467 httpSession_->destroy();
468 }
469
TEST_F(SPDY3UpstreamSessionTest,IngressGoawayAbortUncreatedStreams)470 TEST_F(SPDY3UpstreamSessionTest, IngressGoawayAbortUncreatedStreams) {
471 // Tests whether the session aborts the streams which are not created
472 // at the remote end.
473
474 // Create SPDY buf for GOAWAY with last good stream as 0 (no streams created)
475 SPDYCodec egressCodec(TransportDirection::DOWNSTREAM, SPDYVersion::SPDY3);
476 folly::IOBufQueue respBuf{IOBufQueue::cacheChainLength()};
477 egressCodec.generateGoaway(respBuf, 0, ErrorCode::NO_ERROR);
478 std::unique_ptr<folly::IOBuf> goawayFrame = respBuf.move();
479 goawayFrame->coalesce();
480
481 InSequence enforceOrder;
482
483 auto handler = openTransaction();
484 handler->expectGoaway();
485 handler->expectError([&](const HTTPException& err) {
486 EXPECT_TRUE(err.hasProxygenError());
487 EXPECT_EQ(err.getProxygenError(), kErrorStreamUnacknowledged);
488 ASSERT_EQ(folly::to<std::string>("StreamUnacknowledged on transaction id: ",
489 handler->txn_->getID()),
490 std::string(err.what()));
491 });
492 handler->expectDetachTransaction([this] {
493 // Make sure the session can't create any more transactions.
494 MockHTTPHandler handler2;
495 EXPECT_EQ(httpSession_->newTransaction(&handler2), nullptr);
496 });
497
498 // Send the GET request
499 handler->sendRequest();
500
501 // Receive GOAWAY frame while waiting for SYN_REPLY
502 readAndLoop(goawayFrame->data(), goawayFrame->length());
503
504 // Session will delete itself after the abort
505 }
506
TEST_F(SPDY3UpstreamSessionTest,IngressGoawaySessionError)507 TEST_F(SPDY3UpstreamSessionTest, IngressGoawaySessionError) {
508 // Tests whether the session aborts non-refused stream
509
510 // Create SPDY buf for GOAWAY with last good stream as 1
511 SPDYCodec egressCodec(TransportDirection::DOWNSTREAM, SPDYVersion::SPDY3);
512 folly::IOBufQueue respBuf{IOBufQueue::cacheChainLength()};
513 egressCodec.generateGoaway(respBuf, 1, ErrorCode::PROTOCOL_ERROR);
514 std::unique_ptr<folly::IOBuf> goawayFrame = respBuf.move();
515 goawayFrame->coalesce();
516
517 auto handler1 = openTransaction();
518 auto handler2 = openTransaction();
519 handler2->expectGoaway();
520 handler1->expectGoaway();
521 // handler2 was refused
522 handler2->expectError([&](const HTTPException& err) {
523 EXPECT_TRUE(err.hasProxygenError());
524 EXPECT_EQ(err.getProxygenError(), kErrorStreamUnacknowledged);
525 ASSERT_EQ(folly::to<std::string>("StreamUnacknowledged on transaction id: ",
526 handler2->txn_->getID()),
527 std::string(err.what()));
528 });
529 // handler1 wasn't refused - receives an error
530 handler1->expectError([&](const HTTPException& err) {
531 EXPECT_TRUE(err.hasProxygenError());
532 EXPECT_EQ(err.getProxygenError(), kErrorConnectionReset);
533 ASSERT_EQ(folly::to<std::string>(
534 "ConnectionReset on transaction id: ",
535 handler1->txn_->getID(),
536 ". GOAWAY error with codec error: PROTOCOL_ERROR "
537 "with debug info: "),
538 std::string(err.what()));
539 });
540 handler1->expectDetachTransaction([this] {
541 // Make sure the session can't create any more transactions.
542 MockHTTPHandler handler3;
543 EXPECT_EQ(httpSession_->newTransaction(&handler3), nullptr);
544 });
545 handler2->expectDetachTransaction();
546
547 // Send the GET request
548 handler1->sendRequest();
549 handler2->sendRequest();
550
551 // Receive GOAWAY frame while waiting for SYN_REPLY
552 readAndLoop(goawayFrame->data(), goawayFrame->length());
553
554 // Session will delete itself after the abort
555 }
556
TEST_F(SPDY3UpstreamSessionTest,TestUnderLimitOnWriteError)557 TEST_F(SPDY3UpstreamSessionTest, TestUnderLimitOnWriteError) {
558 InSequence enforceOrder;
559 auto handler = openTransaction();
560
561 auto req = getPostRequest();
562 handler->txn_->sendHeaders(req);
563 // pause writes
564 pauseWrites_ = true;
565 handler->expectEgressPaused();
566
567 // send body
568 handler->txn_->sendBody(makeBuf(70000));
569 eventBase_.loopOnce();
570
571 // but no expectEgressResumed
572 handler->expectError();
573 handler->expectDetachTransaction();
574 failWrites_ = true;
575 resumeWrites();
576
577 this->eventBase_.loop();
578 EXPECT_EQ(this->sessionDestroyed_, true);
579 }
580
TEST_F(SPDY3UpstreamSessionTest,TestOverlimitResume)581 TEST_F(SPDY3UpstreamSessionTest, TestOverlimitResume) {
582 HTTPTransaction::setEgressBufferLimit(500);
583 auto handler1 = openTransaction();
584 auto handler2 = openTransaction();
585
586 auto req = getPostRequest();
587 handler1->txn_->sendHeaders(req);
588 handler2->txn_->sendHeaders(req);
589 // pause writes
590 pauseWrites_ = true;
591 eventBase_.loopOnce();
592
593 handler1->expectEgressPaused();
594 handler2->expectEgressPaused();
595
596 // send body
597 handler1->txn_->sendBody(makeBuf(1000));
598 handler2->txn_->sendBody(makeBuf(1000));
599
600 // when this handler is resumed, re-pause the pipe
601 handler1->expectEgressResumed([&] {
602 handler1->txn_->sendBody(makeBuf(1000));
603 pauseWrites_ = true;
604 });
605 // handler2 will get a shot
606 handler2->expectEgressResumed(
607 [&] { handler2->txn_->sendBody(makeBuf(1000)); });
608
609 // But the handlers pause again
610 handler1->expectEgressPaused();
611 handler2->expectEgressPaused();
612 resumeWrites();
613
614 // They both get resumed
615 handler1->expectEgressResumed([&] { handler1->txn_->sendEOM(); });
616 handler2->expectEgressResumed([&] { handler2->txn_->sendEOM(); });
617
618 resumeWrites();
619
620 this->eventBase_.loop();
621
622 // less than graceful shutdown
623 handler1->expectError();
624 handler1->expectDetachTransaction();
625 handler2->expectError();
626 handler2->expectDetachTransaction();
627
628 httpSession_->dropConnection();
629 EXPECT_EQ(this->sessionDestroyed_, true);
630 }
631
TEST_F(HTTP2UpstreamSessionTest,TestPriority)632 TEST_F(HTTP2UpstreamSessionTest, TestPriority) {
633 // virtual priority node with pri=8
634 auto priGroupID = httpSession_->sendPriority({0, false, 7});
635 auto handler1 = openTransaction();
636 auto handler2 = openTransaction();
637
638 auto req = getGetRequest();
639 // send request with maximal weight
640 req.setHTTP2Priority(HTTPMessage::HTTP2Priority(0, false, 255));
641 handler1->sendRequest(req);
642 handler2->sendRequest(req);
643
644 auto id = handler1->txn_->getID();
645 auto id2 = handler2->txn_->getID();
646
647 // Insert depth is 1, only root node has depth 0
648 EXPECT_EQ(std::get<0>(handler1->txn_->getPrioritySummary()), 1);
649 EXPECT_EQ(handler1->txn_->getPriorityFallback(), false);
650
651 // update handler to be in the pri-group
652 handler1->txn_->updateAndSendPriority(
653 http2::PriorityUpdate{priGroupID, false, 15});
654 handler2->txn_->updateAndSendPriority(
655 http2::PriorityUpdate{priGroupID + 254, false, 15});
656
657 // Change pri-group weight to max
658 httpSession_->sendPriority(priGroupID, http2::PriorityUpdate{0, false, 255});
659 eventBase_.loop();
660
661 auto serverCodec = makeServerCodec();
662 NiceMock<MockHTTPCodecCallback> callbacks;
663 serverCodec->setCallback(&callbacks);
664 EXPECT_CALL(callbacks,
665 onPriority(priGroupID, HTTPMessage::HTTP2Priority(0, false, 7)));
666 EXPECT_CALL(callbacks, onHeadersComplete(id, _))
667 .WillOnce(
668 Invoke([&](HTTPCodec::StreamID, std::shared_ptr<HTTPMessage> msg) {
669 EXPECT_EQ(*(msg->getHTTP2Priority()),
670 HTTPMessage::HTTP2Priority(0, false, 255));
671 }));
672 EXPECT_CALL(callbacks, onHeadersComplete(id2, _))
673 .WillOnce(
674 Invoke([&](HTTPCodec::StreamID, std::shared_ptr<HTTPMessage> msg) {
675 EXPECT_EQ(*(msg->getHTTP2Priority()),
676 HTTPMessage::HTTP2Priority(0, false, 255));
677 }));
678 EXPECT_CALL(
679 callbacks,
680 onPriority(id, HTTPMessage::HTTP2Priority(priGroupID, false, 15)));
681 EXPECT_CALL(
682 callbacks,
683 onPriority(id2, HTTPMessage::HTTP2Priority(priGroupID + 254, false, 15)));
684 EXPECT_EQ(handler1->txn_->getPriorityFallback(), false);
685 EXPECT_EQ(handler2->txn_->getPriorityFallback(), false);
686
687 EXPECT_EQ(std::get<1>(handler1->txn_->getPrioritySummary()), 2);
688 // created virtual parent node
689 EXPECT_EQ(std::get<1>(handler2->txn_->getPrioritySummary()), 2);
690 EXPECT_CALL(
691 callbacks,
692 onPriority(priGroupID, HTTPMessage::HTTP2Priority(0, false, 255)));
693 parseOutput(*serverCodec);
694 eventBase_.loop();
695
696 handler1->expectError();
697 handler1->expectDetachTransaction();
698 handler2->expectError();
699 handler2->expectDetachTransaction();
700 httpSession_->dropConnection();
701 eventBase_.loop();
702 EXPECT_EQ(sessionDestroyed_, true);
703 }
704
TEST_F(HTTP2UpstreamSessionTest,TestCircularPriority)705 TEST_F(HTTP2UpstreamSessionTest, TestCircularPriority) {
706 InSequence enforceOrder;
707 auto handler1 = openTransaction();
708
709 auto req = getGetRequest();
710 // send request with circular dep
711 req.setHTTP2Priority(HTTPMessage::HTTP2Priority(1, false, 255));
712 handler1->sendRequest(req);
713 handler1->terminate();
714 handler1->expectDetachTransaction();
715 httpSession_->dropConnection();
716 eventBase_.loop();
717 EXPECT_EQ(sessionDestroyed_, true);
718 }
719
TEST_F(HTTP2UpstreamSessionTest,TestSettingsAck)720 TEST_F(HTTP2UpstreamSessionTest, TestSettingsAck) {
721 auto serverCodec = makeServerCodec();
722 folly::IOBufQueue buf{IOBufQueue::cacheChainLength()};
723 serverCodec->generateSettings(buf);
724 auto settingsFrame = buf.move();
725 settingsFrame->coalesce();
726
727 InSequence enforceOrder;
728
729 NiceMock<MockHTTPCodecCallback> callbacks;
730 serverCodec->setCallback(&callbacks);
731 EXPECT_CALL(callbacks, onSettings(_));
732 EXPECT_CALL(callbacks, onSettingsAck());
733
734 readAndLoop(settingsFrame.get());
735 parseOutput(*serverCodec);
736 httpSession_->dropConnection();
737 EXPECT_EQ(sessionDestroyed_, true);
738 }
739
TEST_F(HTTP2UpstreamSessionTest,TestSettingsInfoCallbacks)740 TEST_F(HTTP2UpstreamSessionTest, TestSettingsInfoCallbacks) {
741 auto serverCodec = makeServerCodec();
742
743 folly::IOBufQueue settingsBuf{IOBufQueue::cacheChainLength()};
744 serverCodec->generateSettings(settingsBuf);
745 auto settingsFrame = settingsBuf.move();
746
747 folly::IOBufQueue settingsAckBuf{IOBufQueue::cacheChainLength()};
748 serverCodec->generateSettingsAck(settingsAckBuf);
749 auto settingsAckFrame = settingsAckBuf.move();
750
751 MockHTTPSessionInfoCallback infoCb;
752 httpSession_->setInfoCallback(&infoCb);
753
754 EXPECT_CALL(infoCb, onRead(_, _, _)).Times(2);
755 EXPECT_CALL(infoCb, onWrite(_, _)).Times(1);
756 EXPECT_CALL(infoCb, onDestroy(_)).Times(1);
757
758 EXPECT_CALL(infoCb, onSettings(_, _)).Times(1);
759 EXPECT_CALL(infoCb, onSettingsAck(_)).Times(1);
760
761 InSequence enforceOrder;
762
763 readAndLoop(settingsFrame.get());
764 readAndLoop(settingsAckFrame.get());
765
766 httpSession_->destroy();
767 }
768
TEST_F(HTTP2UpstreamSessionTest,TestSetControllerInitHeaderIndexingStrat)769 TEST_F(HTTP2UpstreamSessionTest, TestSetControllerInitHeaderIndexingStrat) {
770 StrictMock<MockUpstreamController> mockController;
771 HeaderIndexingStrategy testH2IndexingStrat;
772 EXPECT_CALL(mockController, getHeaderIndexingStrategy())
773 .WillOnce(Return(&testH2IndexingStrat));
774
775 httpSession_->setController(&mockController);
776
777 auto handler = openTransaction();
778 handler->expectDetachTransaction();
779
780 const HTTP2Codec* h2Codec =
781 static_cast<const HTTP2Codec*>(&handler->txn_->getTransport().getCodec());
782 EXPECT_EQ(h2Codec->getHeaderIndexingStrategy(), &testH2IndexingStrat);
783
784 handler->txn_->sendAbort();
785 eventBase_.loop();
786
787 EXPECT_CALL(mockController, detachSession(_));
788 httpSession_->destroy();
789 }
790
791 /*
792 * The sequence of streams are generated in the following order:
793 * - [client --> server] setup the control stream (getGetRequest())
794 * - [server --> client] respond to 1st stream (OK, without EOM)
795 * - [server --> client] request 2nd stream (pub, EOM)
796 * - [client --> server] abort the 2nd stream
797 * - [server --> client] respond to 1st stream (EOM)
798 */
799
TEST_F(HTTP2UpstreamSessionTest,ExheaderFromServer)800 TEST_F(HTTP2UpstreamSessionTest, ExheaderFromServer) {
801 folly::IOBufQueue queue{IOBufQueue::cacheChainLength()};
802
803 // generate enable_ex_headers setting
804 auto serverCodec = makeServerCodec();
805 enableExHeader(serverCodec.get());
806 serverCodec->generateSettings(queue);
807 // generate the response for control stream, but EOM
808 auto cStreamId = HTTPCodec::StreamID(1);
809 serverCodec->generateHeader(
810 queue, cStreamId, getResponse(200, 0), false, nullptr);
811 // generate a request from server, encapsulated in EX_HEADERS frame
812 serverCodec->generateExHeader(queue,
813 2,
814 getGetRequest("/messaging"),
815 HTTPCodec::ExAttributes(cStreamId, false),
816 true,
817 nullptr);
818 serverCodec->generateEOM(queue, 1);
819
820 auto cHandler = openTransaction();
821 cHandler->sendRequest(getGetRequest("/cc"));
822
823 NiceMock<MockHTTPHandler> pubHandler;
824 InSequence handlerSequence;
825 cHandler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
826 EXPECT_EQ(200, msg->getStatusCode());
827 });
828
829 EXPECT_CALL(*cHandler, onExTransaction(_))
830 .WillOnce(Invoke([&pubHandler](HTTPTransaction* pubTxn) {
831 pubTxn->setHandler(&pubHandler);
832 pubHandler.txn_ = pubTxn;
833 }));
834 pubHandler.expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
835 EXPECT_EQ(msg->getPathAsStringPiece(), "/messaging");
836 });
837 pubHandler.expectEOM([&]() { pubHandler.txn_->sendAbort(); });
838 pubHandler.expectDetachTransaction();
839
840 cHandler->expectEOM();
841 cHandler->expectDetachTransaction();
842
843 auto buf = queue.move();
844 buf->coalesce();
845 readAndLoop(buf.get());
846
847 httpSession_->destroy();
848 }
849
TEST_F(HTTP2UpstreamSessionTest,InvalidControlStream)850 TEST_F(HTTP2UpstreamSessionTest, InvalidControlStream) {
851 folly::IOBufQueue queue{IOBufQueue::cacheChainLength()};
852
853 // generate enable_ex_headers setting
854 auto serverCodec = makeServerCodec();
855 enableExHeader(serverCodec.get());
856 serverCodec->generateSettings(queue);
857 // generate the response for control stream, but EOM
858 auto cStreamId = HTTPCodec::StreamID(1);
859 serverCodec->generateHeader(
860 queue, cStreamId, getResponse(200, 0), false, nullptr);
861 // generate a EX_HEADERS frame with non-existing control stream
862 serverCodec->generateExHeader(queue,
863 2,
864 getGetRequest("/messaging"),
865 HTTPCodec::ExAttributes(cStreamId + 2, false),
866 true,
867 nullptr);
868 serverCodec->generateEOM(queue, 1);
869
870 auto cHandler = openTransaction();
871 cHandler->sendRequest(getGetRequest("/cc"));
872
873 InSequence handlerSequence;
874 cHandler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
875 EXPECT_EQ(200, msg->getStatusCode());
876 });
877 EXPECT_CALL(*cHandler, onExTransaction(_)).Times(0);
878 cHandler->expectEOM();
879 cHandler->expectDetachTransaction();
880
881 auto buf = queue.move();
882 buf->coalesce();
883 readAndLoop(buf.get());
884
885 httpSession_->destroy();
886 }
887
888 class HTTP2UpstreamSessionWithVirtualNodesTest
889 : public HTTPUpstreamTest<MockHTTPCodecPair> {
890 public:
SetUp()891 void SetUp() override {
892 auto codec = std::make_unique<NiceMock<MockHTTPCodec>>();
893 codecPtr_ = codec.get();
894 EXPECT_CALL(*codec, supportsParallelRequests())
895 .WillRepeatedly(Return(true));
896 EXPECT_CALL(*codec, getTransportDirection())
897 .WillRepeatedly(Return(TransportDirection::UPSTREAM));
898 EXPECT_CALL(*codec, getProtocol())
899 .WillRepeatedly(Return(CodecProtocol::HTTP_2));
900 EXPECT_CALL(*codec, setCallback(_)).WillRepeatedly(SaveArg<0>(&codecCb_));
901 EXPECT_CALL(*codec, createStream()).WillRepeatedly(Invoke([&] {
902 auto ret = nextOutgoingTxn_;
903 nextOutgoingTxn_ += 2;
904 return ret;
905 }));
906 commonSetUp(std::move(codec));
907 }
908
commonSetUp(unique_ptr<HTTPCodec> codec)909 void commonSetUp(unique_ptr<HTTPCodec> codec) {
910 HTTPSession::setDefaultReadBufferLimit(65536);
911 HTTPSession::setDefaultWriteBufferLimit(65536);
912 EXPECT_CALL(*transport_, writeChain(_, _, _))
913 .WillRepeatedly(
914 Invoke(this, &HTTPUpstreamTest<MockHTTPCodecPair>::onWriteChain));
915 EXPECT_CALL(*transport_, setReadCB(_))
916 .WillRepeatedly(SaveArg<0>(&readCallback_));
917 EXPECT_CALL(*transport_, getReadCB()).WillRepeatedly(Return(readCallback_));
918 EXPECT_CALL(*transport_, getEventBase())
919 .WillRepeatedly(Return(&eventBase_));
920 EXPECT_CALL(*transport_, good())
921 .WillRepeatedly(ReturnPointee(&transportGood_));
922 EXPECT_CALL(*transport_, closeNow())
923 .WillRepeatedly(Assign(&transportGood_, false));
924 AsyncTransport::UniquePtr transportPtr(transport_);
925 httpSession_ = new HTTPUpstreamSession(transactionTimeouts_.get(),
926 std::move(transportPtr),
927 localAddr_,
928 peerAddr_,
929 std::move(codec),
930 mockTransportInfo_,
931 this,
932 level_,
933 builder_);
934 eventBase_.loop();
935 ASSERT_EQ(this->sessionDestroyed_, false);
936 }
937
TearDown()938 void TearDown() override {
939 EXPECT_TRUE(sessionDestroyed_);
940 }
941
942 protected:
943 MockHTTPCodec* codecPtr_{nullptr};
944 HTTPCodec::Callback* codecCb_{nullptr};
945 uint32_t nextOutgoingTxn_{1};
946 std::vector<HTTPCodec::StreamID> dependencies;
947 uint8_t level_{3};
948 std::shared_ptr<TestPriorityMapBuilder> builder_;
949 };
950
TEST_F(HTTP2UpstreamSessionWithVirtualNodesTest,VirtualNodes)951 TEST_F(HTTP2UpstreamSessionWithVirtualNodesTest, VirtualNodes) {
952 InSequence enforceOrder;
953
954 HTTPCodec::StreamID deps[] = {11, 13, 15};
955 EXPECT_CALL(*codecPtr_, addPriorityNodes(_, _, _))
956 .Times(1)
957 .WillOnce(Invoke(
958 [&](HTTPCodec::PriorityQueue&, folly::IOBufQueue&, uint8_t maxLevel) {
959 for (size_t i = 0; i < maxLevel; i++) {
960 dependencies.push_back(deps[i]);
961 }
962 return 123;
963 }));
964 httpSession_->startNow();
965
966 EXPECT_EQ(level_, dependencies.size());
967 StrictMock<MockHTTPHandler> handler;
968 handler.expectTransaction();
969 auto txn = httpSession_->newTransaction(&handler);
970
971 EXPECT_CALL(*codecPtr_, mapPriorityToDependency(_))
972 .Times(1)
973 .WillOnce(
974 Invoke([&](uint8_t priority) { return dependencies[priority]; }));
975 txn->updateAndSendPriority(0);
976
977 handler.expectError();
978 handler.expectDetachTransaction();
979 httpSession_->dropConnection();
980
981 eventBase_.loop();
982 }
983
984 class HTTP2UpstreamSessionWithPriorityTree
985 : public HTTP2UpstreamSessionWithVirtualNodesTest {
986 public:
HTTP2UpstreamSessionWithPriorityTree()987 HTTP2UpstreamSessionWithPriorityTree() {
988 builder_ = std::make_shared<TestPriorityMapBuilder>();
989 }
990 };
991
TEST_F(HTTP2UpstreamSessionWithPriorityTree,PriorityTree)992 TEST_F(HTTP2UpstreamSessionWithPriorityTree, PriorityTree) {
993 InSequence enforceOrder;
994
995 std::array<HTTPCodec::StreamID, 3> deps = {{11, 13, 15}};
996 EXPECT_CALL(*codecPtr_, addPriorityNodes(_, _, _))
997 .Times(0)
998 .WillOnce(Invoke(
999 [&](HTTPCodec::PriorityQueue&, folly::IOBufQueue&, uint8_t maxLevel) {
1000 for (size_t i = 0; i < maxLevel; i++) {
1001 dependencies.push_back(deps[i]);
1002 }
1003 return 123;
1004 }));
1005 httpSession_->startNow();
1006
1007 // It should have built the virtual streams from the tree but not the old
1008 // priority levels.
1009 EXPECT_EQ(dependencies.size(), 0);
1010 HTTPMessage::HTTP2Priority hiPri =
1011 *httpSession_->getHTTPPriority(builder_->hiPriLevel_);
1012 EXPECT_EQ(std::get<2>(hiPri), builder_->hiPriWeight_);
1013 HTTPMessage::HTTP2Priority loPri =
1014 *httpSession_->getHTTPPriority(builder_->loPriLevel_);
1015 EXPECT_EQ(std::get<2>(loPri), builder_->loPriWeight_);
1016
1017 for (size_t level = 0; level < 256; ++level) {
1018 if (level == builder_->hiPriLevel_) {
1019 continue;
1020 }
1021 HTTPMessage::HTTP2Priority pri = *httpSession_->getHTTPPriority(level);
1022 EXPECT_EQ(pri, loPri);
1023 }
1024
1025 StrictMock<MockHTTPHandler> handler;
1026 handler.expectTransaction();
1027 auto txn = httpSession_->newTransaction(&handler);
1028
1029 txn->updateAndSendPriority(0);
1030
1031 handler.expectError();
1032 handler.expectDetachTransaction();
1033 httpSession_->dropConnection();
1034
1035 eventBase_.loop();
1036 }
1037
TYPED_TEST_P(HTTPUpstreamTest,ImmediateEof)1038 TYPED_TEST_P(HTTPUpstreamTest, ImmediateEof) {
1039 // Receive an EOF without any request data
1040 this->readCallback_->readEOF();
1041 this->eventBase_.loop();
1042 EXPECT_EQ(this->sessionDestroyed_, true);
1043 }
1044
1045 template <class CodecPair>
testBasicRequest()1046 void HTTPUpstreamTest<CodecPair>::testBasicRequest() {
1047 InSequence enforceOrder;
1048
1049 auto handler = openTransaction();
1050 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1051 EXPECT_TRUE(msg->getIsChunked());
1052 EXPECT_FALSE(msg->getIsUpgraded());
1053 EXPECT_EQ(200, msg->getStatusCode());
1054 });
1055 handler->expectEOM();
1056 handler->expectDetachTransaction();
1057
1058 handler->sendRequest();
1059 readAndLoop(
1060 "HTTP/1.1 200 OK\r\n"
1061 "Transfer-Encoding: chunked\r\n\r\n"
1062 "0\r\n\r\n");
1063
1064 CHECK(httpSession_->supportsMoreTransactions());
1065 CHECK_EQ(httpSession_->getNumOutgoingStreams(), 0);
1066 }
1067
TEST_F(HTTPUpstreamSessionTest,BasicRequest)1068 TEST_F(HTTPUpstreamSessionTest, BasicRequest) {
1069 testBasicRequest();
1070 httpSession_->destroy();
1071 }
1072
TEST_F(HTTPUpstreamSessionTest,TwoRequests)1073 TEST_F(HTTPUpstreamSessionTest, TwoRequests) {
1074 testBasicRequest();
1075 testBasicRequest();
1076 httpSession_->destroy();
1077 }
1078
1079 TEST_F(HTTPUpstreamSessionTest, 10Requests) {
1080 for (uint16_t i = 0; i < 10; i++) {
1081 testBasicRequest();
1082 }
1083 httpSession_->destroy();
1084 }
1085
TEST_F(HTTPUpstreamSessionTest,TestFirstHeaderByteEventTracker)1086 TEST_F(HTTPUpstreamSessionTest, TestFirstHeaderByteEventTracker) {
1087 auto byteEventTracker = setMockByteEventTracker();
1088
1089 EXPECT_CALL(*byteEventTracker, addFirstHeaderByteEvent(_, _, _))
1090 .WillOnce(Invoke(
1091 [](uint64_t /*byteNo*/, HTTPTransaction* txn, ByteEvent::Callback) {
1092 txn->incrementPendingByteEvents();
1093 }));
1094
1095 InSequence enforceOrder;
1096
1097 auto handler = openTransaction();
1098 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1099 EXPECT_TRUE(msg->getIsChunked());
1100 EXPECT_FALSE(msg->getIsUpgraded());
1101 EXPECT_EQ(200, msg->getStatusCode());
1102 });
1103 handler->expectEOM();
1104 handler->expectDetachTransaction();
1105
1106 handler->sendRequest();
1107 readAndLoop(
1108 "HTTP/1.1 200 OK\r\n"
1109 "Transfer-Encoding: chunked\r\n\r\n"
1110 "0\r\n\r\n");
1111
1112 CHECK(httpSession_->supportsMoreTransactions());
1113 CHECK_EQ(httpSession_->getNumOutgoingStreams(), 0);
1114 handler->txn_->decrementPendingByteEvents();
1115 httpSession_->destroy();
1116 }
1117
1118 template <class CodecPair>
testBasicRequestHttp10(bool keepalive)1119 void HTTPUpstreamTest<CodecPair>::testBasicRequestHttp10(bool keepalive) {
1120 HTTPMessage req = getGetRequest();
1121 req.setHTTPVersion(1, 0);
1122 if (keepalive) {
1123 req.getHeaders().set(HTTP_HEADER_CONNECTION, "Keep-Alive");
1124 }
1125
1126 InSequence enforceOrder;
1127
1128 auto handler = openTransaction();
1129 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1130 EXPECT_EQ(200, msg->getStatusCode());
1131 EXPECT_EQ(keepalive ? "keep-alive" : "close",
1132 msg->getHeaders().getSingleOrEmpty(HTTP_HEADER_CONNECTION));
1133 });
1134 EXPECT_CALL(*handler, onBodyWithOffset(_, _));
1135 handler->expectEOM();
1136 handler->expectDetachTransaction();
1137
1138 handler->sendRequest();
1139 if (keepalive) {
1140 readAndLoop(
1141 "HTTP/1.0 200 OK\r\n"
1142 "Connection: keep-alive\r\n"
1143 "Content-length: 7\r\n\r\n"
1144 "content");
1145 } else {
1146 readAndLoop(
1147 "HTTP/1.0 200 OK\r\n"
1148 "Connection: close\r\n"
1149 "Content-length: 7\r\n\r\n"
1150 "content");
1151 }
1152 }
1153
TEST_F(HTTPUpstreamSessionTest,Http10Keepalive)1154 TEST_F(HTTPUpstreamSessionTest, Http10Keepalive) {
1155 testBasicRequestHttp10(true);
1156 testBasicRequestHttp10(false);
1157 }
1158
TEST_F(HTTPUpstreamSessionTest,BasicTrailers)1159 TEST_F(HTTPUpstreamSessionTest, BasicTrailers) {
1160 InSequence enforceOrder;
1161
1162 auto handler = openTransaction();
1163 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1164 EXPECT_TRUE(msg->getIsChunked());
1165 EXPECT_FALSE(msg->getIsUpgraded());
1166 EXPECT_EQ(200, msg->getStatusCode());
1167 });
1168 EXPECT_CALL(*handler, onTrailers(_));
1169 handler->expectEOM();
1170 handler->expectDetachTransaction();
1171
1172 handler->sendRequest();
1173 readAndLoop(
1174 "HTTP/1.1 200 OK\r\n"
1175 "Transfer-Encoding: chunked\r\n\r\n"
1176 "0\r\n"
1177 "X-Trailer1: foo\r\n"
1178 "\r\n");
1179
1180 CHECK(httpSession_->supportsMoreTransactions());
1181 CHECK_EQ(httpSession_->getNumOutgoingStreams(), 0);
1182 httpSession_->destroy();
1183 }
1184
TEST_F(HTTP2UpstreamSessionTest,BasicTrailers)1185 TEST_F(HTTP2UpstreamSessionTest, BasicTrailers) {
1186 auto egressCodec = makeServerCodec();
1187 folly::IOBufQueue output(folly::IOBufQueue::cacheChainLength());
1188
1189 egressCodec->generateSettings(output);
1190
1191 HTTPMessage resp;
1192 resp.setStatusCode(200);
1193 resp.getHeaders().set("header1", "value1");
1194 egressCodec->generateHeader(output, 1, resp);
1195 auto buf = makeBuf(100);
1196 egressCodec->generateBody(
1197 output, 1, std::move(buf), HTTPCodec::NoPadding, false /* eom */);
1198 HTTPHeaders trailers;
1199 trailers.set("trailer2", "value2");
1200 egressCodec->generateTrailers(output, 1, trailers);
1201
1202 std::unique_ptr<folly::IOBuf> input = output.move();
1203 input->coalesce();
1204
1205 auto handler = openTransaction();
1206
1207 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1208 EXPECT_EQ(200, msg->getStatusCode());
1209 EXPECT_EQ(msg->getHeaders().getSingleOrEmpty("header1"), "value1");
1210 });
1211 handler->expectBody();
1212 handler->expectTrailers([&](std::shared_ptr<HTTPHeaders> trailers) {
1213 EXPECT_EQ(trailers->getSingleOrEmpty("trailer2"), "value2");
1214 });
1215 handler->expectEOM();
1216 handler->expectDetachTransaction();
1217
1218 handler->sendRequest();
1219 readAndLoop(input->data(), input->length());
1220
1221 httpSession_->destroy();
1222 }
1223
TEST_F(HTTP2UpstreamSessionTest,HeadersThenBodyThenHeaders)1224 TEST_F(HTTP2UpstreamSessionTest, HeadersThenBodyThenHeaders) {
1225 auto egressCodec = makeServerCodec();
1226 folly::IOBufQueue output(folly::IOBufQueue::cacheChainLength());
1227
1228 egressCodec->generateSettings(output);
1229
1230 HTTPMessage resp;
1231 resp.setStatusCode(200);
1232 resp.getHeaders().set("header1", "value1");
1233 egressCodec->generateHeader(output, 1, resp);
1234 auto buf = makeBuf(100);
1235 egressCodec->generateBody(
1236 output, 1, std::move(buf), HTTPCodec::NoPadding, false /* eom */);
1237 // generate same headers again on the same stream
1238 egressCodec->generateHeader(output, 1, resp);
1239
1240 std::unique_ptr<folly::IOBuf> input = output.move();
1241 input->coalesce();
1242
1243 auto handler = openTransaction();
1244
1245 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1246 EXPECT_EQ(200, msg->getStatusCode());
1247 EXPECT_EQ(msg->getHeaders().getSingleOrEmpty("header1"), "value1");
1248 });
1249 handler->expectBody();
1250 handler->expectError([&](const HTTPException& err) {
1251 EXPECT_TRUE(err.hasProxygenError());
1252 EXPECT_EQ(err.getProxygenError(), kErrorIngressStateTransition);
1253 ASSERT_EQ(
1254 "Invalid ingress state transition, state=RegularBodyReceived, "
1255 "event=onHeaders, streamID=1",
1256 std::string(err.what()));
1257 });
1258 handler->expectDetachTransaction();
1259
1260 handler->sendRequest();
1261 readAndLoop(input->data(), input->length());
1262
1263 httpSession_->destroy();
1264 }
1265
TEST_F(HTTPUpstreamSessionTest,TwoRequestsWithPause)1266 TEST_F(HTTPUpstreamSessionTest, TwoRequestsWithPause) {
1267 InSequence enforceOrder;
1268
1269 auto handler = openTransaction();
1270 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1271 EXPECT_TRUE(msg->getIsChunked());
1272 EXPECT_FALSE(msg->getIsUpgraded());
1273 EXPECT_EQ(200, msg->getStatusCode());
1274 });
1275
1276 handler->expectEOM([&]() { handler->txn_->pauseIngress(); });
1277 handler->expectDetachTransaction();
1278
1279 handler->sendRequest();
1280 readAndLoop(
1281 "HTTP/1.1 200 OK\r\n"
1282 "Transfer-Encoding: chunked\r\n\r\n"
1283 "0\r\n\r\n");
1284
1285 // Even though the previous transaction paused ingress just before it
1286 // finished up, reads resume automatically when the number of
1287 // transactions goes to zero. This way, the second request can read
1288 // without having to call resumeIngress()
1289 testBasicRequest();
1290 httpSession_->destroy();
1291 }
1292
1293 using HTTPUpstreamTimeoutTest = TimeoutableHTTPUpstreamTest<HTTP1xCodecPair>;
TEST_F(HTTPUpstreamTimeoutTest,WriteTimeoutAfterResponse)1294 TEST_F(HTTPUpstreamTimeoutTest, WriteTimeoutAfterResponse) {
1295 // Test where the upstream session times out while writing the request
1296 // to the server, but after the server has already sent back a full
1297 // response.
1298 pauseWrites_ = true;
1299 HTTPMessage req = getPostRequest();
1300
1301 InSequence enforceOrder;
1302 auto handler = openTransaction();
1303 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1304 EXPECT_TRUE(msg->getIsChunked());
1305 EXPECT_FALSE(msg->getIsUpgraded());
1306 EXPECT_EQ(200, msg->getStatusCode());
1307 });
1308 handler->expectEOM();
1309 handler->expectError([&](const HTTPException& err) {
1310 EXPECT_TRUE(err.hasProxygenError());
1311 ASSERT_EQ(err.getDirection(), HTTPException::Direction::INGRESS_AND_EGRESS);
1312 EXPECT_EQ(err.getProxygenError(), kErrorWriteTimeout);
1313 ASSERT_EQ(folly::to<std::string>("WriteTimeout on transaction id: ",
1314 handler->txn_->getID()),
1315 std::string(err.what()));
1316 });
1317 handler->expectDetachTransaction();
1318
1319 handler->txn_->sendHeaders(req);
1320 // Don't send the body, but get a response immediately
1321 readAndLoop(
1322 "HTTP/1.1 200 OK\r\n"
1323 "Transfer-Encoding: chunked\r\n\r\n"
1324 "0\r\n\r\n");
1325 }
1326
TEST_F(HTTPUpstreamTimeoutTest,SetIngressTimeoutAfterSendEom)1327 TEST_F(HTTPUpstreamTimeoutTest, SetIngressTimeoutAfterSendEom) {
1328 InSequence enforceOrder;
1329 httpSession_->setIngressTimeoutAfterEom(true);
1330
1331 auto handler1 = openTransaction();
1332 handler1->expectHeaders();
1333 handler1->expectEOM();
1334 handler1->expectDetachTransaction();
1335
1336 // Send EOM separately.
1337 auto transaction1 = handler1->txn_;
1338 EXPECT_TRUE(transaction1->hasIdleTimeout());
1339 transaction1->setIdleTimeout(std::chrono::milliseconds(100));
1340 EXPECT_FALSE(transaction1->isScheduled());
1341
1342 transaction1->sendHeaders(getPostRequest());
1343 eventBase_.loopOnce();
1344 EXPECT_FALSE(transaction1->isScheduled());
1345
1346 transaction1->sendBody(makeBuf(100) /* body */);
1347 eventBase_.loopOnce();
1348 EXPECT_FALSE(transaction1->isScheduled());
1349
1350 transaction1->sendEOM();
1351 eventBase_.loopOnce();
1352 EXPECT_TRUE(transaction1->isScheduled());
1353 readAndLoop(
1354 "HTTP/1.1 200 OK\r\n"
1355 "Transfer-Encoding: chunked\r\n\r\n"
1356 "0\r\n\r\n");
1357
1358 // Send EOM with header.
1359 auto handler2 = openTransaction();
1360 handler2->expectHeaders();
1361 handler2->expectEOM();
1362 handler2->expectDetachTransaction();
1363
1364 auto transaction2 = handler2->txn_;
1365 EXPECT_FALSE(transaction2->isScheduled());
1366 transaction2->sendHeadersWithOptionalEOM(getPostRequest(), true /* eom */);
1367 eventBase_.loopOnce();
1368 EXPECT_TRUE(transaction2->isScheduled());
1369
1370 readAndLoop(
1371 "HTTP/1.1 200 OK\r\n"
1372 "Transfer-Encoding: chunked\r\n\r\n"
1373 "0\r\n\r\n");
1374
1375 // Send EOM with body.
1376 auto handler3 = openTransaction();
1377 handler3->expectHeaders();
1378 handler3->expectEOM();
1379 handler3->expectDetachTransaction();
1380
1381 auto transaction3 = handler3->txn_;
1382 EXPECT_FALSE(transaction3->isScheduled());
1383 transaction3->sendHeaders(getPostRequest());
1384 eventBase_.loopOnce();
1385 EXPECT_FALSE(transaction3->isScheduled());
1386 transaction3->sendBody(makeBuf(100) /* body */);
1387 transaction3->sendEOM();
1388 eventBase_.loopOnce();
1389 EXPECT_TRUE(transaction3->isScheduled());
1390
1391 readAndLoop(
1392 "HTTP/1.1 200 OK\r\n"
1393 "Transfer-Encoding: chunked\r\n\r\n"
1394 "0\r\n\r\n");
1395
1396 httpSession_->destroy();
1397 }
1398
TEST_F(HTTPUpstreamSessionTest,SetTransactionTimeout)1399 TEST_F(HTTPUpstreamSessionTest, SetTransactionTimeout) {
1400 // Test that setting a new timeout on the transaction will cancel
1401 // the old one.
1402 auto handler = openTransaction();
1403 handler->expectDetachTransaction();
1404
1405 EXPECT_TRUE(handler->txn_->hasIdleTimeout());
1406 handler->txn_->setIdleTimeout(std::chrono::milliseconds(747));
1407 EXPECT_TRUE(handler->txn_->hasIdleTimeout());
1408 EXPECT_TRUE(handler->txn_->isScheduled());
1409 EXPECT_EQ(transactionTimeouts_->count(), 1);
1410 handler->txn_->sendAbort();
1411 eventBase_.loop();
1412 }
1413
TEST_F(HTTPUpstreamSessionTest,ReadTimeout)1414 TEST_F(HTTPUpstreamSessionTest, ReadTimeout) {
1415 NiceMock<MockUpstreamController> controller;
1416 httpSession_->setController(&controller);
1417 auto cm = wangle::ConnectionManager::makeUnique(
1418 &eventBase_, std::chrono::milliseconds(50));
1419 cm->addConnection(httpSession_, true);
1420 eventBase_.loop();
1421 }
1422
1423 TEST_F(HTTPUpstreamSessionTest, 100ContinueKeepalive) {
1424 // Test a request with 100 continue on a keepalive connection. Then make
1425 // another request.
1426 HTTPMessage req = getGetRequest();
1427 req.getHeaders().set(HTTP_HEADER_EXPECT, "100-continue");
1428
1429 InSequence enforceOrder;
1430
1431 auto handler = openTransaction();
__anon611904442802(std::shared_ptr<HTTPMessage> msg) 1432 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1433 EXPECT_FALSE(msg->getIsChunked());
1434 EXPECT_FALSE(msg->getIsUpgraded());
1435 EXPECT_EQ(100, msg->getStatusCode());
1436 });
__anon611904442902(std::shared_ptr<HTTPMessage> msg) 1437 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1438 EXPECT_TRUE(msg->getIsChunked());
1439 EXPECT_FALSE(msg->getIsUpgraded());
1440 EXPECT_EQ(200, msg->getStatusCode());
1441 });
1442 handler->expectEOM();
1443 handler->expectDetachTransaction();
1444
1445 handler->sendRequest(req);
1446 readAndLoop(
1447 "HTTP/1.1 100 Continue\r\n\r\n"
1448 "HTTP/1.1 200 OK\r\n"
1449 "Transfer-Encoding: chunked\r\n\r\n"
1450 "0\r\n\r\n");
1451
1452 // Now make sure everything still works
1453 testBasicRequest();
1454 httpSession_->destroy();
1455 }
1456
1457 TEST_F(HTTPUpstreamSessionTest, 417Keepalive) {
1458 // Test a request with 100 continue on a keepalive connection. Then make
1459 // another request after the expectation fails.
1460 HTTPMessage req = getGetRequest();
1461 req.getHeaders().set(HTTP_HEADER_EXPECT, "100-continue");
1462
1463 InSequence enforceOrder;
1464
1465 auto handler = openTransaction();
__anon611904442a02(std::shared_ptr<HTTPMessage> msg) 1466 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1467 EXPECT_FALSE(msg->getIsChunked());
1468 EXPECT_FALSE(msg->getIsUpgraded());
1469 EXPECT_EQ(417, msg->getStatusCode());
1470 });
1471 handler->expectEOM();
1472 handler->expectDetachTransaction();
1473
1474 handler->sendRequest(req);
1475 readAndLoop(
1476 "HTTP/1.1 417 Expectation Failed\r\n"
1477 "Content-Length: 0\r\n\r\n");
1478
1479 // Now make sure everything still works
1480 testBasicRequest();
1481 EXPECT_FALSE(sessionDestroyed_);
1482 httpSession_->destroy();
1483 }
1484
1485 TEST_F(HTTPUpstreamSessionTest, 101Upgrade) {
1486 // Test an upgrade request with sending 101 response. Then send
1487 // some data and check the onBody callback contents
1488 HTTPMessage req = getGetRequest();
1489 req.getHeaders().set(HTTP_HEADER_UPGRADE, "http/2.0");
1490
1491 InSequence enforceOrder;
1492
1493 auto handler = openTransaction();
__anon611904442b02(std::shared_ptr<HTTPMessage> msg) 1494 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1495 EXPECT_FALSE(msg->getIsChunked());
1496 EXPECT_EQ(101, msg->getStatusCode());
1497 });
1498 EXPECT_CALL(*handler, onUpgrade(_));
1499 EXPECT_CALL(*handler, onBodyWithOffset(_, _))
1500 .WillOnce(ExpectString("Test Body\r\n"));
1501 handler->expectEOM();
1502 handler->expectDetachTransaction();
1503
1504 handler->txn_->sendHeaders(req);
1505 eventBase_.loop();
1506 readAndLoop(
1507 "HTTP/1.1 101 Switching Protocols\r\n"
1508 "Upgrade: http/2.0\r\n\r\n"
1509 "Test Body\r\n");
1510
1511 handler->sendBody(100);
1512 handler->sendEOM();
1513 eventBase_.loopOnce();
1514 eventBase_.loopOnce();
1515 EXPECT_FALSE(transportGood_);
1516
1517 readCallback_->readEOF();
1518 eventBase_.loop();
1519 }
1520
1521 // ===== Upgrade Tests ====
1522
1523 template <class CodecPair>
testSimpleUpgrade(const std::string & upgradeReqHeader,const std::string & upgradeRespHeader,CodecProtocol respCodecVersion)1524 void HTTPUpstreamTest<CodecPair>::testSimpleUpgrade(
1525 const std::string& upgradeReqHeader,
1526 const std::string& upgradeRespHeader,
1527 CodecProtocol respCodecVersion) {
1528 InSequence dummy;
1529 auto handler = openTransaction();
1530 NiceMock<MockUpstreamController> controller;
1531
1532 httpSession_->setController(&controller);
1533 EXPECT_CALL(controller, onSessionCodecChange(httpSession_));
1534
1535 EXPECT_EQ(httpSession_->getMaxConcurrentOutgoingStreams(), 1);
1536
1537 HeaderIndexingStrategy testH2IndexingStrat;
1538 if (respCodecVersion == CodecProtocol::HTTP_2) {
1539 EXPECT_CALL(controller, getHeaderIndexingStrategy())
1540 .WillOnce(Return(&testH2IndexingStrat));
1541 }
1542
1543 handler->expectHeaders([](std::shared_ptr<HTTPMessage> msg) {
1544 EXPECT_EQ(200, msg->getStatusCode());
1545 });
1546 handler->expectBody();
1547 handler->expectEOM();
1548 handler->expectDetachTransaction();
1549
1550 auto txn = handler->txn_;
1551 HTTPMessage req = getUpgradeRequest(upgradeReqHeader);
1552 txn->sendHeaders(req);
1553 txn->sendEOM();
1554 eventBase_.loopOnce(); // force HTTP/1.1 writes
1555 writes_.move(); // clear them out
1556 readAndLoop(
1557 folly::to<string>("HTTP/1.1 101 Switching Protocols\r\n"
1558 "Upgrade: ",
1559 upgradeRespHeader,
1560 "\r\n"
1561 "\r\n"));
1562
1563 if (respCodecVersion == CodecProtocol::HTTP_2) {
1564 const HTTP2Codec* codec =
1565 dynamic_cast<const HTTP2Codec*>(&txn->getTransport().getCodec());
1566 ASSERT_NE(codec, nullptr);
1567 EXPECT_EQ(codec->getHeaderIndexingStrategy(), &testH2IndexingStrat);
1568 }
1569
1570 readAndLoop(getResponseBuf(respCodecVersion, txn->getID(), 200, 100).get());
1571
1572 EXPECT_EQ(httpSession_->getMaxConcurrentOutgoingStreams(), 10);
1573 httpSession_->destroy();
1574 }
1575
TEST_F(HTTPUpstreamSessionTest,HttpUpgradeNativeH2)1576 TEST_F(HTTPUpstreamSessionTest, HttpUpgradeNativeH2) {
1577 testSimpleUpgrade("h2c", "h2c", CodecProtocol::HTTP_2);
1578 }
1579
1580 // Upgrade to SPDY/3.1 with a non-native proto in the list
TEST_F(HTTPUpstreamSessionTest,HttpUpgradeNativeUnknown)1581 TEST_F(HTTPUpstreamSessionTest, HttpUpgradeNativeUnknown) {
1582 testSimpleUpgrade("blarf, h2c", "h2c", CodecProtocol::HTTP_2);
1583 }
1584
1585 // Upgrade header with extra whitespace
TEST_F(HTTPUpstreamSessionTest,HttpUpgradeNativeWhitespace)1586 TEST_F(HTTPUpstreamSessionTest, HttpUpgradeNativeWhitespace) {
1587 testSimpleUpgrade("blarf, \th2c\t, xyz", "h2c", CodecProtocol::HTTP_2);
1588 }
1589
1590 // Upgrade header with random junk
TEST_F(HTTPUpstreamSessionTest,HttpUpgradeNativeJunk)1591 TEST_F(HTTPUpstreamSessionTest, HttpUpgradeNativeJunk) {
1592 testSimpleUpgrade(
1593 ",,,, ,,\t~^%$(*&@(@$^^*(,h2c", "h2c", CodecProtocol::HTTP_2);
1594 }
1595
TEST_F(HTTPUpstreamSessionTest,HttpUpgrade101Unexpected)1596 TEST_F(HTTPUpstreamSessionTest, HttpUpgrade101Unexpected) {
1597 InSequence dummy;
1598 auto handler = openTransaction();
1599
1600 EXPECT_CALL(*handler, onError(_));
1601 handler->expectDetachTransaction();
1602
1603 handler->sendRequest();
1604 eventBase_.loop();
1605 readAndLoop(
1606 folly::to<string>("HTTP/1.1 101 Switching Protocols\r\n"
1607 "Upgrade: spdy/3\r\n"
1608 "\r\n"));
1609 EXPECT_EQ(readCallback_, nullptr);
1610 EXPECT_TRUE(sessionDestroyed_);
1611 }
1612
TEST_F(HTTPUpstreamSessionTest,HttpUpgrade101MissingUpgrade)1613 TEST_F(HTTPUpstreamSessionTest, HttpUpgrade101MissingUpgrade) {
1614 InSequence dummy;
1615 auto handler = openTransaction();
1616
1617 EXPECT_CALL(*handler, onError(_));
1618 handler->expectDetachTransaction();
1619
1620 handler->sendRequest(getUpgradeRequest("spdy/3"));
1621 readAndLoop(
1622 folly::to<string>("HTTP/1.1 101 Switching Protocols\r\n"
1623 "\r\n"));
1624 EXPECT_EQ(readCallback_, nullptr);
1625 EXPECT_TRUE(sessionDestroyed_);
1626 }
1627
TEST_F(HTTPUpstreamSessionTest,HttpUpgrade101BogusHeader)1628 TEST_F(HTTPUpstreamSessionTest, HttpUpgrade101BogusHeader) {
1629 InSequence dummy;
1630 auto handler = openTransaction();
1631
1632 EXPECT_CALL(*handler, onError(_));
1633 handler->expectDetachTransaction();
1634
1635 handler->sendRequest(getUpgradeRequest("spdy/3"));
1636 eventBase_.loop();
1637 readAndLoop(
1638 folly::to<string>("HTTP/1.1 101 Switching Protocols\r\n"
1639 "Upgrade: blarf\r\n"
1640 "\r\n"));
1641 EXPECT_EQ(readCallback_, nullptr);
1642 EXPECT_TRUE(sessionDestroyed_);
1643 }
1644
TEST_F(HTTPUpstreamSessionTest,HttpUpgradePost100)1645 TEST_F(HTTPUpstreamSessionTest, HttpUpgradePost100) {
1646 InSequence dummy;
1647 auto handler = openTransaction();
1648
1649 handler->expectHeaders([](std::shared_ptr<HTTPMessage> msg) {
1650 EXPECT_EQ(100, msg->getStatusCode());
1651 });
1652 handler->expectHeaders([](std::shared_ptr<HTTPMessage> msg) {
1653 EXPECT_EQ(200, msg->getStatusCode());
1654 });
1655 handler->expectBody();
1656 handler->expectEOM();
1657 handler->expectDetachTransaction();
1658
1659 auto txn = handler->txn_;
1660 HTTPMessage req = getUpgradePostRequest(100, "h2c", true /* 100 */);
1661 txn->sendHeaders(req);
1662 auto buf = makeBuf(100);
1663 txn->sendBody(std::move(buf));
1664 txn->sendEOM();
1665 eventBase_.loop();
1666 readAndLoop(
1667 folly::to<string>("HTTP/1.1 100 Continue\r\n"
1668 "\r\n"
1669 "HTTP/1.1 101 Switching Protocols\r\n"
1670 "Upgrade: h2c\r\n"
1671 "\r\n"));
1672 readAndLoop(
1673 getResponseBuf(CodecProtocol::HTTP_2, txn->getID(), 200, 100).get());
1674 httpSession_->destroy();
1675 }
1676
TEST_F(HTTPUpstreamSessionTest,HttpUpgradePost100Http2)1677 TEST_F(HTTPUpstreamSessionTest, HttpUpgradePost100Http2) {
1678 InSequence dummy;
1679 auto handler = openTransaction();
1680
1681 handler->expectHeaders([](std::shared_ptr<HTTPMessage> msg) {
1682 EXPECT_EQ(100, msg->getStatusCode());
1683 });
1684 handler->expectHeaders([](std::shared_ptr<HTTPMessage> msg) {
1685 EXPECT_EQ(200, msg->getStatusCode());
1686 });
1687 handler->expectBody();
1688 handler->expectEOM();
1689 handler->expectDetachTransaction();
1690
1691 auto txn = handler->txn_;
1692 HTTPMessage req = getUpgradePostRequest(100, "h2c");
1693 txn->sendHeaders(req);
1694 auto buf = makeBuf(100);
1695 txn->sendBody(std::move(buf));
1696 txn->sendEOM();
1697 eventBase_.loop();
1698 readAndLoop(
1699 folly::to<string>("HTTP/1.1 101 Switching Protocols\r\n"
1700 "Upgrade: h2c\r\n"
1701 "\r\n"));
1702 readAndLoop(
1703 getResponseBuf(CodecProtocol::HTTP_2, txn->getID(), 200, 100, true)
1704 .get());
1705 httpSession_->destroy();
1706 }
1707
TEST_F(HTTPUpstreamSessionTest,HttpUpgradeOnTxn2)1708 TEST_F(HTTPUpstreamSessionTest, HttpUpgradeOnTxn2) {
1709 InSequence dummy;
1710 auto handler1 = openTransaction();
1711
1712 handler1->expectHeaders([](std::shared_ptr<HTTPMessage> msg) {
1713 EXPECT_EQ(200, msg->getStatusCode());
1714 });
1715 handler1->expectBody();
1716 handler1->expectEOM();
1717 handler1->expectDetachTransaction();
1718
1719 auto txn = handler1->txn_;
1720 HTTPMessage req = getUpgradeRequest("spdy/3");
1721 txn->sendHeaders(req);
1722 txn->sendEOM();
1723 readAndLoop(
1724 "HTTP/1.1 200 Ok\r\n"
1725 "Content-Length: 10\r\n"
1726 "\r\n"
1727 "abcdefghij");
1728 eventBase_.loop();
1729
1730 auto handler2 = openTransaction();
1731
1732 txn = handler2->txn_;
1733 txn->sendHeaders(req);
1734 txn->sendEOM();
1735
1736 handler2->expectHeaders();
1737 handler2->expectEOM();
1738 handler2->expectDetachTransaction();
1739 readAndLoop("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
1740 httpSession_->destroy();
1741 }
1742
1743 class HTTPUpstreamRecvStreamTest : public HTTPUpstreamSessionTest {
1744 public:
HTTPUpstreamRecvStreamTest()1745 HTTPUpstreamRecvStreamTest() : HTTPUpstreamTest({100000, 105000, 110000}) {
1746 }
1747 };
1748
TEST_F(HTTPUpstreamRecvStreamTest,UpgradeFlowControl)1749 TEST_F(HTTPUpstreamRecvStreamTest, UpgradeFlowControl) {
1750 InSequence dummy;
1751 testSimpleUpgrade("h2c", "h2c", CodecProtocol::HTTP_2);
1752
1753 HTTP2Codec serverCodec(TransportDirection::DOWNSTREAM);
1754 NiceMock<MockHTTPCodecCallback> callbacks;
1755 serverCodec.setCallback(&callbacks);
1756 EXPECT_CALL(callbacks, onSettings(_))
1757 .WillOnce(Invoke([this](const SettingsList& settings) {
1758 if (flowControl_[0] > 0) {
1759 for (const auto& setting : settings) {
1760 if (setting.id == SettingsId::INITIAL_WINDOW_SIZE) {
1761 EXPECT_EQ(flowControl_[0], setting.value);
1762 }
1763 }
1764 }
1765 }));
1766 EXPECT_CALL(
1767 callbacks,
1768 onWindowUpdate(0, flowControl_[2] - serverCodec.getDefaultWindowSize()));
1769 size_t initWindow = flowControl_[0] > 0 ? flowControl_[0]
1770 : serverCodec.getDefaultWindowSize();
1771 EXPECT_CALL(callbacks, onWindowUpdate(1, flowControl_[1] - initWindow));
1772 parseOutput(serverCodec);
1773 }
1774
1775 class NoFlushUpstreamSessionTest : public HTTPUpstreamTest<SPDY3CodecPair> {
1776 public:
onWriteChain(folly::AsyncTransport::WriteCallback * callback,std::shared_ptr<IOBuf>,WriteFlags)1777 void onWriteChain(folly::AsyncTransport::WriteCallback* callback,
1778 std::shared_ptr<IOBuf>,
1779 WriteFlags) override {
1780 if (!timesCalled_++) {
1781 callback->writeSuccess();
1782 } else {
1783 cbs_.push_back(callback);
1784 }
1785 // do nothing -- let unacked egress build up
1786 }
1787
~NoFlushUpstreamSessionTest()1788 ~NoFlushUpstreamSessionTest() override {
1789 AsyncSocketException ex(AsyncSocketException::UNKNOWN, "");
1790 for (auto& cb : cbs_) {
1791 cb->writeErr(0, ex);
1792 }
1793 }
1794
1795 private:
1796 uint32_t timesCalled_{0};
1797 std::vector<folly::AsyncTransport::WriteCallback*> cbs_;
1798 };
1799
TEST_F(NoFlushUpstreamSessionTest,DeleteTxnOnUnpause)1800 TEST_F(NoFlushUpstreamSessionTest, DeleteTxnOnUnpause) {
1801 // Test where the handler gets onEgressPaused() and triggers another
1802 // HTTPSession call to iterate over all transactions (to ensure nested
1803 // iteration works).
1804
1805 HTTPMessage req = getGetRequest();
1806
1807 InSequence enforceOrder;
1808
1809 auto handler1 = openNiceTransaction();
1810 auto handler2 = openNiceTransaction();
1811 auto handler3 = openNiceTransaction();
1812 handler2->expectEgressPaused([this] {
1813 // This time it is invoked by the session on all transactions
1814 httpSession_->dropConnection();
1815 });
1816 handler2->txn_->sendHeaders(req);
1817 // This happens when the body write fills the txn egress queue
1818 // Send a body big enough to pause egress
1819 handler2->txn_->onIngressWindowUpdate(100);
1820 handler2->txn_->sendBody(makeBuf(httpSession_->getWriteBufferLimit() + 1));
1821 eventBase_.loop();
1822 }
1823
1824 class MockHTTPUpstreamTest : public HTTPUpstreamTest<MockHTTPCodecPair> {
1825 public:
SetUp()1826 void SetUp() override {
1827 auto codec = std::make_unique<NiceMock<MockHTTPCodec>>();
1828 codecPtr_ = codec.get();
1829 EXPECT_CALL(*codec, supportsParallelRequests())
1830 .WillRepeatedly(Return(true));
1831 EXPECT_CALL(*codec, getTransportDirection())
1832 .WillRepeatedly(Return(TransportDirection::UPSTREAM));
1833 EXPECT_CALL(*codec, setCallback(_)).WillRepeatedly(SaveArg<0>(&codecCb_));
1834 EXPECT_CALL(*codec, isReusable()).WillRepeatedly(ReturnPointee(&reusable_));
1835 EXPECT_CALL(*codec, isWaitingToDrain())
1836 .WillRepeatedly(ReturnPointee(&reusable_));
1837 EXPECT_CALL(*codec, getDefaultWindowSize()).WillRepeatedly(Return(65536));
1838 EXPECT_CALL(*codec, getProtocol())
1839 .WillRepeatedly(Return(CodecProtocol::SPDY_3_1));
1840 EXPECT_CALL(*codec, generateGoaway(_, _, _, _))
1841 .WillRepeatedly(Invoke([&](IOBufQueue& writeBuf,
1842 HTTPCodec::StreamID lastStream,
1843 ErrorCode,
1844 std::shared_ptr<folly::IOBuf>) {
1845 goawayCount_++;
1846 size_t written = 0;
1847 if (reusable_) {
1848 writeBuf.append("GOAWAY", 6);
1849 written = 6;
1850 reusable_ = false;
1851 }
1852 return written;
1853 }));
1854 EXPECT_CALL(*codec, createStream()).WillRepeatedly(Invoke([&] {
1855 auto ret = nextOutgoingTxn_;
1856 nextOutgoingTxn_ += 2;
1857 return ret;
1858 }));
1859
1860 commonSetUp(std::move(codec));
1861 }
1862
TearDown()1863 void TearDown() override {
1864 EXPECT_TRUE(sessionDestroyed_);
1865 }
1866
openTransaction()1867 std::unique_ptr<StrictMock<MockHTTPHandler>> openTransaction() {
1868 // Returns a mock handler with txn_ field set in it
1869 auto handler = std::make_unique<StrictMock<MockHTTPHandler>>();
1870 handler->expectTransaction();
1871 auto txn = httpSession_->newTransaction(handler.get());
1872 EXPECT_EQ(txn, handler->txn_);
1873 return handler;
1874 }
1875
1876 MockHTTPCodec* codecPtr_{nullptr};
1877 HTTPCodec::Callback* codecCb_{nullptr};
1878 bool reusable_{true};
1879 uint32_t nextOutgoingTxn_{1};
1880 uint32_t goawayCount_{0};
1881 };
1882
TEST_F(HTTP2UpstreamSessionTest,ServerPush)1883 TEST_F(HTTP2UpstreamSessionTest, ServerPush) {
1884 httpSession_->setEgressSettings({{SettingsId::ENABLE_PUSH, 1}});
1885
1886 auto egressCodec = makeServerCodec();
1887 folly::IOBufQueue output(folly::IOBufQueue::cacheChainLength());
1888
1889 HTTPMessage push;
1890 push.getHeaders().set("HOST", "www.foo.com");
1891 push.setURL("https://www.foo.com/");
1892 egressCodec->generateSettings(output);
1893 // PUSH_PROMISE
1894 egressCodec->generatePushPromise(output, 2, push, 1);
1895
1896 // Pushed resource
1897 HTTPMessage resp;
1898 resp.setStatusCode(200);
1899 resp.getHeaders().set("ohai", "push");
1900 egressCodec->generateHeader(output, 2, resp);
1901 auto buf = makeBuf(100);
1902 egressCodec->generateBody(
1903 output, 2, std::move(buf), HTTPCodec::NoPadding, true /* eom */);
1904
1905 // Original resource
1906 resp.getHeaders().set("ohai", "orig");
1907 egressCodec->generateHeader(output, 1, resp);
1908 buf = makeBuf(100);
1909 egressCodec->generateBody(
1910 output, 1, std::move(buf), HTTPCodec::NoPadding, true /* eom */);
1911
1912 std::unique_ptr<folly::IOBuf> input = output.move();
1913 input->coalesce();
1914
1915 MockHTTPHandler pushHandler;
1916
1917 InSequence enforceOrder;
1918
1919 auto handler = openTransaction();
1920 EXPECT_CALL(*handler, onPushedTransaction(_))
1921 .WillOnce(Invoke([&pushHandler](HTTPTransaction* pushTxn) {
1922 pushTxn->setHandler(&pushHandler);
1923 }));
1924 EXPECT_CALL(pushHandler, setTransaction(_));
1925 pushHandler.expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1926 EXPECT_EQ(httpSession_->getNumIncomingStreams(), 1);
1927 EXPECT_TRUE(msg->getIsChunked());
1928 EXPECT_FALSE(msg->getIsUpgraded());
1929 EXPECT_EQ(msg->getPathAsStringPiece(), "/");
1930 EXPECT_EQ(msg->getHeaders().getSingleOrEmpty(HTTP_HEADER_HOST),
1931 "www.foo.com");
1932 });
1933 pushHandler.expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1934 EXPECT_EQ(msg->getStatusCode(), 200);
1935 EXPECT_EQ(msg->getHeaders().getSingleOrEmpty("ohai"), "push");
1936 });
1937 pushHandler.expectBody();
1938 pushHandler.expectEOM();
1939 pushHandler.expectDetachTransaction();
1940
1941 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
1942 EXPECT_FALSE(msg->getIsUpgraded());
1943 EXPECT_EQ(200, msg->getStatusCode());
1944 EXPECT_EQ(msg->getHeaders().getSingleOrEmpty("ohai"), "orig");
1945 });
1946 handler->expectBody();
1947 handler->expectEOM();
1948 handler->expectDetachTransaction();
1949
1950 handler->sendRequest();
1951 readAndLoop(input->data(), input->length());
1952
1953 EXPECT_EQ(httpSession_->getNumIncomingStreams(), 0);
1954 httpSession_->destroy();
1955 }
1956
1957 class MockHTTP2UpstreamTest : public MockHTTPUpstreamTest {
1958 public:
SetUp()1959 void SetUp() override {
1960 MockHTTPUpstreamTest::SetUp();
1961
1962 // This class assumes we are doing a test for SPDY or HTTP/2+ where
1963 // this function is *not* a no-op. Indicate this via a positive number
1964 // of bytes being generated for writing RST_STREAM.
1965
1966 ON_CALL(*codecPtr_, generateRstStream(_, _, _)).WillByDefault(Return(1));
1967 }
1968 };
1969
TEST_F(MockHTTP2UpstreamTest,ParseErrorNoTxn)1970 TEST_F(MockHTTP2UpstreamTest, ParseErrorNoTxn) {
1971 // 1) Create streamID == 1
1972 // 2) Send request
1973 // 3) Detach handler
1974 // 4) Get an ingress parse error on reply
1975 // Expect that the codec should be asked to generate an abort on streamID==1
1976
1977 // Setup the codec expectations.
1978 EXPECT_CALL(*codecPtr_, generateHeader(_, _, _, _, _, _))
1979 .WillOnce(Invoke(
1980 [](folly::IOBufQueue& writeBuf,
1981 HTTPCodec::StreamID,
1982 const HTTPMessage&,
1983 bool,
1984 HTTPHeaderSize*,
1985 folly::Optional<HTTPHeaders>) { writeBuf.append("1", 1); }));
1986 EXPECT_CALL(*codecPtr_, generateEOM(_, _)).WillOnce(Return(20));
1987 EXPECT_CALL(*codecPtr_, generateRstStream(_, 1, _));
1988
1989 // 1)
1990 auto handler = openTransaction();
1991
1992 // 2)
1993 handler->sendRequest(getPostRequest());
1994
1995 // 3) Note this sendAbort() doesn't destroy the txn since byte events are
1996 // enqueued
1997 handler->txn_->sendAbort();
1998
1999 // 4)
2000 HTTPException ex(HTTPException::Direction::INGRESS_AND_EGRESS, "foo");
2001 ex.setProxygenError(kErrorParseHeader);
2002 ex.setCodecStatusCode(ErrorCode::REFUSED_STREAM);
2003 codecCb_->onError(1, ex, true);
2004
2005 // cleanup
2006 handler->expectDetachTransaction();
2007 httpSession_->dropConnection();
2008 eventBase_.loop();
2009 }
2010
2011 TEST_F(MockHTTPUpstreamTest, 0MaxOutgoingTxns) {
2012 // Test where an upstream session gets a SETTINGS frame with 0 max
2013 // outgoing transactions. In our implementation, we jsut send a GOAWAY
2014 // and close the connection.
2015
2016 codecCb_->onSettings({{SettingsId::MAX_CONCURRENT_STREAMS, 0}});
2017 EXPECT_TRUE(transactionsFull_);
2018 httpSession_->dropConnection();
2019 }
2020
TEST_F(MockHTTPUpstreamTest,OutgoingTxnSettings)2021 TEST_F(MockHTTPUpstreamTest, OutgoingTxnSettings) {
2022 // Create 2 transactions, then receive a settings frame from
2023 // the server indicating 1 parallel transaction at a time is allowed.
2024 // Then get another SETTINGS frame indicating 100 max parallel
2025 // transactions. Expect that HTTPSession invokes both info callbacks.
2026
2027 NiceMock<MockHTTPHandler> handler1;
2028 NiceMock<MockHTTPHandler> handler2;
2029 httpSession_->newTransaction(&handler1);
2030 httpSession_->newTransaction(&handler2);
2031
2032 codecCb_->onSettings({{SettingsId::MAX_CONCURRENT_STREAMS, 1}});
2033 EXPECT_TRUE(transactionsFull_);
2034 codecCb_->onSettings({{SettingsId::MAX_CONCURRENT_STREAMS, 100}});
2035 EXPECT_FALSE(transactionsFull_);
2036 httpSession_->dropConnection();
2037 }
2038
TEST_F(MockHTTPUpstreamTest,IngressGoawayDrain)2039 TEST_F(MockHTTPUpstreamTest, IngressGoawayDrain) {
2040 // Tests whether the session drains existing transactions and
2041 // deletes itself after receiving a GOAWAY.
2042
2043 InSequence enforceOrder;
2044
2045 auto handler = openTransaction();
2046 EXPECT_CALL(*handler, onGoaway(ErrorCode::NO_ERROR));
2047 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
2048 EXPECT_FALSE(msg->getIsUpgraded());
2049 EXPECT_EQ(200, msg->getStatusCode());
2050 });
2051 handler->expectEOM();
2052 handler->expectDetachTransaction();
2053
2054 // Send the GET request
2055 handler->sendRequest();
2056
2057 // Receive GOAWAY frame with last good stream as 1
2058 codecCb_->onGoaway(1, ErrorCode::NO_ERROR);
2059
2060 // New transactions cannot be created afrer goaway
2061 EXPECT_FALSE(httpSession_->isReusable());
2062 EXPECT_EQ(httpSession_->newTransaction(handler.get()), nullptr);
2063
2064 // Receive 200 OK
2065 auto resp = makeResponse(200);
2066 codecCb_->onMessageBegin(1, resp.get());
2067 codecCb_->onHeadersComplete(1, std::move(resp));
2068 codecCb_->onMessageComplete(1, false);
2069 eventBase_.loop();
2070
2071 // Session will delete itself after getting the response
2072 }
2073
2074 /*
2075 * 1. Setup ControlStream 1, respond with 200
2076 * 2. Send ExStream 3, make HttpSession believes Stream 1 is a ControlStream
2077 * 3. Trigger GOAWAY, Stream should be aborted.
2078 */
2079
TEST_F(MockHTTPUpstreamTest,ControlStreamGoaway)2080 TEST_F(MockHTTPUpstreamTest, ControlStreamGoaway) {
2081 // Tests whether a recognized control stream is aborted
2082 // when session receiving a final GOAWAY, but not on a draining GOAWAY
2083
2084 HTTPSettings settings;
2085 settings.setSetting(SettingsId::ENABLE_EX_HEADERS, 1);
2086 EXPECT_CALL(*codecPtr_, getEgressSettings())
2087 .WillRepeatedly(Return(&settings));
2088
2089 auto handler = openTransaction();
2090
2091 // Create a dummy request
2092 auto pub = getGetRequest("/sub/fyi");
2093 NiceMock<MockHTTPHandler> pubHandler1;
2094 NiceMock<MockHTTPHandler> pubHandler2;
2095
2096 {
2097 InSequence enforceOrder;
2098 handler->expectHeaders([&] {
2099 // Txn 1 completes after draining GOAWAY
2100 auto* pubTxn1 = handler->txn_->newExTransaction(&pubHandler1);
2101 pubTxn1->setHandler(&pubHandler1);
2102 pubHandler1.txn_ = pubTxn1;
2103
2104 pubTxn1->sendHeaders(pub);
2105 pubTxn1->sendBody(makeBuf(200));
2106 pubTxn1->sendEOM();
2107
2108 // Txn 2 completes after cs aborted
2109 auto* pubTxn2 = handler->txn_->newExTransaction(&pubHandler2);
2110 pubTxn2->setHandler(&pubHandler2);
2111 pubHandler2.txn_ = pubTxn2;
2112
2113 pubTxn2->sendHeaders(pub);
2114 pubTxn2->sendBody(makeBuf(200));
2115 pubTxn2->sendEOM();
2116 });
2117 }
2118
2119 // send response header to stream 1 first, triggers sending 2 dep streams
2120 codecCb_->onHeadersComplete(1, makeResponse(200));
2121 eventBase_.loopOnce();
2122
2123 // expect goaway
2124 // transactionIds is stored in unordered set(F14FastSet), the invocation
2125 // order of each txn's goaway method is not deterministic
2126 pubHandler1.expectGoaway();
2127 pubHandler2.expectGoaway();
2128 handler->expectGoaway();
2129
2130 // Receive draining GOAWAY, no-op
2131 codecCb_->onGoaway(http2::kMaxStreamID, ErrorCode::NO_ERROR);
2132 eventBase_.loopOnce();
2133
2134 // Send a reply to finish stream 3
2135 pubHandler1.expectHeaders();
2136 pubHandler1.expectEOM();
2137 pubHandler1.expectDetachTransaction();
2138 codecCb_->onHeadersComplete(3, makeResponse(200));
2139 codecCb_->onMessageComplete(3, false);
2140
2141 {
2142 InSequence enforceOrder;
2143 handler->expectGoaway();
2144 handler->expectError([&](const HTTPException& err) {
2145 EXPECT_TRUE(err.hasProxygenError());
2146 EXPECT_EQ(err.getProxygenError(), kErrorStreamAbort);
2147 ASSERT_EQ("StreamAbort on transaction id: 1", std::string(err.what()));
2148 });
2149 handler->expectDetachTransaction();
2150 }
2151
2152 // Receive GOAWAY frame with last good stream as 5
2153 pubHandler2.expectGoaway();
2154 codecCb_->onGoaway(5, ErrorCode::NO_ERROR);
2155 eventBase_.loop();
2156
2157 pubHandler2.expectError();
2158 pubHandler2.expectDetachTransaction();
2159 // Send a reply to finish stream 5, but it errors because of broken control
2160 codecCb_->onHeadersComplete(5, makeResponse(200));
2161 codecCb_->onMessageComplete(5, false);
2162
2163 eventBase_.loop();
2164 }
2165
TEST_F(MockHTTPUpstreamTest,Goaway)2166 TEST_F(MockHTTPUpstreamTest, Goaway) {
2167 // Make sure existing txns complete successfully even if we drain the
2168 // upstream session
2169 const unsigned numTxns = 10;
2170 MockHTTPHandler handler[numTxns];
2171
2172 InSequence enforceOrder;
2173
2174 for (unsigned i = 0; i < numTxns; ++i) {
2175 handler[i].expectTransaction();
2176 handler[i].expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
2177 EXPECT_FALSE(msg->getIsUpgraded());
2178 EXPECT_EQ(200, msg->getStatusCode());
2179 });
2180 httpSession_->newTransaction(&handler[i]);
2181
2182 // Send the GET request
2183 handler[i].sendRequest();
2184
2185 // Receive 200 OK
2186 auto resp = makeResponse(200);
2187 codecCb_->onMessageBegin(handler[i].txn_->getID(), resp.get());
2188 codecCb_->onHeadersComplete(handler[i].txn_->getID(), std::move(resp));
2189 }
2190
2191 codecCb_->onGoaway(numTxns * 2 + 1, ErrorCode::NO_ERROR);
2192 for (unsigned i = 0; i < numTxns; ++i) {
2193 handler[i].expectEOM();
2194 handler[i].expectDetachTransaction();
2195 codecCb_->onMessageComplete(i * 2 + 1, false);
2196 }
2197 eventBase_.loop();
2198
2199 // Session will delete itself after drain completes
2200 }
2201
TEST_F(MockHTTPUpstreamTest,GoawayPreHeaders)2202 TEST_F(MockHTTPUpstreamTest, GoawayPreHeaders) {
2203 // Make sure existing txns complete successfully even if we drain the
2204 // upstream session
2205 MockHTTPHandler handler;
2206
2207 InSequence enforceOrder;
2208
2209 handler.expectTransaction();
2210 EXPECT_CALL(*codecPtr_, generateHeader(_, _, _, _, _, _))
2211 .WillOnce(Invoke([&](IOBufQueue& writeBuf,
2212 HTTPCodec::StreamID /*stream*/,
2213 const HTTPMessage& /*msg*/,
2214 bool /*eom*/,
2215 HTTPHeaderSize* /*size*/,
2216 folly::Optional<HTTPHeaders>) {
2217 writeBuf.append("HEADERS", 7);
2218 }));
2219 handler.expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
2220 EXPECT_FALSE(msg->getIsUpgraded());
2221 EXPECT_EQ(200, msg->getStatusCode());
2222 });
2223 httpSession_->newTransaction(&handler);
2224 httpSession_->drain();
2225
2226 // Send the GET request
2227 handler.sendRequest();
2228
2229 // Receive 200 OK
2230 auto resp = makeResponse(200);
2231 codecCb_->onMessageBegin(handler.txn_->getID(), resp.get());
2232 codecCb_->onHeadersComplete(handler.txn_->getID(), std::move(resp));
2233
2234 codecCb_->onGoaway(1, ErrorCode::NO_ERROR);
2235 handler.expectEOM();
2236 handler.expectDetachTransaction();
2237 codecCb_->onMessageComplete(1, false);
2238 eventBase_.loop();
2239
2240 auto buf = writes_.move();
2241 ASSERT_TRUE(buf != nullptr);
2242 EXPECT_EQ(buf->moveToFbString().data(), string("HEADERSGOAWAY"));
2243 // Session will delete itself after drain completes
2244 }
2245
TEST_F(MockHTTPUpstreamTest,NoWindowUpdateOnDrain)2246 TEST_F(MockHTTPUpstreamTest, NoWindowUpdateOnDrain) {
2247 EXPECT_CALL(*codecPtr_, supportsStreamFlowControl())
2248 .WillRepeatedly(Return(true));
2249
2250 auto handler = openTransaction();
2251
2252 handler->sendRequest();
2253 httpSession_->drain();
2254 auto streamID = handler->txn_->getID();
2255
2256 EXPECT_CALL(*handler, onGoaway(ErrorCode::NO_ERROR));
2257 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
2258 EXPECT_FALSE(msg->getIsUpgraded());
2259 EXPECT_EQ(200, msg->getStatusCode());
2260 });
2261 EXPECT_CALL(*handler, onBodyWithOffset(_, _)).Times(3);
2262 handler->expectEOM();
2263
2264 handler->expectDetachTransaction();
2265
2266 uint32_t outstanding = 0;
2267 uint32_t sendWindow = 65536;
2268 uint32_t toSend = sendWindow * 1.55;
2269
2270 NiceMock<MockHTTPSessionStats> stats;
2271
2272 httpSession_->setSessionStats(&stats);
2273
2274 // Below is expected to be called by httpSession_ destructor
2275 EXPECT_CALL(stats, recordPendingBufferedReadBytes(0));
2276
2277 // We'll get exactly one window update because we are draining
2278 EXPECT_CALL(*codecPtr_, generateWindowUpdate(_, _, _))
2279 .WillOnce(Invoke([&](folly::IOBufQueue& writeBuf,
2280 HTTPCodec::StreamID /*stream*/,
2281 uint32_t delta) {
2282 EXPECT_EQ(delta, sendWindow);
2283 outstanding -= delta;
2284 uint32_t len = std::min(toSend, sendWindow - outstanding);
2285 EXPECT_CALL(stats, recordPendingBufferedReadBytes(len));
2286 EXPECT_CALL(stats, recordPendingBufferedReadBytes(-1 * (int32_t)len));
2287 EXPECT_LT(len, sendWindow);
2288 toSend -= len;
2289 EXPECT_EQ(toSend, 0);
2290 eventBase_.tryRunAfterDelay(
2291 [this, streamID, len] {
2292 failWrites_ = true;
2293 auto respBody = makeBuf(len);
2294 codecCb_->onBody(streamID, std::move(respBody), 0);
2295 codecCb_->onMessageComplete(streamID, false);
2296 },
2297 50);
2298
2299 const std::string dummy("window");
2300 writeBuf.append(dummy);
2301 return 6;
2302 }));
2303
2304 codecCb_->onGoaway(streamID, ErrorCode::NO_ERROR);
2305 auto resp = makeResponse(200);
2306 codecCb_->onMessageBegin(streamID, resp.get());
2307 codecCb_->onHeadersComplete(streamID, std::move(resp));
2308
2309 // While there is room and the window and body to send
2310 while (sendWindow - outstanding > 0 && toSend > 0) {
2311 // Send up to a 36k chunk
2312 uint32_t len = std::min(toSend, uint32_t(36000));
2313 // limited by the available window
2314 len = std::min(len, sendWindow - outstanding);
2315 EXPECT_CALL(stats, recordPendingBufferedReadBytes(len));
2316 EXPECT_CALL(stats, recordPendingBufferedReadBytes(-1 * (int32_t)len));
2317 auto respBody = makeBuf(len);
2318 toSend -= len;
2319 outstanding += len;
2320 codecCb_->onBody(streamID, std::move(respBody), 0);
2321 }
2322
2323 eventBase_.loop();
2324 }
2325
TEST_F(MockHTTPUpstreamTest,GetWithBody)2326 TEST_F(MockHTTPUpstreamTest, GetWithBody) {
2327 // Should be allowed to send a GET request with body.
2328 NiceMock<MockHTTPHandler> handler;
2329 HTTPMessage req = getGetRequest();
2330 req.getHeaders().set(HTTP_HEADER_CONTENT_LENGTH, "10");
2331
2332 InSequence enforceOrder;
2333
2334 EXPECT_CALL(*codecPtr_, generateHeader(_, _, _, _, _, _));
2335 EXPECT_CALL(*codecPtr_, generateBody(_, _, _, _, true));
2336
2337 auto txn = httpSession_->newTransaction(&handler);
2338 txn->sendHeaders(req);
2339 txn->sendBody(makeBuf(10));
2340 txn->sendEOM();
2341
2342 eventBase_.loop();
2343 httpSession_->dropConnection();
2344 }
2345
TEST_F(MockHTTPUpstreamTest,HeaderWithEom)2346 TEST_F(MockHTTPUpstreamTest, HeaderWithEom) {
2347 NiceMock<MockHTTPHandler> handler;
2348 HTTPMessage req = getGetRequest();
2349 EXPECT_CALL(*codecPtr_, generateHeader(_, _, _, true, _, _));
2350
2351 auto txn = httpSession_->newTransaction(&handler);
2352 txn->sendHeadersWithEOM(req);
2353 eventBase_.loop();
2354 EXPECT_TRUE(txn->isEgressComplete());
2355 httpSession_->dropConnection();
2356 }
2357
2358 template <int stage>
2359 class TestAbortPost : public MockHTTPUpstreamTest {
2360 public:
doAbortTest()2361 void doAbortTest() {
2362 // Send an abort at various points while receiving the response to a GET
2363 // The test is broken into "stages"
2364 // Stage 0) headers received
2365 // Stage 1) chunk header received
2366 // Stage 2) body received
2367 // Stage 3) chunk complete received
2368 // Stage 4) trailers received
2369 // Stage 5) eom received
2370 // This test makes sure expected callbacks are received if an abort is
2371 // sent before any of these stages.
2372 InSequence enforceOrder;
2373 StrictMock<MockHTTPHandler> handler;
2374 HTTPMessage req = getPostRequest(10);
2375
2376 std::unique_ptr<HTTPMessage> resp;
2377 std::unique_ptr<folly::IOBuf> respBody;
2378 std::tie(resp, respBody) = makeResponse(200, 50);
2379
2380 handler.expectTransaction();
2381 EXPECT_CALL(*codecPtr_, generateHeader(_, _, _, _, _, _));
2382
2383 if (stage > 0) {
2384 handler.expectHeaders();
2385 }
2386 if (stage > 1) {
2387 EXPECT_CALL(handler, onChunkHeader(_));
2388 }
2389 if (stage > 2) {
2390 EXPECT_CALL(handler, onBodyWithOffset(_, _));
2391 }
2392 if (stage > 3) {
2393 EXPECT_CALL(handler, onChunkComplete());
2394 }
2395 if (stage > 4) {
2396 EXPECT_CALL(handler, onTrailers(_));
2397 }
2398 if (stage > 5) {
2399 handler.expectEOM();
2400 }
2401
2402 auto txn = httpSession_->newTransaction(&handler);
2403 auto streamID = txn->getID();
2404 txn->sendHeaders(req);
2405 txn->sendBody(makeBuf(5)); // only send half the body
2406
2407 auto doAbort = [&] {
2408 EXPECT_CALL(*codecPtr_, generateRstStream(_, txn->getID(), _));
2409 handler.expectDetachTransaction();
2410 const auto id = txn->getID();
2411 txn->sendAbort();
2412 EXPECT_CALL(*codecPtr_,
2413 generateRstStream(_, id, ErrorCode::STREAM_CLOSED))
2414 .Times(AtLeast(0));
2415 };
2416
2417 if (stage == 0) {
2418 doAbort();
2419 }
2420 codecCb_->onHeadersComplete(streamID, std::move(resp));
2421 if (stage == 1) {
2422 doAbort();
2423 }
2424 codecCb_->onChunkHeader(streamID, respBody->computeChainDataLength());
2425 if (stage == 2) {
2426 doAbort();
2427 }
2428 codecCb_->onBody(streamID, std::move(respBody), 0);
2429 if (stage == 3) {
2430 doAbort();
2431 }
2432 codecCb_->onChunkComplete(streamID);
2433 if (stage == 4) {
2434 doAbort();
2435 }
2436 codecCb_->onTrailersComplete(streamID, std::make_unique<HTTPHeaders>());
2437 if (stage == 5) {
2438 doAbort();
2439 }
2440 codecCb_->onMessageComplete(streamID, false);
2441
2442 eventBase_.loop();
2443 }
2444 };
2445
2446 typedef TestAbortPost<1> TestAbortPost1;
2447 typedef TestAbortPost<2> TestAbortPost2;
2448 typedef TestAbortPost<3> TestAbortPost3;
2449 typedef TestAbortPost<4> TestAbortPost4;
2450 typedef TestAbortPost<5> TestAbortPost5;
2451
TEST_F(TestAbortPost1,Test)2452 TEST_F(TestAbortPost1, Test) {
2453 doAbortTest();
2454 }
TEST_F(TestAbortPost2,Test)2455 TEST_F(TestAbortPost2, Test) {
2456 doAbortTest();
2457 }
TEST_F(TestAbortPost3,Test)2458 TEST_F(TestAbortPost3, Test) {
2459 doAbortTest();
2460 }
TEST_F(TestAbortPost4,Test)2461 TEST_F(TestAbortPost4, Test) {
2462 doAbortTest();
2463 }
TEST_F(TestAbortPost5,Test)2464 TEST_F(TestAbortPost5, Test) {
2465 doAbortTest();
2466 }
2467
TEST_F(MockHTTPUpstreamTest,AbortUpgrade)2468 TEST_F(MockHTTPUpstreamTest, AbortUpgrade) {
2469 // This is basically the same test as above, just for the upgrade path
2470 InSequence enforceOrder;
2471 StrictMock<MockHTTPHandler> handler;
2472 HTTPMessage req = getPostRequest(10);
2473
2474 std::unique_ptr<HTTPMessage> resp = makeResponse(200);
2475
2476 handler.expectTransaction();
2477 EXPECT_CALL(*codecPtr_, generateHeader(_, _, _, _, _, _));
2478
2479 auto txn = httpSession_->newTransaction(&handler);
2480 const auto streamID = txn->getID();
2481 txn->sendHeaders(req);
2482 txn->sendBody(makeBuf(5)); // only send half the body
2483
2484 handler.expectHeaders();
2485 codecCb_->onHeadersComplete(streamID, std::move(resp));
2486
2487 EXPECT_CALL(*codecPtr_, generateRstStream(_, streamID, _));
2488 handler.expectDetachTransaction();
2489 EXPECT_CALL(*codecPtr_,
2490 generateRstStream(_, streamID, ErrorCode::STREAM_CLOSED));
2491 txn->sendAbort();
2492 codecCb_->onMessageComplete(streamID, true); // upgrade
2493 EXPECT_CALL(*codecPtr_,
2494 generateRstStream(_, streamID, ErrorCode::STREAM_CLOSED));
2495 codecCb_->onMessageComplete(streamID, false); // eom
2496
2497 eventBase_.loop();
2498 }
2499
TEST_F(MockHTTPUpstreamTest,DrainBeforeSendHeaders)2500 TEST_F(MockHTTPUpstreamTest, DrainBeforeSendHeaders) {
2501 // Test that drain on session before sendHeaders() is called on open txn
2502
2503 InSequence enforceOrder;
2504 MockHTTPHandler pushHandler;
2505
2506 auto handler = openTransaction();
2507 EXPECT_CALL(*codecPtr_, generateHeader(_, _, _, _, _, _));
2508
2509 handler->expectHeaders();
2510 handler->expectEOM();
2511 handler->expectDetachTransaction();
2512
2513 httpSession_->drain();
2514 handler->sendRequest();
2515 codecCb_->onHeadersComplete(handler->txn_->getID(), makeResponse(200));
2516 codecCb_->onMessageComplete(handler->txn_->getID(), false); // eom
2517
2518 eventBase_.loop();
2519 }
2520
TEST_F(MockHTTP2UpstreamTest,ReceiveDoubleGoaway)2521 TEST_F(MockHTTP2UpstreamTest, ReceiveDoubleGoaway) {
2522 // Test that we handle receiving two goaways correctly
2523
2524 auto req = getGetRequest();
2525
2526 // Open 2 txns but doesn't send headers yet
2527 auto handler1 = openTransaction();
2528 auto handler2 = openTransaction();
2529
2530 // Get first goaway acking many un-started txns
2531 handler1->expectGoaway();
2532 handler2->expectGoaway();
2533 codecCb_->onGoaway(101, ErrorCode::NO_ERROR);
2534 reusable_ = false;
2535
2536 // This txn should be alive since it was ack'd by the above goaway
2537 handler1->txn_->sendHeaders(req);
2538
2539 // Second goaway acks the only the current outstanding transaction
2540 handler1->expectGoaway();
2541 handler2->expectGoaway();
2542 handler2->expectError([&](const HTTPException& err) {
2543 EXPECT_TRUE(err.hasProxygenError());
2544 EXPECT_EQ(err.getProxygenError(), kErrorStreamUnacknowledged);
2545 ASSERT_EQ(folly::to<std::string>("StreamUnacknowledged on transaction id: ",
2546 handler2->txn_->getID()),
2547 std::string(err.what()));
2548 });
2549 handler2->expectDetachTransaction();
2550 codecCb_->onGoaway(handler1->txn_->getID(), ErrorCode::NO_ERROR);
2551
2552 // Clean up
2553 httpSession_->drain();
2554 EXPECT_CALL(*codecPtr_, generateRstStream(_, handler1->txn_->getID(), _));
2555 handler1->expectDetachTransaction();
2556 handler1->txn_->sendAbort();
2557 eventBase_.loop();
2558 }
2559
TEST_F(MockHTTP2UpstreamTest,ReceiveDoubleGoaway2)2560 TEST_F(MockHTTP2UpstreamTest, ReceiveDoubleGoaway2) {
2561 // Test that we handle receiving two goaways correctly
2562 httpSession_->enableDoubleGoawayDrain();
2563 auto req = getGetRequest();
2564
2565 // Open 2 txns but doesn't send headers yet
2566 auto handler1 = openTransaction();
2567 auto handler2 = openTransaction();
2568
2569 // Get first goaway acking many un-started txns
2570 handler1->expectGoaway();
2571 handler2->expectGoaway();
2572 codecCb_->onGoaway(http2::kMaxStreamID, ErrorCode::NO_ERROR);
2573 reusable_ = false;
2574 // this doesn't trigger a goaway
2575 EXPECT_EQ(goawayCount_, 0);
2576
2577 // Sending all unstarted headers should not send the final GOAWAY.
2578 handler1->txn_->sendHeadersWithEOM(req);
2579 handler2->txn_->sendHeadersWithEOM(req);
2580 EXPECT_EQ(goawayCount_, 0);
2581
2582 handler1->expectHeaders();
2583 handler1->expectEOM();
2584 handler1->expectDetachTransaction();
2585 handler2->expectHeaders();
2586 handler2->expectEOM();
2587 handler2->expectDetachTransaction();
2588 codecCb_->onHeadersComplete(1, makeResponse(200));
2589 codecCb_->onMessageComplete(1, false);
2590 codecCb_->onHeadersComplete(3, makeResponse(200));
2591 codecCb_->onMessageComplete(3, false);
2592
2593 eventBase_.loop();
2594 EXPECT_GE(goawayCount_, 1);
2595 }
2596
TEST_F(MockHTTP2UpstreamTest,ServerPushInvalidAssoc)2597 TEST_F(MockHTTP2UpstreamTest, ServerPushInvalidAssoc) {
2598 // Test that protocol error is generated on server push
2599 // with invalid assoc stream id
2600 InSequence enforceOrder;
2601 auto req = getGetRequest();
2602 auto handler = openTransaction();
2603
2604 int streamID = handler->txn_->getID();
2605 int pushID = streamID + 1;
2606 int badAssocID = streamID + 2;
2607
2608 EXPECT_CALL(*codecPtr_,
2609 generateRstStream(_, pushID, ErrorCode::PROTOCOL_ERROR));
2610 EXPECT_CALL(*codecPtr_,
2611 generateRstStream(_, pushID, ErrorCode::STREAM_CLOSED))
2612 .Times(2);
2613
2614 auto resp = makeResponse(200);
2615 codecCb_->onPushMessageBegin(pushID, badAssocID, resp.get());
2616 codecCb_->onHeadersComplete(pushID, std::move(resp));
2617 codecCb_->onMessageComplete(pushID, false);
2618
2619 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
2620 EXPECT_FALSE(msg->getIsUpgraded());
2621 EXPECT_EQ(200, msg->getStatusCode());
2622 });
2623 handler->expectEOM();
2624
2625 resp = makeResponse(200);
2626 codecCb_->onMessageBegin(streamID, resp.get());
2627 codecCb_->onHeadersComplete(streamID, std::move(resp));
2628 codecCb_->onMessageComplete(streamID, false);
2629
2630 // Cleanup
2631 EXPECT_CALL(*codecPtr_, generateRstStream(_, streamID, _));
2632 EXPECT_CALL(*handler, detachTransaction());
2633 handler->terminate();
2634
2635 EXPECT_TRUE(!httpSession_->hasActiveTransactions());
2636 httpSession_->destroy();
2637 }
2638
TEST_F(MockHTTP2UpstreamTest,ServerPushAfterFin)2639 TEST_F(MockHTTP2UpstreamTest, ServerPushAfterFin) {
2640 // Test that protocol error is generated on server push
2641 // after FIN is received on regular response on the stream
2642 InSequence enforceOrder;
2643 auto req = getGetRequest();
2644 auto handler = openTransaction();
2645
2646 int streamID = handler->txn_->getID();
2647 int pushID = streamID + 1;
2648
2649 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
2650 EXPECT_FALSE(msg->getIsUpgraded());
2651 EXPECT_EQ(200, msg->getStatusCode());
2652 });
2653 handler->expectEOM();
2654
2655 auto resp = makeResponse(200);
2656 codecCb_->onMessageBegin(streamID, resp.get());
2657 codecCb_->onHeadersComplete(streamID, std::move(resp));
2658 codecCb_->onMessageComplete(streamID, false);
2659
2660 EXPECT_CALL(*codecPtr_,
2661 generateRstStream(_, pushID, ErrorCode::PROTOCOL_ERROR))
2662 .WillOnce(InvokeWithoutArgs([this] {
2663 // Verify that the assoc txn is still present
2664 EXPECT_TRUE(httpSession_->hasActiveTransactions());
2665 return 1;
2666 }));
2667 EXPECT_CALL(*codecPtr_,
2668 generateRstStream(_, pushID, ErrorCode::STREAM_CLOSED))
2669 .Times(2);
2670
2671 resp = makeResponse(200);
2672 codecCb_->onPushMessageBegin(pushID, streamID, resp.get());
2673 codecCb_->onHeadersComplete(pushID, std::move(resp));
2674 codecCb_->onMessageComplete(pushID, false);
2675
2676 // Cleanup
2677 EXPECT_CALL(*codecPtr_, generateRstStream(_, streamID, _));
2678 EXPECT_CALL(*handler, detachTransaction());
2679 handler->terminate();
2680
2681 EXPECT_TRUE(!httpSession_->hasActiveTransactions());
2682 httpSession_->destroy();
2683 }
2684
TEST_F(MockHTTP2UpstreamTest,ServerPushHandlerInstallFail)2685 TEST_F(MockHTTP2UpstreamTest, ServerPushHandlerInstallFail) {
2686 // Test that REFUSED_STREAM error is generated when the session
2687 // fails to install the server push handler
2688 InSequence enforceOrder;
2689 auto req = getGetRequest();
2690 auto handler = openTransaction();
2691
2692 int streamID = handler->txn_->getID();
2693 int pushID = streamID + 1;
2694
2695 EXPECT_CALL(*handler, onPushedTransaction(_))
2696 .WillOnce(Invoke([](HTTPTransaction* txn) {
2697 // Intentionally unset the handler on the upstream push txn
2698 txn->setHandler(nullptr);
2699 }));
2700 EXPECT_CALL(*codecPtr_,
2701 generateRstStream(_, pushID, ErrorCode::REFUSED_STREAM));
2702 EXPECT_CALL(*codecPtr_,
2703 generateRstStream(_, pushID, ErrorCode::STREAM_CLOSED))
2704 .Times(2);
2705
2706 auto resp = std::make_unique<HTTPMessage>();
2707 resp->setStatusCode(200);
2708 resp->setStatusMessage("OK");
2709 codecCb_->onPushMessageBegin(pushID, streamID, resp.get());
2710 codecCb_->onHeadersComplete(pushID, std::move(resp));
2711 codecCb_->onMessageComplete(pushID, false);
2712
2713 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
2714 EXPECT_FALSE(msg->getIsUpgraded());
2715 EXPECT_EQ(200, msg->getStatusCode());
2716 });
2717 handler->expectEOM();
2718
2719 resp = std::make_unique<HTTPMessage>();
2720 resp->setStatusCode(200);
2721 resp->setStatusMessage("OK");
2722 codecCb_->onMessageBegin(streamID, resp.get());
2723 codecCb_->onHeadersComplete(streamID, std::move(resp));
2724 codecCb_->onMessageComplete(streamID, false);
2725
2726 // Cleanup
2727 EXPECT_CALL(*codecPtr_, generateRstStream(_, streamID, _));
2728 EXPECT_CALL(*handler, detachTransaction());
2729 handler->terminate();
2730
2731 EXPECT_TRUE(!httpSession_->hasActiveTransactions());
2732 httpSession_->destroy();
2733 }
2734
TEST_F(MockHTTP2UpstreamTest,ServerPushUnhandledAssoc)2735 TEST_F(MockHTTP2UpstreamTest, ServerPushUnhandledAssoc) {
2736 // Test that REFUSED_STREAM error is generated when the assoc txn
2737 // is unhandled
2738 InSequence enforceOrder;
2739 auto req = getGetRequest();
2740 auto handler = openTransaction();
2741
2742 int streamID = handler->txn_->getID();
2743 int pushID = streamID + 1;
2744
2745 // Forcefully unset the handler on the assoc txn
2746 handler->txn_->setHandler(nullptr);
2747
2748 EXPECT_CALL(*codecPtr_,
2749 generateRstStream(_, pushID, ErrorCode::REFUSED_STREAM));
2750 EXPECT_CALL(*codecPtr_,
2751 generateRstStream(_, pushID, ErrorCode::STREAM_CLOSED))
2752 .Times(2);
2753
2754 auto resp = std::make_unique<HTTPMessage>();
2755 resp->setStatusCode(200);
2756 resp->setStatusMessage("OK");
2757 codecCb_->onPushMessageBegin(pushID, streamID, resp.get());
2758 codecCb_->onHeadersComplete(pushID, std::move(resp));
2759 codecCb_->onMessageComplete(pushID, false);
2760
2761 // Cleanup
2762 EXPECT_CALL(*codecPtr_, generateRstStream(_, streamID, _));
2763 handler->terminate();
2764
2765 EXPECT_TRUE(!httpSession_->hasActiveTransactions());
2766 httpSession_->destroy();
2767 }
2768
TEST_F(MockHTTPUpstreamTest,HeadersThenBodyThenHeaders)2769 TEST_F(MockHTTPUpstreamTest, HeadersThenBodyThenHeaders) {
2770 HTTPMessage req = getGetRequest();
2771 auto handler = openTransaction();
2772 handler->txn_->sendHeaders(req);
2773
2774 handler->expectHeaders();
2775 EXPECT_CALL(*handler, onBodyWithOffset(_, _));
2776 // After getting the second headers, transaction will detach the handler
2777 handler->expectError([&](const HTTPException& err) {
2778 EXPECT_TRUE(err.hasProxygenError());
2779 EXPECT_EQ(err.getProxygenError(), kErrorIngressStateTransition);
2780 ASSERT_EQ(
2781 "Invalid ingress state transition, state=RegularBodyReceived, "
2782 "event=onHeaders, streamID=1",
2783 std::string(err.what()));
2784 });
2785 handler->expectDetachTransaction();
2786 auto resp = makeResponse(200);
2787 codecCb_->onMessageBegin(1, resp.get());
2788 codecCb_->onHeadersComplete(1, std::move(resp));
2789 codecCb_->onBody(1, makeBuf(20), 0);
2790 // Now receive headers again, on the same stream (illegal!)
2791 codecCb_->onHeadersComplete(1, makeResponse(200));
2792 eventBase_.loop();
2793 }
2794
TEST_F(MockHTTP2UpstreamTest,DelayUpstreamWindowUpdate)2795 TEST_F(MockHTTP2UpstreamTest, DelayUpstreamWindowUpdate) {
2796 EXPECT_CALL(*codecPtr_, supportsStreamFlowControl())
2797 .WillRepeatedly(Return(true));
2798
2799 auto handler = openTransaction();
2800 handler->txn_->setReceiveWindow(1000000); // One miiiillion
2801
2802 InSequence enforceOrder;
2803 EXPECT_CALL(*codecPtr_, generateHeader(_, _, _, _, _, _));
2804 EXPECT_CALL(*codecPtr_, generateWindowUpdate(_, _, _));
2805
2806 HTTPMessage req = getGetRequest();
2807 handler->txn_->sendHeaders(req);
2808 handler->expectDetachTransaction();
2809 handler->txn_->sendAbort();
2810 httpSession_->destroy();
2811 }
2812
TEST_F(MockHTTPUpstreamTest,ForceShutdownInSetTransaction)2813 TEST_F(MockHTTPUpstreamTest, ForceShutdownInSetTransaction) {
2814 StrictMock<MockHTTPHandler> handler;
2815 handler.expectTransaction([&](HTTPTransaction* txn) {
2816 handler.txn_ = txn;
2817 httpSession_->dropConnection();
2818 });
2819 handler.expectError([&](const HTTPException& err) {
2820 EXPECT_TRUE(err.hasProxygenError());
2821 EXPECT_EQ(err.getProxygenError(), kErrorDropped);
2822 ASSERT_EQ(folly::to<std::string>("Dropped on transaction id: ",
2823 handler.txn_->getID()),
2824 std::string(err.what()));
2825 });
2826 handler.expectDetachTransaction();
2827 (void)httpSession_->newTransaction(&handler);
2828 }
2829
2830 // Creates two Handlers, and asserts that each one receives
2831 // an injectTraceEvent call.
TEST_F(MockHTTPUpstreamTest,InjectTraceEvent)2832 TEST_F(MockHTTPUpstreamTest, InjectTraceEvent) {
2833 StrictMock<MockHTTPHandler> handler;
2834 handler.expectTransaction([&](HTTPTransaction* txn) { handler.txn_ = txn; });
2835 // Boilerplate because the session and all handlers have to be
2836 // dropped and errored out.
2837 handler.expectError([&](const HTTPException& err) {});
2838 handler.expectDetachTransaction();
2839
2840 StrictMock<MockHTTPHandler> handler2;
2841 handler2.expectTransaction(
2842 [&](HTTPTransaction* txn) { handler2.txn_ = txn; });
2843 // Boilerplate because the session and all handlers have to be
2844 // dropped and errored out.
2845 handler2.expectError([&](const HTTPException& err) {});
2846 handler2.expectDetachTransaction();
2847
2848 EXPECT_CALL(handler, traceEventAvailable(_)).Times(1);
2849 EXPECT_CALL(handler2, traceEventAvailable(_)).Times(1);
2850
2851 (void)httpSession_->newTransaction(&handler);
2852 (void)httpSession_->newTransaction(&handler2);
2853 TraceEvent te(TraceEventType::TotalRequest);
2854 httpSession_->injectTraceEventIntoAllTransactions(te);
2855 // Boilerplate because test shutdown expects the test finishes
2856 // with the session in a shut-down state.
2857 httpSession_->dropConnection();
2858 }
2859
TEST_F(HTTP2UpstreamSessionTest,TestReplaySafetyCallback)2860 TEST_F(HTTP2UpstreamSessionTest, TestReplaySafetyCallback) {
2861 auto sock = dynamic_cast<HTTPTransaction::Transport*>(httpSession_);
2862
2863 StrictMock<folly::test::MockReplaySafetyCallback> cb1;
2864 StrictMock<folly::test::MockReplaySafetyCallback> cb2;
2865 StrictMock<folly::test::MockReplaySafetyCallback> cb3;
2866
2867 EXPECT_CALL(*transport_, isReplaySafe()).WillRepeatedly(Return(false));
2868 sock->addWaitingForReplaySafety(&cb1);
2869 sock->addWaitingForReplaySafety(&cb2);
2870 sock->addWaitingForReplaySafety(&cb3);
2871 sock->removeWaitingForReplaySafety(&cb2);
2872
2873 ON_CALL(*transport_, isReplaySafe()).WillByDefault(Return(true));
2874 EXPECT_CALL(cb1, onReplaySafe_());
2875 EXPECT_CALL(cb3, onReplaySafe_());
2876 replaySafetyCallback_->onReplaySafe();
2877
2878 httpSession_->destroy();
2879 }
2880
TEST_F(HTTP2UpstreamSessionTest,TestAlreadyReplaySafe)2881 TEST_F(HTTP2UpstreamSessionTest, TestAlreadyReplaySafe) {
2882 auto sock = dynamic_cast<HTTPTransaction::Transport*>(httpSession_);
2883
2884 StrictMock<folly::test::MockReplaySafetyCallback> cb;
2885
2886 EXPECT_CALL(*transport_, isReplaySafe()).WillRepeatedly(Return(true));
2887 EXPECT_CALL(cb, onReplaySafe_());
2888 sock->addWaitingForReplaySafety(&cb);
2889
2890 httpSession_->destroy();
2891 }
2892
TEST_F(HTTP2UpstreamSessionTest,TestChainedBufIngress)2893 TEST_F(HTTP2UpstreamSessionTest, TestChainedBufIngress) {
2894 auto buf = folly::IOBuf::copyBuffer("hi");
2895 buf->prependChain(folly::IOBuf::copyBuffer("hello"));
2896
2897 MockHTTPSessionInfoCallback infoCb;
2898 this->httpSession_->setInfoCallback(&infoCb);
2899
2900 EXPECT_CALL(infoCb, onRead(_, 7, _));
2901 readCallback_->readBufferAvailable(std::move(buf));
2902
2903 httpSession_->destroy();
2904 }
2905
TEST_F(HTTP2UpstreamSessionTest,AttachDetach)2906 TEST_F(HTTP2UpstreamSessionTest, AttachDetach) {
2907 folly::EventBase base;
2908 auto timer = folly::HHWheelTimer::newTimer(
2909 &base,
2910 std::chrono::milliseconds(folly::HHWheelTimer::DEFAULT_TICK_INTERVAL),
2911 TimeoutManager::InternalEnum::INTERNAL,
2912 std::chrono::milliseconds(500));
2913 WheelTimerInstance timerInstance(timer.get());
2914 uint64_t filterCount = 0;
2915 auto fn = [&filterCount](HTTPCodecFilter* /*filter*/) { filterCount++; };
2916
2917 InSequence enforceOrder;
2918 auto egressCodec = makeServerCodec();
2919 folly::IOBufQueue output(folly::IOBufQueue::cacheChainLength());
2920 egressCodec->generateConnectionPreface(output);
2921 egressCodec->generateSettings(output);
2922
2923 for (auto i = 0; i < 2; i++) {
2924 auto handler = openTransaction();
2925 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
2926 EXPECT_EQ(200, msg->getStatusCode());
2927 });
2928 handler->expectBody();
2929 handler->expectEOM();
2930 handler->expectDetachTransaction();
2931
2932 HTTPMessage resp;
2933 resp.setStatusCode(200);
2934 egressCodec->generateHeader(output, handler->txn_->getID(), resp);
2935 egressCodec->generateBody(output,
2936 handler->txn_->getID(),
2937 makeBuf(20),
2938 HTTPCodec::NoPadding,
2939 true /* eom */);
2940
2941 handler->sendRequest();
2942 auto buf = output.move();
2943 buf->coalesce();
2944 readAndLoop(buf.get());
2945
2946 httpSession_->detachThreadLocals();
2947 httpSession_->attachThreadLocals(
2948 &base, nullptr, timerInstance, nullptr, fn, nullptr, nullptr);
2949 EXPECT_EQ(filterCount, 3);
2950 filterCount = 0;
2951 base.loopOnce();
2952 }
2953 httpSession_->destroy();
2954 }
2955
TEST_F(HTTP2UpstreamSessionTest,DetachFlowControlTimeout)2956 TEST_F(HTTP2UpstreamSessionTest, DetachFlowControlTimeout) {
2957 folly::EventBase base;
2958 auto timer = folly::HHWheelTimer::newTimer(
2959 &base,
2960 std::chrono::milliseconds(folly::HHWheelTimer::DEFAULT_TICK_INTERVAL),
2961 TimeoutManager::InternalEnum::INTERNAL,
2962 std::chrono::milliseconds(500));
2963 WheelTimerInstance timerInstance(timer.get());
2964 uint64_t filterCount = 0;
2965 auto fn = [&filterCount](HTTPCodecFilter* /*filter*/) { filterCount++; };
2966
2967 InSequence enforceOrder;
2968 auto egressCodec = makeServerCodec();
2969 folly::IOBufQueue output(folly::IOBufQueue::cacheChainLength());
2970 egressCodec->generateConnectionPreface(output);
2971 egressCodec->generateSettings(output);
2972
2973 for (auto i = 0; i < 2; i++) {
2974 auto handler = openTransaction();
2975 if (i == 1) {
2976 handler->expectHeaders([&](std::shared_ptr<HTTPMessage> msg) {
2977 EXPECT_EQ(200, msg->getStatusCode());
2978 });
2979 handler->expectBody();
2980 handler->expectEOM();
2981
2982 HTTPMessage resp;
2983 resp.setStatusCode(200);
2984 egressCodec->generateHeader(output, handler->txn_->getID(), resp);
2985 egressCodec->generateBody(output,
2986 handler->txn_->getID(),
2987 makeBuf(20),
2988 HTTPCodec::NoPadding,
2989 true /* eom */);
2990 } else {
2991 handler->expectEgressPaused();
2992 egressCodec->generateWindowUpdate(output, 0, 65536 * 2);
2993 }
2994 handler->expectDetachTransaction();
2995
2996 handler->txn_->sendHeaders(getPostRequest(65536 - 2 * i));
2997 handler->txn_->sendBody(makeBuf(65536 - 2 * i));
2998 handler->txn_->sendEOM();
2999 if (i == 0) {
3000 eventBase_.loopOnce();
3001 handler->txn_->sendAbort();
3002 // Even though there are no transactions, the fc timeout is still
3003 // registered.
3004 EXPECT_FALSE(httpSession_->isDetachable(false));
3005 }
3006
3007 auto buf = output.move();
3008 buf->coalesce();
3009 readAndLoop(buf.get());
3010
3011 EXPECT_TRUE(httpSession_->isDetachable(false));
3012 httpSession_->detachThreadLocals();
3013 httpSession_->attachThreadLocals(
3014 &base, nullptr, timerInstance, nullptr, fn, nullptr, nullptr);
3015 EXPECT_EQ(filterCount, 3);
3016 filterCount = 0;
3017 base.loopOnce();
3018 }
3019 httpSession_->destroy();
3020 }
3021
TEST_F(HTTP2UpstreamSessionTest,TestPingPreserveData)3022 TEST_F(HTTP2UpstreamSessionTest, TestPingPreserveData) {
3023 auto serverCodec = makeServerCodec();
3024 folly::IOBufQueue output(folly::IOBufQueue::cacheChainLength());
3025 serverCodec->generateConnectionPreface(output);
3026 serverCodec->generateSettings(output);
3027
3028 auto pingData = std::chrono::duration_cast<std::chrono::milliseconds>(
3029 std::chrono::steady_clock::now().time_since_epoch())
3030 .count();
3031 serverCodec->generatePingRequest(output, pingData);
3032 NiceMock<MockHTTPCodecCallback> callbacks;
3033 serverCodec->setCallback(&callbacks);
3034 EXPECT_CALL(callbacks, onPingReply(pingData));
3035 auto buf = output.move();
3036 buf->coalesce();
3037 readAndLoop(buf.get());
3038 parseOutput(*serverCodec);
3039 httpSession_->destroy();
3040 }
3041
TEST_F(HTTP2UpstreamSessionTest,TestConnectionToken)3042 TEST_F(HTTP2UpstreamSessionTest, TestConnectionToken) {
3043 auto handler = openTransaction();
3044 handler->expectError();
3045 handler->expectDetachTransaction();
3046
3047 // The transaction should not have a connection token
3048 // by default.
3049 EXPECT_EQ(handler->txn_->getConnectionToken(), folly::none);
3050
3051 // Passing connection token to a session should
3052 // make it visible to the transaction.
3053 HTTPTransaction::ConnectionToken connToken{"TOKEN1234"};
3054 httpSession_->setConnectionToken(connToken);
3055
3056 EXPECT_NE(handler->txn_->getConnectionToken(), folly::none);
3057 EXPECT_EQ(*handler->txn_->getConnectionToken(), connToken);
3058
3059 eventBase_.loop();
3060 httpSession_->dropConnection();
3061 }
3062
TEST_F(HTTP2UpstreamSessionTest,HTTPPriority)3063 TEST_F(HTTP2UpstreamSessionTest, HTTPPriority) {
3064 auto handler = openTransaction();
3065 handler->expectError();
3066 handler->expectDetachTransaction();
3067 EXPECT_EQ(handler->txn_->getHTTPPriority(), folly::none);
3068 eventBase_.loop();
3069 httpSession_->dropConnection();
3070 }
3071
3072 // Register and instantiate all our type-paramterized tests
3073 REGISTER_TYPED_TEST_CASE_P(HTTPUpstreamTest, ImmediateEof);
3074
3075 using AllTypes = ::testing::Types<HTTP1xCodecPair, SPDY3CodecPair>;
3076 INSTANTIATE_TYPED_TEST_CASE_P(AllTypesPrefix, HTTPUpstreamTest, AllTypes);
3077