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 "folly/Expected.h"
10 #include <proxygen/lib/http/session/HQDownstreamSession.h>
11
12 #include <folly/io/async/EventBaseManager.h>
13 #include <proxygen/lib/http/codec/HQControlCodec.h>
14 #include <proxygen/lib/http/codec/HQStreamCodec.h>
15 #include <proxygen/lib/http/codec/HQUnidirectionalCodec.h>
16 #include <proxygen/lib/http/codec/HTTP1xCodec.h>
17 #include <proxygen/lib/http/session/test/HQDownstreamSessionTest.h>
18 #include <proxygen/lib/http/session/test/HQSessionMocks.h>
19 #include <proxygen/lib/http/session/test/HTTPSessionMocks.h>
20 #include <proxygen/lib/http/session/test/HTTPTransactionMocks.h>
21 #include <proxygen/lib/http/session/test/MockQuicSocketDriver.h>
22 #include <proxygen/lib/http/session/test/TestUtils.h>
23 #include <quic/api/test/MockQuicSocket.h>
24 #include <quic/dsr/Types.h>
25 #include <wangle/acceptor/ConnectionManager.h>
26
27 #include <folly/futures/Future.h>
28 #include <folly/portability/GTest.h>
29
30 using namespace proxygen;
31 using namespace proxygen::hq;
32 using namespace quic;
33 using namespace folly;
34 using namespace testing;
35 using namespace std::chrono;
36
37 // Use this test class for h1q-fb only tests
38 using HQDownstreamSessionTestH1q = HQDownstreamSessionTest;
39 // Use this test class for h1q-fb-v1 only tests
40 using HQDownstreamSessionTestH1qv1 = HQDownstreamSessionTest;
41 // Use this test class for h1q-fb-v2/hq common tests (goaway)
42 using HQDownstreamSessionTestH1qv2HQ = HQDownstreamSessionTest;
43
44 // Use this test class for hq only tests
45 using HQDownstreamSessionTestHQ = HQDownstreamSessionTest;
46 using HQDownstreamSessionTestHQDeliveryAck = HQDownstreamSessionTest;
47
48 // Use this test class for h3 server push tests
49 using HQDownstreamSessionTestHQPush = HQDownstreamSessionTest;
50
51 namespace {
getProgressiveGetRequest()52 HTTPMessage getProgressiveGetRequest() {
53 auto req = proxygen::getGetRequest();
54 req.getHeaders().add(HTTP_HEADER_PRIORITY, "u=1, i");
55 return req;
56 }
57 } // namespace
58
sendRequest(const std::string & url,int8_t priority,bool eom)59 HTTPCodec::StreamID HQDownstreamSessionTest::sendRequest(const std::string& url,
60 int8_t priority,
61 bool eom) {
62 auto req = proxygen::getGetRequest();
63 req.setURL(url);
64 req.setPriority(priority);
65 return sendRequest(req, eom);
66 }
67
nextStreamId()68 quic::StreamId HQDownstreamSessionTest::nextStreamId() {
69 auto id = nextStreamId_;
70 nextStreamId_ += 4;
71 return id;
72 }
73
sendRequest(const proxygen::HTTPMessage & req,bool eom,quic::StreamId id)74 quic::StreamId HQDownstreamSessionTest::sendRequest(
75 const proxygen::HTTPMessage& req, bool eom, quic::StreamId id) {
76 if (id == quic::kEightByteLimit) {
77 id = nextStreamId();
78 }
79 auto res = requests_.emplace(std::piecewise_construct,
80 std::forward_as_tuple(id),
81 std::forward_as_tuple(makeCodec(id)));
82 auto& request = res.first->second;
83 request.id = request.codec->createStream();
84 request.readEOF = eom;
85 request.codec->generateHeader(request.buf, request.id, req, eom);
86 return id;
87 }
88
sendHeader()89 quic::StreamId HQDownstreamSessionTest::sendHeader() {
90 return sendRequest("/", 0, false);
91 }
92
sendRequestLater(proxygen::HTTPMessage req,bool eof)93 folly::Promise<folly::Unit> HQDownstreamSessionTest::sendRequestLater(
94 proxygen::HTTPMessage req, bool eof) {
95 folly::Promise<folly::Unit> reqp;
96 reqp.getSemiFuture().via(&eventBase_).thenValue([=](auto&&) {
97 auto id = sendRequest(req, eof);
98 socketDriver_->addReadEvent(
99 id, getStream(id).buf.move(), std::chrono::milliseconds(0));
100 socketDriver_->addReadEOF(id, std::chrono::milliseconds(0));
101 // note that eof=true used to terminate the connection and now it
102 // no longer does
103 });
104 return reqp;
105 }
106
SetUp()107 void HQDownstreamSessionTest::SetUp() {
108 SetUpBase();
109 SetUpOnTransportReady();
110 }
111
TearDown()112 void HQDownstreamSessionTest::TearDown() {
113 if (!IS_H1Q_FB_V1) {
114 // with these versions we need to wait for GOAWAY delivery on the control
115 // stream
116 eventBase_.loop();
117 }
118 }
119
SetUpBase()120 void HQDownstreamSessionTest::SetUpBase() {
121 HQSessionTest::SetUp();
122 streamTransInfo_ = {.totalHeadOfLineBlockedTime =
123 std::chrono::milliseconds(100),
124 .holbCount = 2,
125 .isHolb = true};
126
127 EXPECT_CALL(*socketDriver_->getSocket(), getStreamTransportInfo(testing::_))
128 .WillRepeatedly(testing::Return(streamTransInfo_));
129 }
130
SetUpOnTransportReady()131 void HQDownstreamSessionTest::SetUpOnTransportReady() {
132 hqSession_->onTransportReady();
133
134 if (createControlStreams()) {
135 eventBase_.loopOnce();
136 if (IS_HQ) {
137 EXPECT_EQ(httpCallbacks_.settings, 1);
138 }
139 }
140 }
141
142 template <class HandlerType>
143 std::unique_ptr<testing::StrictMock<HandlerType>>
addSimpleStrictHandlerBase()144 HQDownstreamSessionTest::addSimpleStrictHandlerBase() {
145 auto handler = std::make_unique<testing::StrictMock<HandlerType>>();
146
147 // The ownership model here is suspect, but assume the callers won't destroy
148 // handler before it's requested
149 auto rawHandler = handler.get();
150 EXPECT_CALL(getMockController(), getRequestHandler(testing::_, testing::_))
151 .WillOnce(testing::Return(rawHandler))
152 .RetiresOnSaturation();
153
154 EXPECT_CALL(*handler, setTransaction(testing::_))
155 .WillOnce(testing::SaveArg<0>(&handler->txn_));
156
157 return handler;
158 }
159
160 std::unique_ptr<testing::StrictMock<proxygen::MockHTTPHandler>>
addSimpleStrictHandler()161 HQDownstreamSessionTest::addSimpleStrictHandler() {
162 return addSimpleStrictHandlerBase<proxygen::MockHTTPHandler>();
163 }
164
165 std::pair<quic::StreamId,
166 std::unique_ptr<testing::StrictMock<proxygen::MockHTTPHandler>>>
checkRequest(proxygen::HTTPMessage req)167 HQDownstreamSessionTest::checkRequest(proxygen::HTTPMessage req) {
168 auto id = sendRequest(req);
169 auto handler = addSimpleStrictHandler();
170 handler->expectHeaders();
171 handler->expectEOM(
172 [hdlr = handler.get()] { hdlr->sendReplyWithBody(200, 100); });
173 handler->expectDetachTransaction();
174 return {id, std::move(handler)};
175 }
176
flushRequestsAndWaitForReads(bool eof,std::chrono::milliseconds eofDelay,std::chrono::milliseconds initialDelay,std::function<void ()> extraEventsFn)177 void HQDownstreamSessionTest::flushRequestsAndWaitForReads(
178 bool eof,
179 std::chrono::milliseconds eofDelay,
180 std::chrono::milliseconds initialDelay,
181 std::function<void()> extraEventsFn) {
182 while (!flushRequests(eof, eofDelay, initialDelay, extraEventsFn)) {
183 CHECK(eventBase_.loop());
184 }
185 CHECK(eventBase_.loop());
186 }
187
flushRequestsAndLoop(bool eof,std::chrono::milliseconds eofDelay,std::chrono::milliseconds initialDelay,std::function<void ()> extraEventsFn)188 void HQDownstreamSessionTest::flushRequestsAndLoop(
189 bool eof,
190 std::chrono::milliseconds eofDelay,
191 std::chrono::milliseconds initialDelay,
192 std::function<void()> extraEventsFn) {
193 flushRequests(eof, eofDelay, initialDelay, extraEventsFn);
194 CHECK(eventBase_.loop());
195 }
196
flushRequestsAndLoopN(uint64_t n,bool eof,std::chrono::milliseconds eofDelay,std::chrono::milliseconds initialDelay,std::function<void ()> extraEventsFn)197 void HQDownstreamSessionTest::flushRequestsAndLoopN(
198 uint64_t n,
199 bool eof,
200 std::chrono::milliseconds eofDelay,
201 std::chrono::milliseconds initialDelay,
202 std::function<void()> extraEventsFn) {
203 flushRequests(eof, eofDelay, initialDelay, extraEventsFn);
204 for (uint64_t i = 0; i < n; i++) {
205 eventBase_.loopOnce();
206 }
207 }
208
flushRequests(bool eof,std::chrono::milliseconds eofDelay,std::chrono::milliseconds initialDelay,std::function<void ()> extraEventsFn)209 bool HQDownstreamSessionTest::flushRequests(
210 bool eof,
211 std::chrono::milliseconds eofDelay,
212 std::chrono::milliseconds initialDelay,
213 std::function<void()> extraEventsFn) {
214 bool done = true;
215
216 if (!encoderWriteBuf_.empty()) {
217 socketDriver_->addReadEvent(
218 kQPACKEncoderIngressStreamId, encoderWriteBuf_.move(), initialDelay);
219 initialDelay = std::chrono::milliseconds(0);
220 }
221 for (auto& req : requests_) {
222 if (socketDriver_->isStreamIdle(req.first)) {
223 continue;
224 }
225 if (req.second.buf.chainLength() > 0) {
226 socketDriver_->addReadEvent(
227 req.first, req.second.buf.move(), initialDelay);
228 done = false;
229 }
230 // EOM -> stream EOF
231 if (req.second.readEOF) {
232 socketDriver_->addReadEOF(req.first, eofDelay);
233 done = false;
234 }
235 }
236 if (extraEventsFn) {
237 extraEventsFn();
238 }
239 if (eof || eofDelay.count() > 0) {
240 /* wonkiness. Should somehow close the connection?
241 * socketDriver_->addReadEOF(1, eofDelay);
242 */
243 }
244 return done;
245 }
246
247 testing::StrictMock<proxygen::MockController>&
getMockController()248 HQDownstreamSessionTest::getMockController() {
249 return controllerContainer_.mockController;
250 }
251
makeCodec(proxygen::HTTPCodec::StreamID id)252 std::unique_ptr<proxygen::HTTPCodec> HQDownstreamSessionTest::makeCodec(
253 proxygen::HTTPCodec::StreamID id) {
254 if (IS_HQ) {
255 return std::make_unique<proxygen::hq::HQStreamCodec>(
256 id,
257 proxygen::TransportDirection::UPSTREAM,
258 qpackCodec_,
259 encoderWriteBuf_,
260 decoderWriteBuf_,
261 [] { return std::numeric_limits<uint64_t>::max(); },
262 ingressSettings_);
263 } else {
264 return std::make_unique<proxygen::HTTP1xCodec>(
265 proxygen::TransportDirection::UPSTREAM, true);
266 }
267 }
268
getStream(proxygen::HTTPCodec::StreamID id)269 HQDownstreamSessionTest::ClientStream& HQDownstreamSessionTest::getStream(
270 proxygen::HTTPCodec::StreamID id) {
271 auto it = requests_.find(id);
272 CHECK(it != requests_.end());
273 return it->second;
274 }
275
expectTransactionTimeout(testing::StrictMock<proxygen::MockHTTPHandler> & handler,folly::Function<void ()> fn)276 void HQDownstreamSessionTest::expectTransactionTimeout(
277 testing::StrictMock<proxygen::MockHTTPHandler>& handler,
278 folly::Function<void()> fn) {
279 EXPECT_CALL(getMockController(),
280 getTransactionTimeoutHandler(testing::_, testing::_))
281 .WillOnce(testing::Return(&handler));
282 EXPECT_CALL(handler, setTransaction(testing::_))
283 .WillOnce(testing::SaveArg<0>(&handler.txn_));
284 handler.expectError(
285 [&handler, &fn](const proxygen::HTTPException& ex) mutable {
286 if (fn) {
287 fn();
288 }
289 EXPECT_FALSE(ex.hasHttpStatusCode());
290 handler.sendHeaders(408, 100);
291 handler.sendBody(100);
292 handler.sendEOM();
293 });
294 handler.expectDetachTransaction();
295 }
296
TEST_P(HQDownstreamSessionTest,GetMaxPushIdOK)297 TEST_P(HQDownstreamSessionTest, GetMaxPushIdOK) {
298 folly::Optional<hq::PushId> expectedId = hqSession_->getMaxAllowedPushId();
299 EXPECT_EQ(expectedId, folly::none);
300 hqSession_->closeWhenIdle();
301 }
302
TEST_P(HQDownstreamSessionTest,SimpleGet)303 TEST_P(HQDownstreamSessionTest, SimpleGet) {
304 auto idh = checkRequest();
305 flushRequestsAndLoop();
306 EXPECT_GT(socketDriver_->streams_[idh.first].writeBuf.chainLength(), 110);
307 EXPECT_TRUE(socketDriver_->streams_[idh.first].writeEOF);
308 if (IS_HQ) {
309 // Checks that the server response is sent using the QPACK dynamic table
310 CHECK_GE(qpackCodec_.getCompressionInfo().ingress.headerTableSize_, 0);
311 }
312 hqSession_->closeWhenIdle();
313 }
314
TEST_P(HQDownstreamSessionTest,PriorityUpdateIntoTransport)315 TEST_P(HQDownstreamSessionTest, PriorityUpdateIntoTransport) {
316 if (!IS_HQ) { // H1Q tests do not support priority
317 hqSession_->closeWhenIdle();
318 return;
319 }
320 auto request = getProgressiveGetRequest();
321 sendRequest(request);
322 auto handler = addSimpleStrictHandler();
323 EXPECT_CALL(*socketDriver_->getSocket(), setStreamPriority(_, 1, true));
324 handler->expectHeaders();
325 handler->expectEOM([&]() {
326 auto resp = makeResponse(200, 0);
327 std::get<0>(resp)->getHeaders().add(HTTP_HEADER_PRIORITY, "u=2");
328 EXPECT_CALL(*socketDriver_->getSocket(), setStreamPriority(_, 2, false))
329 .Times(1);
330 handler->sendRequest(*std::get<0>(resp));
331 });
332 handler->expectDetachTransaction();
333 flushRequestsAndLoop();
334 hqSession_->closeWhenIdle();
335 }
336
TEST_P(HQDownstreamSessionTest,ReplyResponsePriority)337 TEST_P(HQDownstreamSessionTest, ReplyResponsePriority) {
338 if (!IS_HQ) { // H1Q tests do not support priority
339 hqSession_->closeWhenIdle();
340 return;
341 }
342 auto request = getProgressiveGetRequest();
343 sendRequest(request);
344 auto handler = addSimpleStrictHandler();
345 EXPECT_CALL(*socketDriver_->getSocket(), setStreamPriority(_, 1, true))
346 .Times(1);
347 handler->expectHeaders();
348 handler->expectEOM([&]() {
349 auto resp = makeResponse(200, 0);
350 EXPECT_CALL(*socketDriver_->getSocket(), getStreamPriority(_))
351 .Times(1)
352 .WillOnce(Return(quic::Priority(1, true)));
353 handler->sendRequest(*std::get<0>(resp));
354 });
355 handler->expectDetachTransaction();
356 flushRequestsAndLoop();
357 hqSession_->closeWhenIdle();
358 }
359
TEST_P(HQDownstreamSessionTestHQPush,PushPriority)360 TEST_P(HQDownstreamSessionTestHQPush, PushPriority) {
361 sendRequest("/", 1);
362 HTTPMessage promiseReq, parentResp;
363 promiseReq.getHeaders().set(HTTP_HEADER_HOST, "www.foo.com");
364 promiseReq.setURL("/");
365 promiseReq.setHTTPPriority(0, false);
366
367 parentResp.setStatusCode(200);
368 parentResp.setStatusMessage("Ohai");
369
370 HTTPMessage pushResp(parentResp);
371 pushResp.setHTTPPriority(1, false);
372
373 auto handler = addSimpleStrictHandler();
374 StrictMock<MockHTTPPushHandler> pushHandler;
375 handler->expectHeaders();
376 HTTPCodec::StreamID pushStreamId = 0;
377 handler->expectEOM([&] {
378 EXPECT_CALL(*socketDriver_->getSocket(),
379 setStreamPriority(handler->txn_->getID(), _, _))
380 .Times(0);
381 handler->txn_->sendHeaders(parentResp);
382 handler->txn_->sendBody(makeBuf(100));
383
384 auto outgoingStreams = hqSession_->getNumOutgoingStreams();
385 auto* pushTxn = handler->txn_->newPushedTransaction(&pushHandler);
386 ASSERT_NE(pushTxn, nullptr);
387 EXPECT_EQ(hqSession_->getNumOutgoingStreams(), outgoingStreams + 1);
388 // PushPromise doesn't update parent streram's priority. It does update push
389 // stream priority
390 EXPECT_CALL(*socketDriver_->getSocket(),
391 setStreamPriority(handler->txn_->getID(), _, _))
392 .Times(0);
393 EXPECT_CALL(*socketDriver_->getSocket(),
394 setStreamPriority(pushTxn->getID(), 0, false))
395 .Times(1);
396 pushTxn->sendHeaders(promiseReq);
397 pushStreamId = pushTxn->getID();
398 EXPECT_CALL(*socketDriver_->getSocket(),
399 setStreamPriority(pushStreamId, 1, false))
400 .Times(1);
401 pushTxn->sendHeaders(pushResp);
402 pushTxn->sendBody(makeBuf(200));
403 pushTxn->sendEOM();
404 });
405 EXPECT_CALL(pushHandler, setTransaction(_))
406 .WillOnce(Invoke([&](HTTPTransaction* txn) { pushHandler.txn_ = txn; }));
407 EXPECT_CALL(pushHandler, detachTransaction());
408
409 flushRequestsAndLoopN(1);
410 handler->txn_->sendEOM();
411 handler->expectDetachTransaction();
412 flushRequestsAndLoop();
413 hqSession_->closeWhenIdle();
414 }
415
TEST_P(HQDownstreamSessionTest,OnPriorityCallback)416 TEST_P(HQDownstreamSessionTest, OnPriorityCallback) {
417 if (!IS_HQ) { // H1Q tests do not support priority
418 hqSession_->closeWhenIdle();
419 return;
420 }
421 // Simulate priority arriving too early, connection still valid
422 // this is going to be stored and applied when receiving headers
423 hqSession_->onPriority(0, HTTPPriority(3, false));
424 EXPECT_CALL(*socketDriver_->getSocket(), setStreamPriority(0, 3, false));
425 auto id = sendRequest(getProgressiveGetRequest());
426 CHECK_EQ(id, 0);
427 auto handler = addSimpleStrictHandler();
428 handler->expectHeaders([&]() {
429 handler->sendHeaders(200, 1000);
430 // Priority update on the stream
431 EXPECT_CALL(*socketDriver_->getSocket(), setStreamPriority(0, 2, true));
432 hqSession_->onPriority(id, HTTPPriority(2, true));
433 handler->sendBody(1000);
434 handler->sendEOM();
435 });
436 handler->expectEOM();
437 handler->expectDetachTransaction();
438 flushRequestsAndLoop();
439 // Priority on a stream we can't find - no-op
440 hqSession_->onPriority(id, HTTPPriority(4, true));
441 hqSession_->closeWhenIdle();
442 }
443
TEST_P(HQDownstreamSessionTest,GetStopSending)444 TEST_P(HQDownstreamSessionTest, GetStopSending) {
445 auto id = sendRequest(getGetRequest());
446 auto handler = addSimpleStrictHandler();
447 handler->expectHeaders();
448 handler->expectEOM([hdlr = handler.get()] { hdlr->sendHeaders(200, 100); });
449 handler->expectError([](const proxygen::HTTPException& ex) {
450 EXPECT_EQ(ex.getCodecStatusCode(), ErrorCode::CANCEL);
451 EXPECT_EQ(ex.getProxygenError(), kErrorStreamAbort);
452 });
453 handler->expectDetachTransaction();
454 flushRequestsAndLoopN(1);
455 socketDriver_->addStopSending(id, HTTP3::ErrorCode::HTTP_REQUEST_CANCELLED);
456 flushRequestsAndLoop();
457 hqSession_->closeWhenIdle();
458 }
459
TEST_P(HQDownstreamSessionTest,HttpRateLimitNormal)460 TEST_P(HQDownstreamSessionTest, HttpRateLimitNormal) {
461 // The rate-limiting code grabs the event base from the EventBaseManager,
462 // so we need to set it.
463 folly::EventBaseManager::get()->setEventBase(&eventBase_, false);
464 uint32_t rspLengthBytes = 100000;
465
466 // make sure we are not limited by connection flow control
467 socketDriver_->getSocket()->setConnectionFlowControlWindow(rspLengthBytes *
468 2);
469 // Create a request
470 auto id = sendRequest();
471
472 // Set a low rate-limit on the transaction
473 auto handler1 = addSimpleStrictHandler();
474 handler1->expectHeaders([&] {
475 uint32_t rateLimit_kbps = 640;
476 handler1->txn_->setEgressRateLimit(rateLimit_kbps * 1024);
477 });
478 // Send a somewhat big response that we know will get rate-limited
479 handler1->expectEOM([&] {
480 // At 640kbps, this should take slightly over 800ms
481 handler1->sendHeaders(200, rspLengthBytes);
482 handler1->sendBody(rspLengthBytes);
483 });
484 EXPECT_CALL(*handler1, onEgressPaused()).Times(AtLeast(1));
485 handler1->expectEgressResumed([&handler1] { handler1->txn_->sendEOM(); });
486 handler1->expectDetachTransaction();
487 flushRequestsAndLoop();
488
489 // Check that the write side got blocked
490 socketDriver_->expectStreamWritesPaused(id);
491 // Open flow control again
492 socketDriver_->getSocket()->setStreamFlowControlWindow(id,
493 rspLengthBytes * 2);
494 flushRequestsAndLoop();
495
496 hqSession_->closeWhenIdle();
497 }
498
TEST_P(HQDownstreamSessionTest,SimplePost)499 TEST_P(HQDownstreamSessionTest, SimplePost) {
500 auto id = sendRequest(getPostRequest(10), false);
501 auto& request = getStream(id);
502 request.codec->generateBody(
503 request.buf, request.id, makeBuf(10), HTTPCodec::NoPadding, true);
504 request.readEOF = true;
505 auto handler = addSimpleStrictHandler();
506 handler->expectHeaders();
507 handler->expectBody(); // should check length too but meh
508 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
509 handler->expectDetachTransaction();
510 flushRequestsAndLoop();
511 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
512 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
513 hqSession_->closeWhenIdle();
514 }
515
516 // HQ doesn't have the notion of chunked
TEST_P(HQDownstreamSessionTestH1q,ChunkedPost)517 TEST_P(HQDownstreamSessionTestH1q, ChunkedPost) {
518 InSequence enforceOrder;
519
520 auto id = sendRequest(getChunkedPostRequest(), false);
521 auto& request = getStream(id);
522 auto handler = addSimpleStrictHandler();
523 handler->expectHeaders();
524 for (int i = 1; i <= 3; i++) {
525 auto size = 10 * i;
526 request.codec->generateChunkHeader(request.buf, request.id, size);
527 handler->expectChunkHeader();
528 request.codec->generateBody(
529 request.buf, request.id, makeBuf(size), HTTPCodec::NoPadding, false);
530 handler->expectBody([size](uint64_t, std::shared_ptr<folly::IOBuf> buf) {
531 EXPECT_EQ(size, buf->length());
532 });
533 request.codec->generateChunkTerminator(request.buf, request.id);
534 handler->expectChunkComplete();
535 }
536 request.codec->generateEOM(request.buf, request.id);
537 request.readEOF = true;
538 handler->expectEOM([&handler] {
539 // Chunked Transfer Encoding for the response too
540 handler->sendChunkedReplyWithBody(200, 400, 100, false);
541 });
542 handler->expectDetachTransaction();
543 flushRequestsAndLoop();
544 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
545 hqSession_->closeWhenIdle();
546 }
547
TEST_P(HQDownstreamSessionTest,SimpleGetEofDelay)548 TEST_P(HQDownstreamSessionTest, SimpleGetEofDelay) {
549 auto idh = checkRequest();
550 flushRequestsAndLoop(false, std::chrono::milliseconds(10));
551 EXPECT_GT(socketDriver_->streams_[idh.first].writeBuf.chainLength(), 110);
552 EXPECT_TRUE(socketDriver_->streams_[idh.first].writeEOF);
553 hqSession_->closeWhenIdle();
554 }
555
TEST_P(HQDownstreamSessionTest,UnfinishedPost)556 TEST_P(HQDownstreamSessionTest, UnfinishedPost) {
557 auto id = sendRequest(getPostRequest(10), false);
558 auto& request = getStream(id);
559 request.codec->generateBody(
560 request.buf, request.id, makeBuf(9), HTTPCodec::NoPadding, true);
561 request.readEOF = true;
562 auto handler = addSimpleStrictHandler();
563 handler->expectHeaders();
564 handler->expectBody();
565 handler->expectError([this, &handler](const proxygen::HTTPException& ex) {
566 if (IS_HQ) {
567 // The HTTP/1.1 parser tracks content-length and 400's if it is short
568 // The HQStreamCodec does no such thing, and it's caught by
569 // HTTPTransaction, with a different error.
570 EXPECT_EQ(ex.getProxygenError(), kErrorParseBody);
571 } else {
572 EXPECT_TRUE(ex.hasHttpStatusCode());
573 EXPECT_EQ(ex.getHttpStatusCode(), 400);
574 }
575 handler->sendReplyWithBody(400, 100);
576 // afrind: this logic is in HTTPSession so should move to base or
577 // duplicate in HQSession (see also custom error handlers)
578 });
579 handler->expectDetachTransaction();
580 flushRequestsAndLoop();
581 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
582 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
583 hqSession_->dropConnection();
584 }
585
586 // This is a bit weird. Extra junk after an HTTP/1.1 message now gets ignored
587 // until more junk or an EOF arrives. Had to split the test into two loops.
TEST_P(HQDownstreamSessionTestH1qv1,TwoMessages)588 TEST_P(HQDownstreamSessionTestH1qv1, TwoMessages) {
589 auto id = sendRequest(getGetRequest(), false);
590 auto handler = addSimpleStrictHandler();
591 handler->expectHeaders();
592 flushRequestsAndLoopN(1);
593
594 // add a second request to the stream with Connection: close
595 auto& request = getStream(id);
596 auto req2 = getGetRequest();
597 req2.getHeaders().add(HTTP_HEADER_CONNECTION, "close");
598 request.codec->generateHeader(request.buf, request.id, req2, true);
599 request.readEOF = true;
600 hqSession_->notifyPendingShutdown();
601 handler->expectError(
602 [&handler](const HTTPException&) { handler->txn_->sendAbort(); });
603 handler->expectDetachTransaction();
604 flushRequestsAndLoop();
605 hqSession_->closeWhenIdle();
606 }
607
TEST_P(HQDownstreamSessionTest,Multiplexing)608 TEST_P(HQDownstreamSessionTest, Multiplexing) {
609 std::vector<std::unique_ptr<StrictMock<MockHTTPHandler>>> handlers;
610 for (auto n = 0; n < 10; n++) {
611 auto idh = checkRequest();
612 handlers.emplace_back(std::move(idh.second));
613 }
614 flushRequestsAndWaitForReads();
615 for (auto& req : requests_) {
616 EXPECT_GT(socketDriver_->streams_[req.first].writeBuf.chainLength(), 110);
617 EXPECT_TRUE(socketDriver_->streams_[req.first].writeEOF);
618 }
619 hqSession_->closeWhenIdle();
620 }
621
TEST_P(HQDownstreamSessionTest,Maxreadsperloop)622 TEST_P(HQDownstreamSessionTest, Maxreadsperloop) {
623 std::vector<std::unique_ptr<StrictMock<MockHTTPHandler>>> handlers;
624 for (auto n = 0; n < 20; n++) {
625 auto idh = checkRequest();
626 handlers.emplace_back(std::move(idh.second));
627 }
628
629 flushRequestsAndLoopN(1);
630 // After one loop, reads on some streams will be idle
631 // while on some other they will not
632 int idleCount = 0;
633 int nonIdleCount = 0;
634 for (auto& req : requests_) {
635 if (socketDriver_->isStreamIdle(req.first)) {
636 idleCount++;
637 } else {
638 nonIdleCount++;
639 }
640 }
641 EXPECT_GT(idleCount, 0);
642 EXPECT_GT(nonIdleCount, 0);
643
644 // Now finish all the reads
645 eventBase_.loop();
646 for (auto& req : requests_) {
647 EXPECT_GT(socketDriver_->streams_[req.first].writeBuf.chainLength(), 110);
648 EXPECT_TRUE(socketDriver_->streams_[req.first].writeEOF);
649 }
650 hqSession_->closeWhenIdle();
651 }
652
TEST_P(HQDownstreamSessionTest,OnFlowControlUpdate)653 TEST_P(HQDownstreamSessionTest, OnFlowControlUpdate) {
654 auto id = sendRequest();
655 auto handler = addSimpleStrictHandler();
656 handler->expectHeaders();
657 handler->expectEOM([&handler] {
658 handler->sendHeaders(200, 100);
659 handler->txn_->sendBody(makeBuf(100));
660 });
661 handler->expectEgressPaused();
662 handler->expectEgressResumed([&handler] { handler->txn_->sendEOM(); });
663 handler->expectDetachTransaction();
664
665 // Initialize the flow control window to less than the response body
666 socketDriver_->setStreamFlowControlWindow(id, 10);
667 flushRequestsAndLoop();
668 // Check that the write side got blocked
669 socketDriver_->expectStreamWritesPaused(id);
670 // Open the flow control window
671 socketDriver_->getSocket()->setStreamFlowControlWindow(id, 200);
672 CHECK(eventBase_.loop());
673 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
674 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
675 hqSession_->closeWhenIdle();
676 }
677
TEST_P(HQDownstreamSessionTest,OnFlowControlUpdateOnUnknownStream)678 TEST_P(HQDownstreamSessionTest, OnFlowControlUpdateOnUnknownStream) {
679 auto id = sendRequest();
680 auto handler = addSimpleStrictHandler();
681 handler->expectHeaders();
682 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
683 handler->expectDetachTransaction();
684
685 // Call flowControlUpdate on a stream the Application doesn't know
686 socketDriver_->sock_->connCb_->onFlowControlUpdate(id + 4);
687 flushRequestsAndLoop();
688 hqSession_->closeWhenIdle();
689 }
690
691 // This test does not work with header compression
TEST_P(HQDownstreamSessionTest,OnConnectionWindowPartialHeaders)692 TEST_P(HQDownstreamSessionTest, OnConnectionWindowPartialHeaders) {
693 // Only enough conn window to send headers initially.
694 auto id = sendRequest();
695 auto handler = addSimpleStrictHandler();
696 handler->expectHeaders();
697 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
698 handler->expectDetachTransaction();
699
700 // Initialize the flow control window to less than the response body
701 socketDriver_->setConnectionFlowControlWindow(10 + numCtrlStreams_);
702 flushRequestsAndLoop();
703 // Check that the write side got blocked
704 socketDriver_->expectConnWritesPaused();
705 if (!IS_HQ) {
706 // We should have 10 bytes pending to be written out.
707 EXPECT_GE(socketDriver_->streams_[id].writeBuf.chainLength(), 10);
708 } else {
709 // We should have some bytes pending to be written out in the QPACK Encoder
710 // stream
711 EXPECT_GT(socketDriver_->streams_[kQPACKEncoderEgressStreamId]
712 .writeBuf.chainLength(),
713 0);
714 }
715 EXPECT_FALSE(socketDriver_->streams_[id].writeEOF);
716 // Open the flow control window
717 socketDriver_->getSocket()->setConnectionFlowControlWindow(200);
718 CHECK(eventBase_.loop());
719 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
720 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
721 hqSession_->closeWhenIdle();
722 }
723
TEST_P(HQDownstreamSessionTest,OnConnectionWindowPartialBody)724 TEST_P(HQDownstreamSessionTest, OnConnectionWindowPartialBody) {
725 flushRequestsAndLoop(); // loop once for SETTINGS, etc
726 // Only enough conn window to send headers initially.
727 auto id = sendRequest();
728 auto handler = addSimpleStrictHandler();
729 handler->expectHeaders();
730 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
731 // TODO: we should probably pause egress on conn limited.
732 // handler->expectEgressPaused();
733 // handler->expectEgressResumed();
734 handler->expectDetachTransaction();
735
736 // Initialize the flow control window to less than the response body
737 socketDriver_->setConnectionFlowControlWindow(110 + numCtrlStreams_);
738 flushRequestsAndLoop();
739 // Check that the write side got blocked
740 socketDriver_->expectConnWritesPaused();
741 if (!IS_HQ) {
742 // We should have 110 bytes pending to be written out.
743 EXPECT_GE(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
744 } else {
745 // We should have some bytes pending to be written out in the QPACK Encoder
746 // stream
747 EXPECT_GT(socketDriver_->streams_[kQPACKEncoderEgressStreamId]
748 .writeBuf.chainLength(),
749 0);
750 EXPECT_GT(qpackCodec_.getCompressionInfo().egress.headerTableSize_, 0);
751 }
752 EXPECT_FALSE(socketDriver_->streams_[id].writeEOF);
753 // Open the flow control window
754 socketDriver_->getSocket()->setConnectionFlowControlWindow(200 +
755 numCtrlStreams_);
756 CHECK(eventBase_.loop());
757 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
758 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
759 hqSession_->closeWhenIdle();
760 }
761
TEST_P(HQDownstreamSessionTest,SeparateEom)762 TEST_P(HQDownstreamSessionTest, SeparateEom) {
763 // Only enough conn window to send headers initially.
764 auto id = sendRequest();
765 auto handler = addSimpleStrictHandler();
766 handler->expectHeaders();
767 handler->expectEOM([&handler] {
768 handler->sendHeaders(200, 100);
769 handler->sendBody(100);
770 });
771 handler->expectDetachTransaction();
772 flushRequestsAndLoop();
773 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
774 EXPECT_FALSE(socketDriver_->streams_[id].writeEOF);
775
776 handler->sendEOM();
777 // Open the flow control window
778 CHECK(eventBase_.loop());
779 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
780 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
781 hqSession_->closeWhenIdle();
782 }
783
getSimpleRequestData()784 std::unique_ptr<folly::IOBuf> getSimpleRequestData() {
785 std::string req("GET / HTTP/1.1\nHost: www.facebook.com\n\n");
786 return folly::IOBuf::copyBuffer(req);
787 }
788
estimateResponseSize(bool isHq,HTTPMessage msg,size_t contentLength,size_t chunkSize)789 std::tuple<size_t, size_t, size_t> estimateResponseSize(bool isHq,
790 HTTPMessage msg,
791 size_t contentLength,
792 size_t chunkSize) {
793 folly::IOBufQueue estimateSizeBuf{folly::IOBufQueue::cacheChainLength()};
794 std::unique_ptr<HTTPCodec> codec;
795 QPACKCodec qpackCodec;
796 folly::IOBufQueue encoderWriteBuf{folly::IOBufQueue::cacheChainLength()};
797 folly::IOBufQueue decoderWriteBuf{folly::IOBufQueue::cacheChainLength()};
798 HTTPSettings dummySettings;
799 qpackCodec.setEncoderHeaderTableSize(kQPACKTestDecoderMaxTableSize);
800 if (isHq) {
801 codec = std::make_unique<hq::HQStreamCodec>(
802 0,
803 TransportDirection::DOWNSTREAM,
804 qpackCodec,
805 encoderWriteBuf,
806 decoderWriteBuf,
807 [] { return std::numeric_limits<uint64_t>::max(); },
808 dummySettings);
809 } else {
810 codec = std::make_unique<HTTP1xCodec>(TransportDirection::DOWNSTREAM, true);
811 }
812
813 MockHTTPCodecCallback callback;
814 codec->setCallback(&callback);
815 auto txn = codec->createStream();
816
817 if (!isHq) {
818 EXPECT_CALL(callback, onHeadersComplete(_, _));
819 EXPECT_CALL(callback, onMessageBegin(_, _));
820 codec->onIngress(*getSimpleRequestData());
821 }
822
823 codec->generateHeader(estimateSizeBuf, txn, msg);
824 size_t currentLength = contentLength;
825
826 bool chunking = (chunkSize != 0);
827 if (!chunking) {
828 chunkSize = std::numeric_limits<size_t>::max();
829 }
830 auto currentSize = estimateSizeBuf.chainLength();
831 while (currentLength > 0) {
832 uint32_t toSend = std::min(currentLength, chunkSize);
833 std::vector<uint8_t> buf;
834 buf.resize(toSend, 'a');
835 if (chunking) {
836 codec->generateChunkHeader(estimateSizeBuf, txn, toSend);
837 }
838 codec->generateBody(estimateSizeBuf,
839 txn,
840 folly::IOBuf::copyBuffer(buf),
841 HTTPCodec::NoPadding,
842 false);
843 if (chunking) {
844 codec->generateChunkTerminator(estimateSizeBuf, txn);
845 }
846 currentLength -= toSend;
847 }
848 size_t framingOverhead =
849 estimateSizeBuf.chainLength() - currentSize - contentLength;
850 currentSize = estimateSizeBuf.chainLength();
851 codec->generateEOM(estimateSizeBuf, txn);
852
853 size_t eomSize = estimateSizeBuf.chainLength() - currentSize;
854 size_t estimatedSize = estimateSizeBuf.chainLength();
855 return std::tuple<size_t, size_t, size_t>(
856 estimatedSize, framingOverhead, eomSize);
857 }
858
TEST_P(HQDownstreamSessionTest,PendingEomBuffered)859 TEST_P(HQDownstreamSessionTest, PendingEomBuffered) {
860 size_t contentLength = 100;
861 size_t chunkSize = 5;
862
863 auto reply = makeResponse(200);
864 reply->setIsChunked(true);
865 size_t estimatedSize = 0;
866 size_t framingOverhead = 0;
867 size_t eomSize = 0;
868 std::tie(estimatedSize, framingOverhead, eomSize) =
869 estimateResponseSize(IS_HQ, *reply, contentLength, chunkSize);
870 // EOMs are 0 bytes in H3, but there is framing overhead of at least two
871 // bytes.
872 auto bytesWithheld = (IS_HQ) ? 2 : eomSize;
873
874 auto id = sendRequest();
875 auto handler = addSimpleStrictHandler();
876 handler->expectHeaders();
877 handler->expectEOM([&handler, contentLength, chunkSize] {
878 handler->sendChunkedReplyWithBody(
879 200, contentLength, chunkSize, false, true);
880 });
881
882 // Set the flow control window to be less than the EOM overhead added by
883 // the codec
884 socketDriver_->setStreamFlowControlWindow(id, estimatedSize - bytesWithheld);
885 flushRequestsAndLoop();
886 CHECK(eventBase_.loop());
887 EXPECT_GE(socketDriver_->streams_[id].writeBuf.chainLength(),
888 estimatedSize - bytesWithheld);
889 EXPECT_FALSE(socketDriver_->streams_[id].writeEOF);
890
891 handler->expectDetachTransaction();
892 socketDriver_->getSocket()->setStreamFlowControlWindow(id, estimatedSize);
893
894 CHECK(eventBase_.loop());
895 EXPECT_GE(socketDriver_->streams_[id].writeBuf.chainLength(), estimatedSize);
896 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
897 hqSession_->closeWhenIdle();
898 }
899
TEST_P(HQDownstreamSessionTest,PendingEomQueuedNotFlushed)900 TEST_P(HQDownstreamSessionTest, PendingEomQueuedNotFlushed) {
901 auto reply = makeResponse(200);
902 reply->setWantsKeepalive(true);
903 reply->getHeaders().add(HTTP_HEADER_CONTENT_LENGTH,
904 folly::to<std::string>(1));
905 size_t estimatedSize = 0;
906 size_t framingOverhead = 0;
907 size_t eomSize = 0;
908 std::tie(estimatedSize, framingOverhead, eomSize) =
909 estimateResponseSize(IS_HQ, *reply, 1, 0);
910 CHECK_EQ(eomSize, 0);
911 // There's no EOM and no framing overhead for h1q, withhold the body byte
912 auto bytesWithheld = IS_HQ ? framingOverhead : 1;
913
914 auto id = sendRequest(getGetRequest());
915 auto handler = addSimpleStrictHandler();
916 handler->expectHeaders();
917 handler->expectEOM([&handler, this, id, estimatedSize, bytesWithheld] {
918 // Initialize the flow control window to just less than the
919 // estimated size of the eom codec which the codec generates..
920 socketDriver_->setStreamFlowControlWindow(id,
921 estimatedSize - bytesWithheld);
922 handler->sendReplyWithBody(200, 1);
923 });
924
925 flushRequestsAndLoop();
926 CHECK(eventBase_.loop());
927 EXPECT_GE(socketDriver_->streams_[id].writeBuf.chainLength(),
928 estimatedSize - bytesWithheld);
929 EXPECT_FALSE(socketDriver_->streams_[id].writeEOF);
930
931 handler->expectDetachTransaction();
932 socketDriver_->getSocket()->setStreamFlowControlWindow(id, estimatedSize);
933
934 CHECK(eventBase_.loop());
935 EXPECT_GE(socketDriver_->streams_[id].writeBuf.chainLength(), estimatedSize);
936 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
937 hqSession_->closeWhenIdle();
938 }
939
TEST_P(HQDownstreamSessionTestHQ,PendingEomQueuedNotFlushedConn)940 TEST_P(HQDownstreamSessionTestHQ, PendingEomQueuedNotFlushedConn) {
941 // flush control streams first
942 flushRequestsAndLoop();
943 CHECK(eventBase_.loop());
944
945 auto reply = makeResponse(200);
946 reply->setWantsKeepalive(true);
947 size_t estimatedSize = 0;
948 size_t framingOverhead = 0;
949 size_t eomSize = 0;
950 std::tie(estimatedSize, framingOverhead, eomSize) =
951 estimateResponseSize(IS_HQ, *reply, 1, 0);
952
953 // No EOM yet
954 auto id = sendRequest(getGetRequest(), false);
955 auto handler = addSimpleStrictHandler();
956 handler->expectHeaders([&handler] { handler->sendHeaders(200, 1); });
957 flushRequestsAndLoopN(1);
958
959 socketDriver_->addReadEOF(id, std::chrono::milliseconds(0));
960 handler->expectEOM([&handler, this] {
961 handler->txn_->sendBody(makeBuf(1));
962 handler->txn_->sendEOM();
963 socketDriver_->setConnectionFlowControlWindow(1);
964 });
965
966 // Set the conn flow control to be enough for the body byte but not enough
967 // for the framing overhead
968 auto remaining = framingOverhead + eomSize;
969 flushRequestsAndLoop();
970 CHECK(eventBase_.loop());
971 EXPECT_GE(socketDriver_->streams_[id].writeBuf.chainLength(),
972 estimatedSize - remaining);
973 EXPECT_FALSE(socketDriver_->streams_[id].writeEOF);
974
975 handler->expectDetachTransaction();
976 for (size_t i = 0; i < remaining; i++) {
977 socketDriver_->getSocket()->setConnectionFlowControlWindow(1);
978
979 CHECK(eventBase_.loop());
980 EXPECT_GE(socketDriver_->streams_[id].writeBuf.chainLength(),
981 estimatedSize - remaining + i);
982
983 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF != (i < remaining - 1));
984 }
985
986 // Need flow control for goaway
987 socketDriver_->getSocket()->setConnectionFlowControlWindow(100);
988 hqSession_->closeWhenIdle();
989 }
990
TEST_P(HQDownstreamSessionTest,SendEomLaterChunked)991 TEST_P(HQDownstreamSessionTest, SendEomLaterChunked) {
992 size_t contentLength = 100;
993 size_t chunkSize = 10;
994
995 auto id = sendRequest();
996 auto handler = addSimpleStrictHandler();
997 handler->expectHeaders([&handler, contentLength, chunkSize] {
998 handler->sendChunkedReplyWithBody(
999 200, contentLength, chunkSize, false, false);
1000 });
1001 handler->expectEOM([&handler] { handler->sendEOM(); });
1002 handler->expectDetachTransaction();
1003
1004 flushRequestsAndLoop();
1005 CHECK(eventBase_.loop());
1006 EXPECT_GE(socketDriver_->streams_[id].writeBuf.chainLength(), contentLength);
1007 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
1008 hqSession_->closeWhenIdle();
1009 }
1010
TEST_P(HQDownstreamSessionTest,SendEomLater)1011 TEST_P(HQDownstreamSessionTest, SendEomLater) {
1012 size_t contentLength = 100;
1013 auto id = sendRequest();
1014 auto handler = addSimpleStrictHandler();
1015 handler->expectHeaders([&handler, contentLength] {
1016 handler->sendHeaders(200, contentLength);
1017 handler->sendBody(contentLength);
1018 });
1019 handler->expectEOM([&handler] { handler->sendEOM(); });
1020 handler->expectDetachTransaction();
1021
1022 flushRequestsAndLoop();
1023 CHECK(eventBase_.loop());
1024 EXPECT_GE(socketDriver_->streams_[id].writeBuf.chainLength(), contentLength);
1025 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
1026 hqSession_->closeWhenIdle();
1027 }
1028
1029 // Invoke notifyPendingShutdown, which will include an outgoing
1030 // Connection: close header on the next outbound headers. The next incoming
1031 // request containing a Connection: close header will complete the drain state
1032 // machine
1033 // NOTE: this behavior is only valid for basic h1q
TEST_P(HQDownstreamSessionTestH1qv1,ShutdownNotify)1034 TEST_P(HQDownstreamSessionTestH1qv1, ShutdownNotify) {
1035 hqSession_->notifyPendingShutdown();
1036 EXPECT_FALSE(hqSession_->isReusable());
1037 auto idh1 = checkRequest();
1038 flushRequestsAndLoop();
1039 // we should write Connection: close in the outgoing headers
1040 auto resp =
1041 socketDriver_->streams_[idh1.first].writeBuf.move()->moveToFbString();
1042 EXPECT_TRUE(resp.find("Connection: close"));
1043
1044 // Add connection: close
1045 auto req = getGetRequest();
1046 req.getHeaders().set(HTTP_HEADER_CONNECTION, "close");
1047 auto idh2 = checkRequest(req);
1048 flushRequestsAndLoop();
1049 }
1050
1051 // closeWhenIdle on an idle conn - immediate delete
TEST_P(HQDownstreamSessionTest,ShutdownCloseIdle)1052 TEST_P(HQDownstreamSessionTest, ShutdownCloseIdle) {
1053 EXPECT_TRUE(hqSession_->isReusable());
1054 hqSession_->closeWhenIdle();
1055 }
1056
1057 // closeWhenIdle invoked when a request is open, delete happens when it finishes
TEST_P(HQDownstreamSessionTest,ShutdownCloseIdleReq)1058 TEST_P(HQDownstreamSessionTest, ShutdownCloseIdleReq) {
1059 sendRequest();
1060 auto handler = addSimpleStrictHandler();
1061 handler->expectHeaders([this] {
1062 hqSession_->closeWhenIdle();
1063 EXPECT_TRUE(hqSession_->isClosing());
1064 });
1065 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
1066 handler->expectDetachTransaction();
1067 flushRequestsAndLoop();
1068 }
1069
1070 // Peer initiates shutdown by sending Connection: close
1071 // NOTE: this behavior is only valid for basic h1q
TEST_P(HQDownstreamSessionTestH1qv1,ShutdownFromPeer)1072 TEST_P(HQDownstreamSessionTestH1qv1, ShutdownFromPeer) {
1073 // client initiates shutdown by including Connection: close
1074 auto req = getGetRequest();
1075 req.getHeaders().set(HTTP_HEADER_CONNECTION, "close");
1076 auto idh = checkRequest(req);
1077 flushRequestsAndLoop();
1078
1079 // session deleted when server emits connection: close
1080 }
1081
1082 // dropConnection invoked while a request being processed, it receives an
1083 // error
TEST_P(HQDownstreamSessionTest,ShutdownDropWithReq)1084 TEST_P(HQDownstreamSessionTest, ShutdownDropWithReq) {
1085 sendRequest();
1086 auto handler = addSimpleStrictHandler();
1087 handler->expectHeaders();
1088 handler->expectEOM();
1089 handler->expectError();
1090 handler->expectDetachTransaction();
1091 flushRequestsAndLoopN(1);
1092 hqSession_->dropConnection();
1093 }
1094
1095 // dropConnection invoked while a request is partial, it receives an
1096 // error from the transport
TEST_P(HQDownstreamSessionTest,ShutdownDropWithPartialReq)1097 TEST_P(HQDownstreamSessionTest, ShutdownDropWithPartialReq) {
1098 sendRequest(getPostRequest(10), false);
1099 auto handler = addSimpleStrictHandler();
1100 handler->expectHeaders();
1101 handler->expectError();
1102 handler->expectDetachTransaction();
1103 flushRequestsAndLoopN(1);
1104 hqSession_->dropConnection();
1105 }
1106
1107 // Call drop connection while there are bytes pending to egress
TEST_P(HQDownstreamSessionTest,DropConnectionPendingEgress)1108 TEST_P(HQDownstreamSessionTest, DropConnectionPendingEgress) {
1109 // NOTE: this test assumes that dropConnection() gets called by the handler
1110 // before the session has the chance to write data.
1111 // This is not true anymore when there are control streams
1112 // So let's just loop a bit to give time to the Downstream Session to send the
1113 // control stream preface
1114 if (!IS_H1Q_FB_V1) {
1115 flushRequestsAndLoop();
1116 }
1117
1118 sendRequest(getGetRequest());
1119 auto handler = addSimpleStrictHandler();
1120 handler->expectHeaders([&handler, this] {
1121 handler->sendReplyWithBody(200, 1);
1122 eventBase_.runInLoop([this] { hqSession_->dropConnection(); }, true);
1123 });
1124 handler->expectEOM();
1125 handler->expectError();
1126 handler->expectDetachTransaction();
1127 flushRequestsAndLoop();
1128 }
1129
TEST_P(HQDownstreamSessionTest,TestInfoCallbacks)1130 TEST_P(HQDownstreamSessionTest, TestInfoCallbacks) {
1131 folly::Optional<HTTPCodec::StreamID> id = sendRequest();
1132 auto handler = addSimpleStrictHandler();
1133 handler->expectHeaders();
1134 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
1135 handler->expectDetachTransaction();
1136 EXPECT_CALL(infoCb_, onRequestBegin(_)).Times(1);
1137 EXPECT_CALL(infoCb_, onActivateConnection(_)).Times(1);
1138 EXPECT_CALL(infoCb_, onIngressMessage(_, _)).Times(1);
1139 EXPECT_CALL(infoCb_, onRead(_, _, id)).Times(AtLeast(2));
1140 EXPECT_CALL(infoCb_, onWrite(_, _)).Times(AtLeast(1));
1141 EXPECT_CALL(infoCb_, onDestroy(_)).Times(1);
1142 EXPECT_CALL(infoCb_, onRequestEnd(_, _)).Times(1);
1143 EXPECT_CALL(infoCb_, onDeactivateConnection(_)).Times(1);
1144 flushRequestsAndLoop();
1145 hqSession_->dropConnection();
1146 }
1147
TEST_P(HQDownstreamSessionTest,NotifyDropNoStreams)1148 TEST_P(HQDownstreamSessionTest, NotifyDropNoStreams) {
1149 hqSession_->notifyPendingShutdown();
1150 eventBase_.loop();
1151 // no need to explicitly drop in H1Q-V2
1152 if (IS_H1Q_FB_V1) {
1153 hqSession_->dropConnection();
1154 }
1155 }
1156
TEST_P(HQDownstreamSessionTest,ShutdownDropWithUnflushedResp)1157 TEST_P(HQDownstreamSessionTest, ShutdownDropWithUnflushedResp) {
1158 auto id = sendRequest();
1159 // should be enough to trick HQSession into serializing the EOM into
1160 // HQStreamTransport but without enough to send it.
1161 // HTTP/3 has an extra grease frame on the first transaction
1162 socketDriver_->setStreamFlowControlWindow(id, IS_HQ ? 209 : 206);
1163 auto handler = addSimpleStrictHandler();
1164 handler->expectHeaders();
1165 handler->expectEOM([&handler] {
1166 handler->sendChunkedReplyWithBody(200, 100, 100, false, true);
1167 });
1168 handler->expectDetachTransaction();
1169 flushRequestsAndLoopN(1);
1170 hqSession_->dropConnection();
1171 }
1172
1173 // rst_stream while a request is partial, terminate cleanly
TEST_P(HQDownstreamSessionTest,Cancel)1174 TEST_P(HQDownstreamSessionTest, Cancel) {
1175 auto id = sendRequest(getPostRequest(10), false);
1176 auto handler = addSimpleStrictHandler();
1177 handler->expectHeaders([this, id] {
1178 socketDriver_->addReadError(id,
1179 HTTP3::ErrorCode::HTTP_INTERNAL_ERROR,
1180 std::chrono::milliseconds(0));
1181 hqSession_->closeWhenIdle();
1182 });
1183 handler->expectError();
1184 handler->expectDetachTransaction();
1185 flushRequestsAndLoop();
1186 EXPECT_EQ(*socketDriver_->streams_[id].error,
1187 HTTP3::ErrorCode::HTTP_REQUEST_CANCELLED);
1188 }
1189
TEST_P(HQDownstreamSessionTestHQ,EndOfStreamWithPartialFrame)1190 TEST_P(HQDownstreamSessionTestHQ, EndOfStreamWithPartialFrame) {
1191 auto id = sendRequest(getPostRequest(10), false);
1192 auto& request = getStream(id);
1193 // generate body + EOM, but trim the last byte
1194 folly::IOBufQueue writeBuf{folly::IOBufQueue::cacheChainLength()};
1195 request.codec->generateBody(
1196 writeBuf, request.id, makeBuf(10), HTTPCodec::NoPadding, true);
1197 request.buf.append(writeBuf.split(writeBuf.chainLength() - 1));
1198 request.readEOF = true;
1199 auto handler = addSimpleStrictHandler();
1200 handler->expectHeaders();
1201 handler->expectBody();
1202 handler->expectError();
1203 handler->expectDetachTransaction();
1204 flushRequestsAndLoop();
1205 EXPECT_EQ(*socketDriver_->streams_[kConnectionStreamId].error,
1206 HTTP3::ErrorCode::HTTP_FRAME_ERROR);
1207 }
1208
1209 // read() returns a LocalErrorCode
TEST_P(HQDownstreamSessionTest,ReadErrorSync)1210 TEST_P(HQDownstreamSessionTest, ReadErrorSync) {
1211 auto id = sendRequest(getPostRequest(10), false);
1212 auto handler = addSimpleStrictHandler();
1213 handler->expectHeaders([this, id] {
1214 // mark the stream in read error and trigger a readAvailable call
1215 socketDriver_->setReadError(id);
1216 // This is just to trigger readAvailable
1217 socketDriver_->addReadEvent(id, makeBuf(10), milliseconds(0));
1218 hqSession_->closeWhenIdle();
1219 });
1220 handler->expectError();
1221 handler->expectDetachTransaction();
1222 flushRequestsAndLoop();
1223 }
1224
1225 // Connection dies in error with an open stream
TEST_P(HQDownstreamSessionTest,TransportErrorWithOpenStream)1226 TEST_P(HQDownstreamSessionTest, TransportErrorWithOpenStream) {
1227 sendRequest(getPostRequest(10), false);
1228 auto handler = addSimpleStrictHandler();
1229 handler->expectHeaders([this] {
1230 eventBase_.runInLoop([this] {
1231 // This should error out the stream first, then destroy the session
1232 socketDriver_->deliverConnectionError(
1233 std::make_pair(quic::TransportErrorCode::PROTOCOL_VIOLATION, ""));
1234 });
1235 });
1236 handler->expectError([](const HTTPException& ex) {
1237 EXPECT_EQ(ex.getProxygenError(), kErrorConnectionReset);
1238 });
1239 handler->expectDetachTransaction();
1240 EXPECT_CALL(infoCb_, onConnectionError(_)).Times(0);
1241 flushRequestsAndLoop();
1242 }
1243
1244 // writeChain() returns a LocalErrorCode with a half-closed stream
TEST_P(HQDownstreamSessionTest,WriteError)1245 TEST_P(HQDownstreamSessionTest, WriteError) {
1246 auto id = sendRequest();
1247 auto handler = addSimpleStrictHandler();
1248 handler->expectHeaders();
1249 handler->expectEOM([&handler, this, id] {
1250 handler->sendHeaders(200, 100);
1251 socketDriver_->setWriteError(id);
1252 hqSession_->closeWhenIdle();
1253 });
1254 handler->expectError([](const HTTPException& ex) {
1255 EXPECT_EQ(ex.getProxygenError(), kErrorWrite);
1256 });
1257 handler->expectDetachTransaction();
1258 flushRequestsAndLoop();
1259 }
1260
1261 // writeChain() returns a LocalErrorCode with stream open both ways
TEST_P(HQDownstreamSessionTest,WriteErrorPartialReq)1262 TEST_P(HQDownstreamSessionTest, WriteErrorPartialReq) {
1263 auto id = sendRequest(getPostRequest(10), false);
1264 auto handler = addSimpleStrictHandler();
1265 handler->expectHeaders([&handler, this, id] {
1266 handler->sendReplyWithBody(200, 100);
1267 socketDriver_->setWriteError(id);
1268 hqSession_->closeWhenIdle();
1269 });
1270 handler->expectError();
1271 handler->expectDetachTransaction();
1272 flushRequestsAndLoop();
1273 }
1274
1275 // Test write on non writable stream
TEST_P(HQDownstreamSessionTest,WriteNonWritableStream)1276 TEST_P(HQDownstreamSessionTest, WriteNonWritableStream) {
1277 auto idh = checkRequest();
1278 // delay the eof event so that we won't have to loop
1279 flushRequestsAndLoop(false, milliseconds(0), milliseconds(50), [&] {
1280 // Force the read in the loop, so that this will trigger a write.
1281 eventBase_.loop();
1282 socketDriver_->flowControlAccess_.clear();
1283 });
1284 // Once the eof is written and no more bytes remain, we should never
1285 // call flow control methods.
1286 EXPECT_EQ(socketDriver_->flowControlAccess_.count(idh.first), 0);
1287 hqSession_->closeWhenIdle();
1288 }
1289
TEST_P(HQDownstreamSessionTest,WriteErrorFlowControl)1290 TEST_P(HQDownstreamSessionTest, WriteErrorFlowControl) {
1291 auto id = sendRequest(getPostRequest(10), false);
1292 auto handler = addSimpleStrictHandler();
1293 handler->expectHeaders([&handler, this, id] {
1294 handler->sendReplyWithBody(200, 100);
1295 socketDriver_->forceStreamClose(id);
1296 hqSession_->closeWhenIdle();
1297 });
1298 handler->expectError();
1299 handler->expectDetachTransaction();
1300 flushRequestsAndLoop();
1301 }
1302
1303 // Connection error on idle connection
TEST_P(HQDownstreamSessionTest,ConnectionErrorIdle)1304 TEST_P(HQDownstreamSessionTest, ConnectionErrorIdle) {
1305 socketDriver_->deliverConnectionError(
1306 std::make_pair(quic::TransportErrorCode::PROTOCOL_VIOLATION, ""));
1307 eventBase_.loopOnce();
1308 }
1309
1310 // Connection End on an idle connection
TEST_P(HQDownstreamSessionTest,ConnectionEnd)1311 TEST_P(HQDownstreamSessionTest, ConnectionEnd) {
1312 nextStreamId();
1313 socketDriver_->addOnConnectionEndEvent(10);
1314 CHECK(eventBase_.loop());
1315 }
1316
1317 // invalid HTTP on stream before headers
1318 // Might need an HQ test with unparseable junk?
TEST_P(HQDownstreamSessionTestH1q,BadHttp)1319 TEST_P(HQDownstreamSessionTestH1q, BadHttp) {
1320 auto id = nextStreamId();
1321 auto buf = IOBuf::create(10);
1322 memset(buf->writableData(), 'a', 10);
1323 buf->append(10);
1324 testing::StrictMock<MockHTTPHandler> handler;
1325 EXPECT_CALL(getMockController(), getParseErrorHandler(_, _, _))
1326 .WillOnce(Return(&handler));
1327 EXPECT_CALL(handler, setTransaction(testing::_))
1328 .WillOnce(testing::SaveArg<0>(&handler.txn_));
1329 handler.expectError([&handler](const HTTPException& ex) {
1330 EXPECT_TRUE(ex.hasHttpStatusCode());
1331 handler.sendReplyWithBody(ex.getHttpStatusCode(), 100);
1332 });
1333 handler.expectDetachTransaction();
1334 socketDriver_->addReadEvent(id, std::move(buf), milliseconds(0));
1335 socketDriver_->addReadEOF(id);
1336
1337 flushRequestsAndLoop();
1338 hqSession_->closeWhenIdle();
1339 }
1340
1341 // Invalid HTTP headers
TEST_P(HQDownstreamSessionTestH1q,BadHttpHeaders)1342 TEST_P(HQDownstreamSessionTestH1q, BadHttpHeaders) {
1343 auto id = nextStreamId();
1344 auto buf = IOBuf::copyBuffer("GET", 3);
1345 socketDriver_->addReadEvent(id, std::move(buf), milliseconds(0));
1346 socketDriver_->addReadEOF(id);
1347 testing::StrictMock<MockHTTPHandler> handler;
1348 EXPECT_CALL(getMockController(), getParseErrorHandler(_, _, _))
1349 .WillOnce(Return(&handler));
1350 EXPECT_CALL(handler, setTransaction(testing::_))
1351 .WillOnce(testing::SaveArg<0>(&handler.txn_));
1352 handler.expectError([&handler](const HTTPException& ex) {
1353 EXPECT_TRUE(ex.hasHttpStatusCode());
1354 handler.sendReplyWithBody(ex.getHttpStatusCode(), 100);
1355 });
1356 handler.expectDetachTransaction();
1357
1358 flushRequestsAndLoop();
1359 hqSession_->closeWhenIdle();
1360 }
1361
TEST_P(HQDownstreamSessionTestHQ,BadHttpHeaders)1362 TEST_P(HQDownstreamSessionTestHQ, BadHttpHeaders) {
1363 auto id = nextStreamId();
1364 std::array<uint8_t, 4> badHeaders{0x01, 0x02, 0x00, 0x81};
1365 auto buf = folly::IOBuf::copyBuffer(badHeaders.data(), badHeaders.size());
1366 socketDriver_->addReadEvent(id, std::move(buf), milliseconds(0));
1367 socketDriver_->addReadEOF(id);
1368 /* T35641532 -- Should QPACK errors be a session errors ?
1369 testing::StrictMock<MockHTTPHandler> handler;
1370 EXPECT_CALL(getMockController(), getParseErrorHandler(_, _, _))
1371 .WillOnce(Return(&handler));
1372 EXPECT_CALL(handler, setTransaction(testing::_))
1373 .WillOnce(testing::SaveArg<0>(&handler.txn_));
1374 handler.expectError();
1375 handler.expectDetachTransaction();
1376 */
1377 flushRequestsAndLoop();
1378 // The QPACK error will cause the connection to get dropped
1379 }
1380
TEST_P(HQDownstreamSessionTest,BadHttpStrict)1381 TEST_P(HQDownstreamSessionTest, BadHttpStrict) {
1382 hqSession_->setStrictValidation(true);
1383 sendRequest(getGetRequest("/foo\xff"));
1384 testing::StrictMock<MockHTTPHandler> handler;
1385 EXPECT_CALL(getMockController(), getParseErrorHandler(_, _, _))
1386 .WillOnce(Return(&handler));
1387 EXPECT_CALL(handler, setTransaction(testing::_))
1388 .WillOnce(testing::SaveArg<0>(&handler.txn_));
1389 handler.expectError([&handler](const HTTPException& ex) {
1390 EXPECT_TRUE(ex.hasHttpStatusCode());
1391 handler.sendReplyWithBody(ex.getHttpStatusCode(), 100);
1392 });
1393 handler.expectDetachTransaction();
1394
1395 flushRequestsAndLoop();
1396 hqSession_->closeWhenIdle();
1397 }
1398
TEST_P(HQDownstreamSessionTest,BadHttpHeadersStrict)1399 TEST_P(HQDownstreamSessionTest, BadHttpHeadersStrict) {
1400 hqSession_->setStrictValidation(true);
1401 auto req = getGetRequest("/foo");
1402 req.getHeaders().set("Foo", "bad value\xff");
1403 sendRequest(req);
1404 testing::StrictMock<MockHTTPHandler> handler;
1405 EXPECT_CALL(getMockController(), getParseErrorHandler(_, _, _))
1406 .WillOnce(Return(&handler));
1407 EXPECT_CALL(handler, setTransaction(testing::_))
1408 .WillOnce(testing::SaveArg<0>(&handler.txn_));
1409 handler.expectError([&handler](const HTTPException& ex) {
1410 EXPECT_TRUE(ex.hasHttpStatusCode());
1411 handler.sendReplyWithBody(ex.getHttpStatusCode(), 100);
1412 });
1413 handler.expectDetachTransaction();
1414
1415 flushRequestsAndLoop();
1416 hqSession_->closeWhenIdle();
1417 }
1418
1419 // NOTE: this behavior is only valid for basic h1q
TEST_P(HQDownstreamSessionTestH1qv1,ShutdownWithTwoTxn)1420 TEST_P(HQDownstreamSessionTestH1qv1, ShutdownWithTwoTxn) {
1421 sendRequest();
1422 auto req = getGetRequest();
1423 req.getHeaders().set(HTTP_HEADER_CONNECTION, "close");
1424 sendRequest(req);
1425 auto handler1 = addSimpleStrictHandler();
1426 auto handler2 = addSimpleStrictHandler();
1427 handler1->expectHeaders();
1428 handler1->expectEOM([&handler1] { handler1->sendReplyWithBody(200, 100); });
1429 handler1->expectDetachTransaction();
1430 handler2->expectHeaders();
1431 handler2->expectEOM([&handler2] { handler2->sendReplyWithBody(200, 100); });
1432 handler2->expectDetachTransaction();
1433 flushRequestsAndLoop();
1434 }
1435
TEST_P(HQDownstreamSessionTestH1q,SendEmptyResponseHeadersOnly)1436 TEST_P(HQDownstreamSessionTestH1q, SendEmptyResponseHeadersOnly) {
1437 HTTPMessage req;
1438 req.setMethod(HTTPMethod::GET);
1439 req.setHTTPVersion(0, 9);
1440 req.setURL("/");
1441 sendRequest(req);
1442 auto handler = addSimpleStrictHandler();
1443 handler->expectHeaders();
1444 handler->expectEOM([&] {
1445 HTTPMessage resp;
1446 resp.setStatusCode(200);
1447 resp.setHTTPVersion(0, 9);
1448 handler->txn_->sendHeaders(resp);
1449 eventBase_.runAfterDelay([&] { handler->txn_->sendEOM(); }, 10);
1450 });
1451 handler->expectDetachTransaction();
1452 flushRequestsAndLoop();
1453 hqSession_->closeWhenIdle();
1454 }
1455
TEST_P(HQDownstreamSessionTest,SendFinOnly)1456 TEST_P(HQDownstreamSessionTest, SendFinOnly) {
1457 HTTPMessage req;
1458 req.setMethod(HTTPMethod::GET);
1459 req.setHTTPVersion(0, 9);
1460 req.setURL("/");
1461 sendRequest(req);
1462 auto handler = addSimpleStrictHandler();
1463 handler->expectHeaders();
1464 handler->expectEOM([&handler] {
1465 HTTPMessage resp;
1466 resp.setStatusCode(200);
1467 resp.setHTTPVersion(0, 9);
1468 handler->txn_->sendHeaders(resp);
1469 handler->txn_->sendEOM();
1470 });
1471 handler->expectDetachTransaction();
1472 flushRequestsAndLoop();
1473 hqSession_->closeWhenIdle();
1474 }
1475
TEST_P(HQDownstreamSessionTest,PauseResume)1476 TEST_P(HQDownstreamSessionTest, PauseResume) {
1477 auto id = sendRequest(getPostRequest(65547), false);
1478 auto& request = getStream(id);
1479 auto handler = addSimpleStrictHandler();
1480 // Handler pauses as soon as it receives headers. Nothing buffered so
1481 // transport continues reading
1482 handler->expectHeaders([&handler] { handler->txn_->pauseIngress(); });
1483 flushRequestsAndLoop();
1484 EXPECT_FALSE(socketDriver_->isStreamPaused(id));
1485
1486 // Generate some body, but over the limit. The session (currently) reads
1487 // everything from the transport, so it will exceed the limit and pause
1488 request.codec->generateBody(
1489 request.buf, request.id, makeBuf(65537), HTTPCodec::NoPadding, true);
1490 flushRequestsAndLoop();
1491 EXPECT_TRUE(socketDriver_->isStreamPaused(id));
1492 EXPECT_TRUE(socketDriver_->streams_[id].readBuf.empty());
1493
1494 // Now send some more data, all buffered
1495 request.codec->generateBody(
1496 request.buf, request.id, makeBuf(10), HTTPCodec::NoPadding, true);
1497 request.readEOF = true;
1498 flushRequestsAndLoop();
1499 EXPECT_FALSE(socketDriver_->streams_[id].readBuf.empty());
1500
1501 auto id2 = sendRequest();
1502 auto handler2 = addSimpleStrictHandler();
1503 // stream 2 will start paused at the transport, so even headers are parsed.
1504 flushRequestsAndLoopN(1);
1505 EXPECT_FALSE(socketDriver_->streams_[id2].readBuf.empty());
1506 EXPECT_TRUE(socketDriver_->isStreamPaused(id2));
1507 hqSession_->closeWhenIdle();
1508
1509 // After resume, body (2 calls) and EOM delivered
1510 EXPECT_CALL(*handler, onBodyWithOffset(_, _)).Times(2);
1511 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
1512 handler->expectDetachTransaction();
1513 handler2->expectHeaders(
1514 [&handler2] { handler2->sendReplyWithBody(200, 100); });
1515 handler2->expectEOM();
1516 handler2->expectDetachTransaction();
1517 handler->txn_->resumeIngress();
1518 eventBase_.loop();
1519 }
1520
TEST_P(HQDownstreamSessionTest,EnqueuedAbort)1521 TEST_P(HQDownstreamSessionTest, EnqueuedAbort) {
1522 sendRequest();
1523 auto handler = addSimpleStrictHandler();
1524 handler->expectHeaders();
1525 handler->expectEOM([&handler] {
1526 handler->sendHeaders(200, 100);
1527 handler->txn_->sendBody(makeBuf(100));
1528 handler->txn_->sendAbort();
1529 });
1530 handler->expectDetachTransaction();
1531 flushRequestsAndLoop();
1532 hqSession_->closeWhenIdle();
1533 }
1534
TEST_P(HQDownstreamSessionTest,TransactionTimeout)1535 TEST_P(HQDownstreamSessionTest, TransactionTimeout) {
1536 sendRequest(getPostRequest(10), false);
1537 auto handler = addSimpleStrictHandler();
1538 handler->expectHeaders([&handler] {
1539 // fire the timeout as soon as receiving the headers
1540 handler->txn_->setIdleTimeout(std::chrono::milliseconds(0));
1541 });
1542 handler->expectError([&handler](const HTTPException& ex) {
1543 EXPECT_FALSE(ex.hasHttpStatusCode());
1544 handler->terminate();
1545 });
1546 handler->expectDetachTransaction();
1547 flushRequestsAndLoop();
1548 hqSession_->closeWhenIdle();
1549 }
1550
TEST_P(HQDownstreamSessionTestH1q,ManagedTimeoutReadReset)1551 TEST_P(HQDownstreamSessionTestH1q, ManagedTimeoutReadReset) {
1552 std::chrono::milliseconds connIdleTimeout{200};
1553 auto connManager = wangle::ConnectionManager::makeUnique(
1554 &eventBase_, connIdleTimeout, nullptr);
1555 connManager->addConnection(hqSession_, true);
1556 HQSession::DestructorGuard dg(hqSession_);
1557 auto handler = addSimpleStrictHandler();
1558 auto id = sendRequest(getPostRequest(10), false);
1559 auto& request = getStream(id);
1560 request.codec->generateBody(
1561 request.buf, request.id, makeBuf(3), HTTPCodec::NoPadding, true);
1562 request.readEOF = false;
1563 eventBase_.runAfterDelay(
1564 [&] {
1565 request.codec->generateBody(
1566 request.buf, request.id, makeBuf(3), HTTPCodec::NoPadding, true);
1567 request.readEOF = false;
1568 flushRequests();
1569 },
1570 100);
1571 eventBase_.runAfterDelay(
1572 [&] {
1573 EXPECT_NE(hqSession_->getConnectionCloseReason(),
1574 ConnectionCloseReason::TIMEOUT);
1575 request.codec->generateBody(
1576 request.buf, request.id, makeBuf(4), HTTPCodec::NoPadding, true);
1577 request.readEOF = true;
1578 flushRequests();
1579 },
1580 250);
1581 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
1582 handler->expectHeaders();
1583 EXPECT_CALL(*handler, onBodyWithOffset(testing::_, testing::_)).Times(3);
1584 handler->expectDetachTransaction();
1585 flushRequestsAndLoop();
1586 }
1587
TEST_P(HQDownstreamSessionTestHQ,ManagedTimeoutUnidirectionalReadReset)1588 TEST_P(HQDownstreamSessionTestHQ, ManagedTimeoutUnidirectionalReadReset) {
1589 std::chrono::milliseconds connIdleTimeout{200};
1590 auto connManager = wangle::ConnectionManager::makeUnique(
1591 &eventBase_, connIdleTimeout, nullptr);
1592 connManager->addConnection(hqSession_, true);
1593 HQSession::DestructorGuard dg(hqSession_);
1594
1595 // Just keep sending instructions to set the dynamic table capacity
1596 std::array<uint8_t, 1> data1{0b00100111};
1597 auto buf1 = folly::IOBuf::copyBuffer(data1.data(), data1.size());
1598 socketDriver_->addReadEvent(6, std::move(buf1), milliseconds(0));
1599 std::array<uint8_t, 1> data2{0b00100110};
1600 auto buf2 = folly::IOBuf::copyBuffer(data2.data(), data2.size());
1601 socketDriver_->addReadEvent(6, std::move(buf2), milliseconds(100));
1602 // Check that the session did not timeout, yet
1603 eventBase_.runAfterDelay(
1604 [&] {
1605 EXPECT_NE(hqSession_->getConnectionCloseReason(),
1606 ConnectionCloseReason::TIMEOUT);
1607 },
1608 250);
1609
1610 flushRequestsAndLoop();
1611 }
1612
TEST_P(HQDownstreamSessionTest,ManagedTimeoutActiveStreams)1613 TEST_P(HQDownstreamSessionTest, ManagedTimeoutActiveStreams) {
1614 std::chrono::milliseconds connIdleTimeout{300};
1615 auto connManager = wangle::ConnectionManager::makeUnique(
1616 &eventBase_, connIdleTimeout, nullptr);
1617 HQSession::DestructorGuard dg(hqSession_);
1618 sendRequest(getPostRequest(10), false);
1619 auto handler = addSimpleStrictHandler();
1620 connManager->addConnection(hqSession_, true);
1621 // Txn idle timer is > connIdleTimeout
1622 auto lastErrorTime = std::chrono::steady_clock::now();
1623 handler->expectHeaders([&handler] {
1624 handler->txn_->setIdleTimeout(std::chrono::milliseconds(500));
1625 });
1626 handler->expectError(
1627 [&handler, this, &lastErrorTime](const HTTPException& ex) {
1628 // we should txn timeout
1629 EXPECT_FALSE(ex.hasHttpStatusCode());
1630 EXPECT_EQ(ex.getProxygenError(), kErrorTimeout);
1631 EXPECT_TRUE(hqSession_->isScheduled());
1632 hqSession_->cancelTimeout();
1633 handler->terminate();
1634 lastErrorTime = std::chrono::steady_clock::now();
1635 });
1636 handler->expectDetachTransaction();
1637 flushRequestsAndLoop();
1638 auto now = std::chrono::steady_clock::now();
1639 EXPECT_GE(
1640 std::chrono::duration_cast<std::chrono::milliseconds>(now - lastErrorTime)
1641 .count(),
1642 connIdleTimeout.count());
1643 // Connection timeouts in the loop and closes.
1644 EXPECT_EQ(hqSession_->getConnectionCloseReason(),
1645 ConnectionCloseReason::TIMEOUT);
1646 }
1647
TEST_P(HQDownstreamSessionTest,ManagedTimeoutNoStreams)1648 TEST_P(HQDownstreamSessionTest, ManagedTimeoutNoStreams) {
1649 std::chrono::milliseconds connIdleTimeout{300};
1650 auto connManager = wangle::ConnectionManager::makeUnique(
1651 &eventBase_, connIdleTimeout, nullptr);
1652 HQSession::DestructorGuard dg(hqSession_);
1653 connManager->addConnection(hqSession_, true);
1654 eventBase_.loop();
1655 EXPECT_EQ(hqSession_->getConnectionCloseReason(),
1656 ConnectionCloseReason::TIMEOUT);
1657 }
1658
1659 // HQ can't do this case, because onMessageBegin is only called with full
1660 // headers.
TEST_P(HQDownstreamSessionTestH1q,TransactionTimeoutNoHandler)1661 TEST_P(HQDownstreamSessionTestH1q, TransactionTimeoutNoHandler) {
1662 // test transaction timeout before receiving the full headers
1663 auto id = nextStreamId();
1664 auto res = requests_.emplace(std::piecewise_construct,
1665 std::forward_as_tuple(id),
1666 std::forward_as_tuple(makeCodec(id)));
1667 auto req = getGetRequest();
1668 auto& request = res.first->second;
1669 request.id = request.codec->createStream();
1670 request.codec->generateHeader(request.buf, request.id, req, false);
1671 // Send some bytes, but less than the whole headers, so that a stream gets
1672 // created but the handler does not get assigned
1673 request.buf.trimEnd(1);
1674
1675 testing::StrictMock<MockHTTPHandler> handler;
1676 expectTransactionTimeout(handler);
1677
1678 flushRequestsAndLoop();
1679 hqSession_->closeWhenIdle();
1680 }
1681
TEST_P(HQDownstreamSessionTest,TransactionTimeoutNoCodecId)1682 TEST_P(HQDownstreamSessionTest, TransactionTimeoutNoCodecId) {
1683 auto id = nextStreamId();
1684 auto res = requests_.emplace(std::piecewise_construct,
1685 std::forward_as_tuple(id),
1686 std::forward_as_tuple(makeCodec(id)));
1687 auto req = getGetRequest();
1688 auto& request = res.first->second;
1689 request.id = request.codec->createStream();
1690 request.codec->generateHeader(request.buf, request.id, req, false);
1691 // Send only a new line, so that onMessageBegin does not get called
1692 request.buf.split(request.buf.chainLength() - 1);
1693 testing::StrictMock<MockHTTPHandler> handler;
1694 expectTransactionTimeout(handler);
1695 flushRequestsAndLoop();
1696 hqSession_->closeWhenIdle();
1697 }
1698
TEST_P(HQDownstreamSessionTest,SendOnFlowControlPaused)1699 TEST_P(HQDownstreamSessionTest, SendOnFlowControlPaused) {
1700 // 106 bytes of resp headers, 1 byte of body but 5 bytes of chunk overhead
1701 auto id = sendRequest();
1702 // HTTP/3 has an extra grease frame on the first transaction
1703 socketDriver_->setStreamFlowControlWindow(id, IS_HQ ? 103 : 100);
1704
1705 auto handler = addSimpleStrictHandler();
1706 handler->expectHeaders();
1707 handler->expectEOM([&handler] {
1708 handler->sendHeaders(200, 100);
1709 handler->txn_->sendBody(makeBuf(100));
1710 });
1711 handler->expectEgressPaused([&handler] { handler->txn_->sendEOM(); });
1712 flushRequestsAndLoop();
1713 socketDriver_->setStreamFlowControlWindow(id, 100);
1714 handler->expectDetachTransaction();
1715 eventBase_.loop();
1716 hqSession_->closeWhenIdle();
1717 }
1718
TEST_P(HQDownstreamSessionTest,Http_100Continue)1719 TEST_P(HQDownstreamSessionTest, Http_100Continue) {
1720 auto req = getPostRequest(100);
1721 req.getHeaders().add(HTTP_HEADER_EXPECT, "100-continue");
1722 auto id = sendRequest(req, false);
1723 auto handler = addSimpleStrictHandler();
1724 handler->expectHeaders([&handler] {
1725 HTTPMessage continueResp;
1726 continueResp.setStatusCode(100);
1727 handler->txn_->sendHeaders(continueResp);
1728 });
1729 flushRequestsAndLoopN(1);
1730 auto& request = getStream(id);
1731 request.codec->generateBody(
1732 request.buf, request.id, makeBuf(100), HTTPCodec::NoPadding, true);
1733 request.readEOF = true;
1734
1735 handler->expectBody();
1736 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
1737 handler->expectDetachTransaction();
1738 flushRequestsAndLoop();
1739 hqSession_->closeWhenIdle();
1740 }
1741
TEST_P(HQDownstreamSessionTest,ByteEvents)1742 TEST_P(HQDownstreamSessionTest, ByteEvents) {
1743 sendRequest();
1744 auto handler = addSimpleStrictHandler();
1745 MockHTTPTransactionTransportCallback callback;
1746 handler->expectHeaders([&handler, &callback] {
1747 handler->txn_->setTransportCallback(&callback);
1748 });
1749 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
1750 handler->expectDetachTransaction();
1751 EXPECT_CALL(callback, headerBytesGenerated(_));
1752 EXPECT_CALL(callback, bodyBytesGenerated(_));
1753 EXPECT_CALL(callback, firstHeaderByteFlushed());
1754 EXPECT_CALL(callback, firstByteFlushed());
1755 EXPECT_CALL(callback, lastByteFlushed());
1756 EXPECT_CALL(callback, lastByteAcked(_));
1757 flushRequestsAndLoop();
1758 hqSession_->closeWhenIdle();
1759 }
1760
TEST_P(HQDownstreamSessionTest,AppRateLimited)1761 TEST_P(HQDownstreamSessionTest, AppRateLimited) {
1762 sendRequest();
1763 auto handler = addSimpleStrictHandler();
1764 MockHTTPTransactionTransportCallback callback;
1765 handler->expectHeaders([&handler, &callback] {
1766 handler->txn_->setTransportCallback(&callback);
1767 });
1768 handler->expectEOM([this, &handler] {
1769 handler->sendHeaders(200, 150);
1770 handler->txn_->sendBody(makeBuf(100));
1771 // force trigger onAppRateLimited
1772 hqSession_->onAppRateLimited();
1773 });
1774 EXPECT_CALL(callback, headerBytesGenerated(_));
1775 EXPECT_CALL(callback, bodyBytesGenerated(Ge(100))); // For HQ it's 100
1776 EXPECT_CALL(callback, firstHeaderByteFlushed());
1777 EXPECT_CALL(callback, firstByteFlushed());
1778 EXPECT_CALL(callback, transportAppRateLimited());
1779 flushRequestsAndLoop();
1780
1781 // send some more bytes and force trigger onAppRateLimited
1782 EXPECT_CALL(callback, bodyBytesGenerated(Ge(50))); // For HQ it's 52
1783 EXPECT_CALL(callback, transportAppRateLimited());
1784 handler->txn_->sendBody(makeBuf(50));
1785 hqSession_->onAppRateLimited();
1786 flushRequestsAndLoop();
1787
1788 // Send the EOM, txn should not detach yet
1789 EXPECT_CALL(callback, bodyBytesGenerated(0));
1790 EXPECT_CALL(callback, lastByteFlushed());
1791 handler->txn_->sendEOM(); // 0 length EOM
1792 flushRequestsAndLoopN(1);
1793
1794 // Let the delivery callback fire, now it can cleanup
1795 EXPECT_CALL(callback, lastByteAcked(_));
1796 handler->expectDetachTransaction();
1797 flushRequestsAndLoop();
1798 hqSession_->closeWhenIdle();
1799 }
1800
TEST_P(HQDownstreamSessionTest,LastByteEventZeroSize)1801 TEST_P(HQDownstreamSessionTest, LastByteEventZeroSize) {
1802 sendRequest();
1803 auto handler = addSimpleStrictHandler();
1804 MockHTTPTransactionTransportCallback callback;
1805 handler->expectHeaders([&handler, &callback] {
1806 handler->txn_->setTransportCallback(&callback);
1807 });
1808 handler->expectEOM([&handler] {
1809 handler->sendHeaders(200, 100);
1810 handler->txn_->sendBody(makeBuf(100));
1811 });
1812 EXPECT_CALL(callback, headerBytesGenerated(_));
1813 EXPECT_CALL(callback, bodyBytesGenerated(Ge(100))); // For HQ it's 103
1814 EXPECT_CALL(callback, firstHeaderByteFlushed());
1815 EXPECT_CALL(callback, firstByteFlushed());
1816 flushRequestsAndLoop();
1817
1818 // Send the EOM, txn should not detach yet
1819 EXPECT_CALL(callback, bodyBytesGenerated(0));
1820 EXPECT_CALL(callback, lastByteFlushed());
1821 handler->txn_->sendEOM(); // 0 length EOM
1822 flushRequestsAndLoopN(1);
1823
1824 // Let the delivery callback fire, now it can cleanup
1825 EXPECT_CALL(callback, lastByteAcked(_));
1826 handler->expectDetachTransaction();
1827 flushRequestsAndLoop();
1828 hqSession_->closeWhenIdle();
1829 }
1830
TEST_P(HQDownstreamSessionTest,DropWithByteEvents)1831 TEST_P(HQDownstreamSessionTest, DropWithByteEvents) {
1832 sendRequest();
1833 auto handler = addSimpleStrictHandler();
1834 MockHTTPTransactionTransportCallback callback;
1835 handler->expectHeaders([&handler, &callback] {
1836 handler->txn_->setTransportCallback(&callback);
1837 });
1838 handler->expectEOM([&handler] { handler->sendReplyWithBody(200, 100); });
1839 handler->expectDetachTransaction();
1840 EXPECT_CALL(callback, headerBytesGenerated(_));
1841 EXPECT_CALL(callback, bodyBytesGenerated(_));
1842 EXPECT_CALL(callback, firstHeaderByteFlushed());
1843 EXPECT_CALL(callback, firstByteFlushed());
1844 EXPECT_CALL(callback, lastByteFlushed());
1845 flushRequestsAndLoopN(1);
1846 hqSession_->dropConnection();
1847 }
1848
TEST_P(HQDownstreamSessionTest,TransportInfo)1849 TEST_P(HQDownstreamSessionTest, TransportInfo) {
1850 wangle::TransportInfo transInfo;
1851 quic::QuicSocket::TransportInfo quicInfo;
1852 quicInfo.srtt = std::chrono::microseconds(135);
1853 quicInfo.rttvar = std::chrono::microseconds(246);
1854 quicInfo.writableBytes = 212;
1855 quicInfo.congestionWindow = 5 * quic::kDefaultUDPSendPacketLen;
1856 quicInfo.packetsRetransmitted = 513;
1857 quicInfo.timeoutBasedLoss = 90;
1858 quicInfo.pto = std::chrono::microseconds(34);
1859 quicInfo.bytesSent = 23;
1860 quicInfo.bytesRecvd = 123;
1861 quicInfo.ptoCount = 1;
1862 quicInfo.totalPTOCount = 2;
1863 EXPECT_CALL(*socketDriver_->getSocket(), getTransportInfo())
1864 .Times(3)
1865 .WillRepeatedly(Return(quicInfo));
1866 hqSession_->getCurrentTransportInfoWithoutUpdate(&transInfo);
1867 EXPECT_EQ(135, transInfo.rtt.count());
1868 EXPECT_EQ(246, transInfo.rtt_var);
1869 EXPECT_EQ(5, transInfo.cwnd);
1870 EXPECT_EQ(5 * quic::kDefaultUDPSendPacketLen, transInfo.cwndBytes);
1871 EXPECT_EQ(513, transInfo.rtx);
1872 EXPECT_EQ(90, transInfo.rtx_tm);
1873 EXPECT_EQ(34, transInfo.rto);
1874 EXPECT_EQ(23, transInfo.totalBytes);
1875 auto quicProtocolInfo =
1876 dynamic_cast<QuicProtocolInfo*>(transInfo.protocolInfo.get());
1877 EXPECT_EQ(0, quicProtocolInfo->ptoCount);
1878 EXPECT_EQ(0, quicProtocolInfo->totalPTOCount);
1879 EXPECT_EQ(0, quicProtocolInfo->totalTransportBytesSent);
1880 EXPECT_EQ(0, quicProtocolInfo->totalTransportBytesRecvd);
1881 hqSession_->getCurrentTransportInfo(&transInfo);
1882 EXPECT_EQ(1, quicProtocolInfo->ptoCount);
1883 EXPECT_EQ(2, quicProtocolInfo->totalPTOCount);
1884 EXPECT_EQ(23, quicProtocolInfo->totalTransportBytesSent);
1885 EXPECT_EQ(123, quicProtocolInfo->totalTransportBytesRecvd);
1886 hqSession_->dropConnection();
1887 }
1888
1889 // Current Transport Info tests
TEST_P(HQDownstreamSessionTest,CurrentTransportInfo)1890 TEST_P(HQDownstreamSessionTest, CurrentTransportInfo) {
1891 sendRequest();
1892 auto handler = addSimpleStrictHandler();
1893 MockHTTPTransactionTransportCallback callback;
1894 handler->expectHeaders([&handler, &callback] {
1895 handler->txn_->setTransportCallback(&callback);
1896 });
1897
1898 QuicStreamProtocolInfo resultProtocolInfo;
1899 handler->expectEOM([&handler, &resultProtocolInfo] {
1900 wangle::TransportInfo transInfo;
1901 handler->txn_->getCurrentTransportInfo(&transInfo);
1902 auto quicStreamProtocolInfo =
1903 dynamic_cast<QuicStreamProtocolInfo*>(transInfo.protocolInfo.get());
1904
1905 if (quicStreamProtocolInfo) {
1906 resultProtocolInfo.streamTransportInfo =
1907 quicStreamProtocolInfo->streamTransportInfo;
1908 }
1909 });
1910
1911 handler->expectDetachTransaction();
1912 handler->expectError([&](const HTTPException& ex) {
1913 EXPECT_EQ(ex.getProxygenError(), kErrorDropped);
1914 });
1915
1916 flushRequestsAndLoop();
1917 hqSession_->dropConnection();
1918
1919 // The stream transport info field should be equal to
1920 // the mock object
1921 EXPECT_EQ(resultProtocolInfo.streamTransportInfo.totalHeadOfLineBlockedTime,
1922 streamTransInfo_.totalHeadOfLineBlockedTime);
1923 EXPECT_EQ(resultProtocolInfo.streamTransportInfo.holbCount,
1924 streamTransInfo_.holbCount);
1925 EXPECT_EQ(resultProtocolInfo.streamTransportInfo.isHolb,
1926 streamTransInfo_.isHolb);
1927 }
1928
TEST_P(HQDownstreamSessionTest,GetAddresses)1929 TEST_P(HQDownstreamSessionTest, GetAddresses) {
1930 EXPECT_EQ(socketDriver_->localAddress_, hqSession_->getLocalAddress());
1931 EXPECT_EQ(socketDriver_->peerAddress_, hqSession_->getPeerAddress());
1932 hqSession_->dropConnection();
1933 }
1934
TEST_P(HQDownstreamSessionTest,GetAddressesFromBase)1935 TEST_P(HQDownstreamSessionTest, GetAddressesFromBase) {
1936 HTTPSessionBase* sessionBase = dynamic_cast<HTTPSessionBase*>(hqSession_);
1937 EXPECT_EQ(socketDriver_->localAddress_, sessionBase->getLocalAddress());
1938 EXPECT_EQ(socketDriver_->peerAddress_, sessionBase->getPeerAddress());
1939 hqSession_->dropConnection();
1940 }
1941
TEST_P(HQDownstreamSessionTest,GetAddressesAfterDropConnection)1942 TEST_P(HQDownstreamSessionTest, GetAddressesAfterDropConnection) {
1943 HQSession::DestructorGuard dg(hqSession_);
1944 hqSession_->dropConnection();
1945 EXPECT_EQ(socketDriver_->localAddress_, hqSession_->getLocalAddress());
1946 EXPECT_EQ(socketDriver_->peerAddress_, hqSession_->getPeerAddress());
1947 }
1948
TEST_P(HQDownstreamSessionTest,RstCancelled)1949 TEST_P(HQDownstreamSessionTest, RstCancelled) {
1950 auto id = nextStreamId();
1951 auto buf = IOBuf::create(3);
1952 memcpy(buf->writableData(), "GET", 3);
1953 buf->append(3);
1954 socketDriver_->addReadEvent(id, std::move(buf), milliseconds(0));
1955 flushRequestsAndLoopN(1);
1956 socketDriver_->addReadError(id,
1957 HTTP3::ErrorCode::HTTP_REQUEST_CANCELLED,
1958 std::chrono::milliseconds(0));
1959 hqSession_->closeWhenIdle();
1960 flushRequestsAndLoop();
1961 EXPECT_EQ(*socketDriver_->streams_[id].error,
1962 HTTP3::ErrorCode::HTTP_REQUEST_REJECTED);
1963 }
1964
TEST_P(HQDownstreamSessionTest,LocalErrQueuedEgress)1965 TEST_P(HQDownstreamSessionTest, LocalErrQueuedEgress) {
1966 sendRequest(getPostRequest(10), false);
1967 auto handler = addSimpleStrictHandler();
1968 handler->expectHeaders([&handler, this] {
1969 socketDriver_->setStreamFlowControlWindow(0, 0);
1970 socketDriver_->setConnectionFlowControlWindow(0);
1971 handler->sendHeaders(200, 65536 * 2);
1972 handler->sendBody(65536 * 2);
1973 });
1974 handler->expectEgressPaused();
1975 flushRequestsAndLoopN(2);
1976 handler->expectError([](const HTTPException& ex) {
1977 EXPECT_EQ(ex.getProxygenError(), kErrorShutdown);
1978 });
1979 handler->expectDetachTransaction();
1980 socketDriver_->deliverConnectionError(
1981 std::make_pair(quic::LocalErrorCode::CONNECTION_RESET, ""));
1982 flushRequestsAndLoop();
1983 }
1984
TEST_P(HQDownstreamSessionTest,NoErrorNoStreams)1985 TEST_P(HQDownstreamSessionTest, NoErrorNoStreams) {
1986 EXPECT_CALL(infoCb_, onIngressError(_, kErrorNone));
1987 socketDriver_->deliverConnectionError(
1988 std::make_pair(HTTP3::ErrorCode::HTTP_NO_ERROR, ""));
1989 flushRequestsAndLoop();
1990 }
1991
TEST_P(HQDownstreamSessionTest,NoErrorOneStreams)1992 TEST_P(HQDownstreamSessionTest, NoErrorOneStreams) {
1993 sendRequest();
1994 auto handler = addSimpleStrictHandler();
1995 handler->expectHeaders();
1996 handler->expectEOM();
1997 flushRequestsAndLoopN(1);
1998 // stream gets an error
1999 handler->expectError([](const HTTPException& ex) {
2000 EXPECT_EQ(ex.getProxygenError(), kErrorEOF);
2001 });
2002 handler->expectDetachTransaction();
2003 // This is for connection level errors, maybe should be reported a
2004 EXPECT_CALL(infoCb_, onIngressError(_, kErrorEOF));
2005 socketDriver_->deliverConnectionError(
2006 std::make_pair(HTTP3::ErrorCode::HTTP_NO_ERROR, ""));
2007 }
2008
TEST_P(HQDownstreamSessionTestHQ,Connect)2009 TEST_P(HQDownstreamSessionTestHQ, Connect) {
2010 auto handler = addSimpleStrictHandler();
2011 // Send HTTP 200 OK to accept the CONNECT request
2012 handler->expectHeaders([&handler] { handler->sendHeaders(200, 100); });
2013 handler->expectEOM([&] { handler->terminate(); });
2014
2015 // Data should be received using onBody
2016 EXPECT_CALL(*handler, onBodyWithOffset(_, _))
2017 .WillOnce(ExpectString("12345"))
2018 .WillOnce(ExpectString("abcdefg"));
2019 handler->expectDetachTransaction();
2020
2021 proxygen::HTTPMessage req;
2022 req.setURL("test.net/path");
2023 req.setMethod("CONNECT");
2024 req.getHeaders().add(proxygen::HTTP_HEADER_HOST, "https://test.net/path");
2025 auto id = sendRequest(req, /* eom */ false);
2026 auto& request = getStream(id);
2027
2028 auto buf1 = IOBuf::copyBuffer("12345");
2029 request.codec->generateBody(
2030 request.buf, request.id, std::move(buf1), HTTPCodec::NoPadding, true);
2031 flushRequestsAndLoopN(1);
2032
2033 auto buf2 = IOBuf::copyBuffer("abcdefg");
2034 request.codec->generateBody(
2035 request.buf, request.id, std::move(buf2), HTTPCodec::NoPadding, true);
2036 flushRequestsAndLoopN(1);
2037
2038 request.readEOF = true;
2039 flushRequestsAndLoop();
2040 hqSession_->closeWhenIdle();
2041 }
2042
TEST_P(HQDownstreamSessionTestHQ,ConnectUDP)2043 TEST_P(HQDownstreamSessionTestHQ, ConnectUDP) {
2044 auto handler = addSimpleStrictHandler();
2045 // Send HTTP 200 OK to accept the CONNECT request
2046 handler->expectHeaders([&handler] { handler->sendHeaders(200, 100); });
2047 handler->expectEOM([&] { handler->terminate(); });
2048
2049 // Data should be received using onBody
2050 EXPECT_CALL(*handler, onBodyWithOffset(_, _))
2051 .WillOnce(ExpectString("12345"))
2052 .WillOnce(ExpectString("abcdefg"));
2053 handler->expectDetachTransaction();
2054
2055 proxygen::HTTPMessage req;
2056 req.setURL("test.net/path");
2057 req.setMethod("CONNECT-UDP");
2058 req.getHeaders().add(proxygen::HTTP_HEADER_HOST, "https://test.net/path");
2059 auto id = sendRequest(req, /* eom */ false);
2060 auto& request = getStream(id);
2061
2062 auto buf1 = IOBuf::copyBuffer("12345");
2063 request.codec->generateBody(
2064 request.buf, request.id, std::move(buf1), HTTPCodec::NoPadding, true);
2065 flushRequestsAndLoopN(1);
2066
2067 auto buf2 = IOBuf::copyBuffer("abcdefg");
2068 request.codec->generateBody(
2069 request.buf, request.id, std::move(buf2), HTTPCodec::NoPadding, true);
2070 flushRequestsAndLoopN(1);
2071
2072 request.readEOF = true;
2073 flushRequestsAndLoop();
2074 hqSession_->closeWhenIdle();
2075 }
2076
2077 // Just open a stream and send nothing
TEST_P(HQDownstreamSessionTest,zeroBytes)2078 TEST_P(HQDownstreamSessionTest, zeroBytes) {
2079 auto id = nextStreamId();
2080 socketDriver_->addReadEvent(
2081 id, folly::IOBuf::copyBuffer("", 0), milliseconds(0));
2082 testing::StrictMock<MockHTTPHandler> handler;
2083 expectTransactionTimeout(handler);
2084 eventBase_.loop();
2085 hqSession_->closeWhenIdle();
2086 }
2087
2088 // For HQ, send an incomplete frame header
TEST_P(HQDownstreamSessionTestHQ,oneByte)2089 TEST_P(HQDownstreamSessionTestHQ, oneByte) {
2090 auto id = nextStreamId();
2091 socketDriver_->addReadEvent(
2092 id, folly::IOBuf::copyBuffer("", 1), milliseconds(0));
2093 testing::StrictMock<MockHTTPHandler> handler;
2094 expectTransactionTimeout(handler);
2095 eventBase_.loop();
2096 hqSession_->closeWhenIdle();
2097 }
2098
TEST_P(HQDownstreamSessionTestH1qv2HQ,TestGoawayID)2099 TEST_P(HQDownstreamSessionTestH1qv2HQ, TestGoawayID) {
2100 // This test check that unidirectional stream IDs are not accounted for
2101 // in the Goaway Max Stream ID
2102 auto req = getGetRequest();
2103 // Explicitly skip some stream IDs to simulate out of order delivery
2104 sendRequest(req, true, 4);
2105 auto handler = addSimpleStrictHandler();
2106 handler->expectHeaders();
2107 handler->expectEOM([hdlr = handler.get()] {
2108 // Delay sending EOM so the streams are active when draining
2109 hdlr->sendReplyWithBody(200, 100, true, false);
2110 });
2111 handler->expectDetachTransaction();
2112 flushRequestsAndLoopN(1);
2113 hqSession_->closeWhenIdle();
2114 // Give it some time to send the two goaways and receive the delivery callback
2115 flushRequestsAndLoopN(3);
2116 EXPECT_EQ(httpCallbacks_.goaways, 2);
2117 EXPECT_THAT(httpCallbacks_.goawayStreamIds,
2118 ElementsAre(kMaxClientBidiStreamId, 8));
2119 handler->sendEOM();
2120 flushRequestsAndLoop();
2121 }
2122
TEST_P(HQDownstreamSessionTestHQ,TestReceiveGoaway)2123 TEST_P(HQDownstreamSessionTestHQ, TestReceiveGoaway) {
2124 folly::IOBufQueue writeBuf{folly::IOBufQueue::cacheChainLength()};
2125 egressControlCodec_->generateGoaway(
2126 writeBuf, HTTPCodec::MaxStreamID, ErrorCode::NO_ERROR, nullptr);
2127 socketDriver_->addReadEvent(
2128 connControlStreamId_, writeBuf.move(), std::chrono::milliseconds(0));
2129 flushRequestsAndLoopN(1);
2130 EXPECT_FALSE(hqSession_->isClosing());
2131 hqSession_->closeWhenIdle();
2132 eventBase_.loop();
2133 }
2134
TEST_P(HQDownstreamSessionTestH1qv2HQ,TestGetGoaway)2135 TEST_P(HQDownstreamSessionTestH1qv2HQ, TestGetGoaway) {
2136 std::vector<std::unique_ptr<StrictMock<MockHTTPHandler>>> handlers;
2137 auto numStreams = 3;
2138 for (auto n = 1; n <= numStreams; n++) {
2139 auto req = getGetRequest();
2140 // Explicitly skip some stream IDs to simulate out of order delivery
2141 sendRequest(req, true, n * 8);
2142 handlers.emplace_back(addSimpleStrictHandler());
2143 auto handler = handlers.back().get();
2144 handler->expectHeaders();
2145 handler->expectEOM([hdlr = handler] {
2146 // Delay sending EOM so the streams are active when draining
2147 hdlr->sendReplyWithBody(200, 100, true, false);
2148 });
2149 handler->expectDetachTransaction();
2150 }
2151 flushRequestsAndLoopN(1);
2152 hqSession_->closeWhenIdle();
2153 // Give it some time to send the two goaways and receive the delivery callback
2154 flushRequestsAndLoopN(3);
2155 EXPECT_EQ(httpCallbacks_.goaways, 2);
2156 EXPECT_THAT(httpCallbacks_.goawayStreamIds,
2157 ElementsAre(kMaxClientBidiStreamId, numStreams * 8 + 4));
2158
2159 // Check that a new stream with id >= lastStreamId gets rejected
2160 auto errReq = getGetRequest();
2161 quic::StreamId errStreamId = numStreams * 8 + 4;
2162 sendRequest(errReq, true, errStreamId);
2163 flushRequestsAndLoopN(1);
2164 auto& errStream = socketDriver_->streams_[errStreamId];
2165 EXPECT_EQ(errStream.writeState, MockQuicSocketDriver::StateEnum::ERROR);
2166 EXPECT_TRUE(errStream.error == HTTP3::ErrorCode::HTTP_REQUEST_REJECTED);
2167
2168 // Check that a new stream with id <= lastStreamId is instead just fine
2169 auto okReq = getGetRequest();
2170 sendRequest(okReq, true, numStreams * 8 - 4);
2171 auto okHandler = addSimpleStrictHandler();
2172 okHandler->expectHeaders();
2173 okHandler->expectEOM([&] { okHandler->sendReplyWithBody(200, 100); });
2174 okHandler->expectDetachTransaction();
2175 flushRequestsAndLoopN(1);
2176
2177 // now send response EOM on the pending transactions, to finish shutdown
2178 for (auto& handler : handlers) {
2179 handler->sendEOM();
2180 }
2181 flushRequestsAndLoop();
2182 }
2183
TEST_P(HQDownstreamSessionTestHQ,DelayedQPACK)2184 TEST_P(HQDownstreamSessionTestHQ, DelayedQPACK) {
2185 auto req = getGetRequest();
2186 req.getHeaders().add("X-FB-Debug", "rfccffgvtvnenjkbtitkfdufddnvbecu");
2187 auto id = sendRequest(req);
2188 auto handler = addSimpleStrictHandler();
2189 handler->expectHeaders();
2190 handler->expectEOM(
2191 [hdlr = handler.get()] { hdlr->sendReplyWithBody(200, 100); });
2192 handler->expectDetachTransaction();
2193
2194 auto controlStream = encoderWriteBuf_.move();
2195 flushRequestsAndLoopN(1);
2196 encoderWriteBuf_.append(std::move(controlStream));
2197 flushRequestsAndLoop();
2198 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
2199 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
2200 hqSession_->closeWhenIdle();
2201 }
2202
TEST_P(HQDownstreamSessionTestHQ,cancelQPACK)2203 TEST_P(HQDownstreamSessionTestHQ, cancelQPACK) {
2204 auto req = getGetRequest();
2205 req.getHeaders().add("X-FB-Debug", "rfccffgvtvnenjkbtitkfdufddnvbecu");
2206 auto id = sendRequest(req);
2207 auto& request = getStream(id);
2208 // discard part of request, header won't get qpack-ack'd
2209 request.buf.trimEnd(request.buf.chainLength() - 3);
2210 request.readEOF = false;
2211 flushRequestsAndLoopN(1);
2212 socketDriver_->addReadError(id,
2213 HTTP3::ErrorCode::HTTP_REQUEST_CANCELLED,
2214 std::chrono::milliseconds(0));
2215 hqSession_->closeWhenIdle();
2216 flushRequestsAndLoop();
2217 // this will evict all headers, which is only legal if the cancellation is
2218 // emitted and processed.
2219 qpackCodec_.setEncoderHeaderTableSize(0);
2220 EXPECT_EQ(*socketDriver_->streams_[id].error,
2221 HTTP3::ErrorCode::HTTP_REQUEST_REJECTED);
2222 eventBase_.loopOnce();
2223 }
2224
TEST_P(HQDownstreamSessionTestHQ,DelayedQPACKCanceled)2225 TEST_P(HQDownstreamSessionTestHQ, DelayedQPACKCanceled) {
2226 auto req = getGetRequest();
2227 req.getHeaders().add("X-FB-Debug", "rfccffgvtvnenjkbtitkfdufddnvbecu");
2228 auto id = sendRequest(req);
2229 // This request never gets a handler
2230
2231 auto controlStream = encoderWriteBuf_.move();
2232 // receive header block with unsatisfied dep
2233 flushRequestsAndLoopN(1);
2234
2235 // cancel this request
2236 socketDriver_->addReadError(id,
2237 HTTP3::ErrorCode::HTTP_REQUEST_CANCELLED,
2238 std::chrono::milliseconds(0));
2239 flushRequestsAndLoopN(1);
2240
2241 // Now send the dependency
2242 encoderWriteBuf_.append(std::move(controlStream));
2243 flushRequestsAndLoop();
2244
2245 // This used to crash
2246 hqSession_->closeWhenIdle();
2247 }
2248
TEST_P(HQDownstreamSessionTestHQ,DelayedQPACKTimeout)2249 TEST_P(HQDownstreamSessionTestHQ, DelayedQPACKTimeout) {
2250 auto req = getPostRequest(10);
2251 req.getHeaders().add("X-FB-Debug", "rfccffgvtvnenjkbtitkfdufddnvbecu");
2252 auto id = sendRequest(req, false);
2253 auto& request = getStream(id);
2254 folly::IOBufQueue reqTail(folly::IOBufQueue::cacheChainLength());
2255 reqTail.append(request.buf.move());
2256 request.buf.append(reqTail.split(reqTail.chainLength() / 2));
2257 // reqTail now has the second half of request
2258
2259 flushRequests();
2260 testing::StrictMock<MockHTTPHandler> handler;
2261 expectTransactionTimeout(handler, [&] {
2262 request.buf.append(reqTail.move());
2263 auto body = folly::IOBuf::wrapBuffer("\3\3\3\3\3\3\3\3\3\3", 10);
2264 request.codec->generateBody(
2265 request.buf, request.id, std::move(body), HTTPCodec::NoPadding, true);
2266 flushRequests();
2267 });
2268 eventBase_.loop();
2269 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
2270 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
2271 hqSession_->closeWhenIdle();
2272 }
2273
TEST_P(HQDownstreamSessionTestHQ,QPACKEncoderLimited)2274 TEST_P(HQDownstreamSessionTestHQ, QPACKEncoderLimited) {
2275 auto req = getGetRequest();
2276 socketDriver_->getSocket()->setStreamFlowControlWindow(
2277 kQPACKEncoderEgressStreamId, 10);
2278 auto id = sendRequest(req);
2279 auto handler = addSimpleStrictHandler();
2280 handler->expectHeaders();
2281 handler->expectEOM([hdlr = handler.get()] {
2282 HTTPMessage resp;
2283 resp.setStatusCode(200);
2284 resp.getHeaders().add("X-FB-Debug", "rfccffgvtvnenjkbtitkfdufddnvbecu");
2285 hdlr->txn_->sendHeaders(resp);
2286 hdlr->txn_->sendEOM();
2287 });
2288 handler->expectDetachTransaction();
2289 flushRequestsAndLoop();
2290
2291 // QPACK will attempt to index the header, but cannot reference it because
2292 // it runs out of stream flow control
2293 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 30);
2294 hqSession_->closeWhenIdle();
2295 }
2296
TEST_P(HQDownstreamSessionTestHQ,DelayedQPACKStopSendingReset)2297 TEST_P(HQDownstreamSessionTestHQ, DelayedQPACKStopSendingReset) {
2298 auto req = getGetRequest();
2299 req.getHeaders().add("X-FB-Debug", "rfccffgvtvnenjkbtitkfdufddnvbecu");
2300 auto id = sendRequest(req);
2301 // This request never gets a handler
2302
2303 auto controlStream = encoderWriteBuf_.move();
2304 // receive header block with unsatisfied dep
2305 flushRequestsAndLoopN(1);
2306
2307 // cancel this request
2308 socketDriver_->addStopSending(id, HTTP3::ErrorCode::HTTP_REQUEST_CANCELLED);
2309 socketDriver_->addReadError(id,
2310 HTTP3::ErrorCode::HTTP_REQUEST_CANCELLED,
2311 std::chrono::milliseconds(0));
2312 flushRequestsAndLoopN(1);
2313
2314 // Now send the dependency
2315 encoderWriteBuf_.append(std::move(controlStream));
2316 flushRequestsAndLoop();
2317
2318 // This used to crash
2319 hqSession_->closeWhenIdle();
2320 }
2321
TEST_P(HQDownstreamSessionTestHQ,QPACKHeadersTooLarge)2322 TEST_P(HQDownstreamSessionTestHQ, QPACKHeadersTooLarge) {
2323 hqSession_->setEgressSettings({{SettingsId::MAX_HEADER_LIST_SIZE, 60}});
2324 auto req = getGetRequest();
2325 req.getHeaders().add("X-FB-Debug", "rfccffgvtvnenjkbtitkfdufddnvbecu");
2326 auto id = sendRequest(req);
2327 testing::StrictMock<MockHTTPHandler> errHandler;
2328 EXPECT_CALL(getMockController(), getParseErrorHandler(_, _, _))
2329 .WillOnce(Return(&errHandler));
2330 EXPECT_CALL(errHandler, setTransaction(testing::_))
2331 .WillOnce(testing::SaveArg<0>(&errHandler.txn_));
2332 errHandler.expectError([&errHandler](const HTTPException& ex) {
2333 EXPECT_EQ(ex.getHttp3ErrorCode(),
2334 (uint32_t)HTTP3::ErrorCode::HTTP_QPACK_DECOMPRESSION_FAILED);
2335 errHandler.sendReplyWithBody(400, 100);
2336 });
2337 errHandler.expectDetachTransaction();
2338 flushRequestsAndLoopN(2);
2339 // Gets a response
2340 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 30);
2341 EXPECT_EQ(*socketDriver_->streams_[id].error,
2342 (uint32_t)HTTP3::ErrorCode::HTTP_QPACK_DECOMPRESSION_FAILED);
2343 auto decoderStream = socketDriver_->streams_[kQPACKDecoderEgressStreamId]
2344 .writeBuf.front()
2345 ->clone();
2346 decoderStream->coalesce();
2347 // preface, cancel, ici
2348 EXPECT_EQ(decoderStream->computeChainDataLength(), 3);
2349 EXPECT_EQ(decoderStream->data()[1], 0x40); // stream 0 cancelled
2350
2351 // But the conn is still usable
2352 sendRequest();
2353 auto handler = addSimpleStrictHandler();
2354 handler->expectHeaders();
2355 handler->expectEOM([hdlr = handler.get(), this] {
2356 HTTPMessage resp;
2357 resp.setStatusCode(200);
2358 hdlr->txn_->sendHeaders(resp);
2359 hdlr->txn_->sendEOM();
2360 hqSession_->closeWhenIdle();
2361 });
2362 handler->expectDetachTransaction();
2363 flushRequestsAndLoop();
2364 }
2365
TEST_P(HQDownstreamSessionBeforeTransportReadyTest,NotifyPendingShutdown)2366 TEST_P(HQDownstreamSessionBeforeTransportReadyTest, NotifyPendingShutdown) {
2367 hqSession_->notifyPendingShutdown();
2368 SetUpOnTransportReady();
2369 // Give it some time to send the two goaways and receive the delivery callback
2370 flushRequestsAndLoopN(3);
2371 if (IS_HQ) {
2372 // There is a check for this already for all the tests, but adding this to
2373 // make it explicit that SETTINGS should be sent before GOAWAY even in this
2374 // corner case, otherwise the peer will error out the session
2375 EXPECT_EQ(httpCallbacks_.settings, 1);
2376 }
2377 EXPECT_EQ(httpCallbacks_.goaways, 2);
2378 EXPECT_THAT(httpCallbacks_.goawayStreamIds,
2379 ElementsAre(kMaxClientBidiStreamId, 0));
2380 }
2381
2382 // NOTE: a failure for this test may cause an infinite loop in processReadData
TEST_P(HQDownstreamSessionTest,ProcessReadDataOnDetachedStream)2383 TEST_P(HQDownstreamSessionTest, ProcessReadDataOnDetachedStream) {
2384 auto id = sendRequest("/", 0, false);
2385 auto handler = addSimpleStrictHandler();
2386 handler->expectHeaders([&] {
2387 eventBase_.runAfterDelay(
2388 [&] {
2389 // schedule a few events to run in the eventbase back-to-back
2390 // call readAvailable with just the EOF
2391 auto& stream = socketDriver_->streams_[id];
2392 CHECK(!stream.readEOF);
2393 stream.readEOF = true;
2394 CHECK(stream.readCB);
2395 stream.readCB->readAvailable(id);
2396 // now send an error so that the stream gets marked for detach
2397 stream.readCB->readError(
2398 id, std::make_pair(HTTP3::ErrorCode::HTTP_NO_ERROR, folly::none));
2399 // then closeWhenIdle (like during shutdown), this calls
2400 // checkForShutdown that calls checkForDetach and may detach a
2401 // transaction that was added to the pendingProcessReadSet in the same
2402 // loop
2403 hqSession_->closeWhenIdle();
2404 },
2405 10);
2406 });
2407 flushRequestsAndLoopN(1);
2408
2409 handler->expectError();
2410 handler->expectDetachTransaction();
2411
2412 flushRequestsAndLoop();
2413 }
2414
2415 // Test Cases for which Settings are not sent in the test SetUp
2416 using HQDownstreamSessionTestHQNoSettings = HQDownstreamSessionTest;
2417 INSTANTIATE_TEST_CASE_P(HQDownstreamSessionTest,
2418 HQDownstreamSessionTestHQNoSettings,
__anond237a0706902null2419 Values([] {
2420 TestParams tp;
2421 tp.alpn_ = "h3";
2422 tp.shouldSendSettings_ = false;
2423 return tp;
2424 }()),
2425 paramsToTestName);
TEST_P(HQDownstreamSessionTestHQNoSettings,SimpleGet)2426 TEST_P(HQDownstreamSessionTestHQNoSettings, SimpleGet) {
2427 auto idh = checkRequest();
2428 flushRequestsAndLoop();
2429 EXPECT_GT(socketDriver_->streams_[idh.first].writeBuf.chainLength(), 110);
2430 EXPECT_TRUE(socketDriver_->streams_[idh.first].writeEOF);
2431 // Checks that the server response is sent without the QPACK dynamic table
2432 CHECK_EQ(qpackCodec_.getCompressionInfo().ingress.headerTableSize_, 0);
2433
2434 // TODO: Check that QPACK does not use the dynamic table for the response
2435 hqSession_->closeWhenIdle();
2436 }
2437
2438 // This test is checking two different scenarios for different protocol
2439 // - in HQ we already have sent SETTINGS in SetUp, so tests that multiple
2440 // setting frames are not allowed
2441 // - in h1q-fb-v2 tests that receiving even a single SETTINGS frame errors
2442 // out the connection
TEST_P(HQDownstreamSessionTestH1qv2HQ,ExtraSettings)2443 TEST_P(HQDownstreamSessionTestH1qv2HQ, ExtraSettings) {
2444 sendRequest();
2445 auto handler = addSimpleStrictHandler();
2446 handler->expectHeaders();
2447 handler->expectEOM();
2448 handler->expectError([&](const HTTPException& ex) {
2449 EXPECT_EQ(ex.getProxygenError(), kErrorConnection);
2450 });
2451 handler->expectDetachTransaction();
2452 flushRequestsAndLoopN(1);
2453
2454 // Need to use a new codec. Since generating settings twice is
2455 // forbidden
2456 HQControlCodec auxControlCodec_{0x0003,
2457 TransportDirection::UPSTREAM,
2458 StreamDirection::EGRESS,
2459 egressSettings_};
2460 folly::IOBufQueue writeBuf{folly::IOBufQueue::cacheChainLength()};
2461 auxControlCodec_.generateSettings(writeBuf);
2462 socketDriver_->addReadEvent(
2463 connControlStreamId_, writeBuf.move(), milliseconds(0));
2464
2465 flushRequestsAndLoop();
2466
2467 EXPECT_EQ(*socketDriver_->streams_[kConnectionStreamId].error,
2468 HTTP3::ErrorCode::HTTP_FRAME_UNEXPECTED);
2469 }
2470
2471 using HQDownstreamSessionFilterTestHQ = HQDownstreamSessionTestHQ;
TEST_P(HQDownstreamSessionFilterTestHQ,ControlStreamFilters)2472 TEST_P(HQDownstreamSessionFilterTestHQ, ControlStreamFilters) {
2473 uint64_t settingsReceived = 0;
2474
2475 class TestFilter : public PassThroughHTTPCodecFilter {
2476 public:
2477 explicit TestFilter(uint64_t& settingsReceived)
2478 : settingsReceived_(settingsReceived) {
2479 }
2480
2481 void onSettings(const SettingsList& settings) override {
2482 settingsReceived_++;
2483 }
2484 uint64_t& settingsReceived_;
2485 };
2486
2487 hqSession_->addCodecFilter<TestFilter>(settingsReceived);
2488 sendSettings();
2489 flushRequestsAndLoop();
2490 EXPECT_EQ(settingsReceived, 1);
2491 hqSession_->closeWhenIdle();
2492 }
2493
TEST_P(HQDownstreamSessionTestH1q,httpPausedBufferedDetach)2494 TEST_P(HQDownstreamSessionTestH1q, httpPausedBufferedDetach) {
2495 IOBufQueue rst{IOBufQueue::cacheChainLength()};
2496 auto id1 = sendRequest();
2497
2498 InSequence handlerSequence;
2499 auto handler1 = addSimpleStrictHandler();
2500 handler1->expectHeaders();
2501 handler1->expectEOM([&handler1, this, id1] {
2502 // HTTP/3 has an extra grease frame on the first transaction
2503 socketDriver_->setStreamFlowControlWindow(id1, IS_HQ ? 202 : 199);
2504 handler1->sendHeaders(200, 100);
2505 handler1->sendBody(100);
2506 eventBase_.runInLoop([&handler1] {
2507 handler1->expectDetachTransaction();
2508 handler1->sendEOM();
2509 });
2510 });
2511 handler1->expectEgressPaused();
2512 flushRequestsAndLoop();
2513
2514 hqSession_->dropConnection();
2515 }
2516
TEST_P(HQDownstreamSessionTest,onErrorEmptyEnqueued)2517 TEST_P(HQDownstreamSessionTest, onErrorEmptyEnqueued) {
2518 IOBufQueue rst{IOBufQueue::cacheChainLength()};
2519 auto id1 = sendRequest();
2520
2521 InSequence handlerSequence;
2522 auto handler1 = addSimpleStrictHandler();
2523 handler1->expectHeaders();
2524 handler1->expectEOM([&handler1, this, id1] {
2525 handler1->sendHeaders(200, 100);
2526 socketDriver_->setStreamFlowControlWindow(id1, 100);
2527 // After one loop, it will become stream flow control blocked, and txn
2528 // will think it is enqueued, but session will not.
2529 handler1->expectEgressPaused();
2530 handler1->sendBody(101);
2531 eventBase_.runInLoop([&handler1, this, id1] {
2532 handler1->expectError();
2533 handler1->expectDetachTransaction();
2534 socketDriver_->addReadError(id1,
2535 HTTP3::ErrorCode::HTTP_INTERNAL_ERROR,
2536 std::chrono::milliseconds(0));
2537 });
2538 });
2539 flushRequestsAndLoop();
2540
2541 hqSession_->closeWhenIdle();
2542 }
2543
TEST_P(HQDownstreamSessionTest,dropWhilePaused)2544 TEST_P(HQDownstreamSessionTest, dropWhilePaused) {
2545 IOBufQueue rst{IOBufQueue::cacheChainLength()};
2546 sendRequest();
2547
2548 InSequence handlerSequence;
2549 auto handler1 = addSimpleStrictHandler();
2550 handler1->expectHeaders();
2551 handler1->expectEOM([&handler1, this] {
2552 // pause writes
2553 socketDriver_->setConnectionFlowControlWindow(0);
2554 // fill session buffer
2555 handler1->sendReplyWithBody(200, hqSession_->getWriteBufferLimit());
2556 });
2557 flushRequestsAndLoop();
2558
2559 handler1->expectError([&](const HTTPException& ex) {
2560 EXPECT_EQ(ex.getProxygenError(), kErrorDropped);
2561 });
2562 handler1->expectDetachTransaction();
2563 hqSession_->dropConnection();
2564 }
2565
TEST_P(HQDownstreamSessionTestH1qv2HQ,StopSendingOnUnknownUnidirectionalStreams)2566 TEST_P(HQDownstreamSessionTestH1qv2HQ,
2567 StopSendingOnUnknownUnidirectionalStreams) {
2568 auto greaseStreamId = nextUnidirectionalStreamId();
2569 createControlStream(socketDriver_.get(),
2570 greaseStreamId,
2571 proxygen::hq::UnidirectionalStreamType(
2572 *getGreaseId(folly::Random::rand32(16))));
2573 auto idh = checkRequest();
2574 flushRequestsAndLoop();
2575
2576 EXPECT_EQ(*socketDriver_->streams_[greaseStreamId].error,
2577 HTTP3::ErrorCode::HTTP_STREAM_CREATION_ERROR);
2578 // Also check that the request completes correctly
2579 EXPECT_GT(socketDriver_->streams_[idh.first].writeBuf.chainLength(), 110);
2580 EXPECT_TRUE(socketDriver_->streams_[idh.first].writeEOF);
2581 if (IS_HQ) {
2582 // Checks that the server response is sent using the QPACK dynamic table
2583 CHECK_GE(qpackCodec_.getCompressionInfo().ingress.headerTableSize_, 0);
2584 }
2585 hqSession_->closeWhenIdle();
2586 }
2587
TEST_P(HQDownstreamSessionTestHQ,DataOnUnknownControlStream)2588 TEST_P(HQDownstreamSessionTestHQ, DataOnUnknownControlStream) {
2589 auto randPreface =
2590 hq::UnidirectionalStreamType(*getGreaseId(folly::Random::rand32(16)));
2591 // Create unidirectional stream with an unknown stream preface
2592 folly::IOBufQueue writeBuf{folly::IOBufQueue::cacheChainLength()};
2593 generateStreamPreface(writeBuf, randPreface);
2594 socketDriver_->addReadEvent(14, writeBuf.move());
2595 flushRequestsAndLoop();
2596
2597 // Send an extra varint on the same stream, ignoring STOP_SENDING
2598 folly::IOBufQueue writeBuf2{folly::IOBufQueue::cacheChainLength()};
2599 generateStreamPreface(writeBuf2, randPreface);
2600 socketDriver_->addReadEvent(14, writeBuf.move());
2601 flushRequestsAndLoop();
2602 hqSession_->closeWhenIdle();
2603 }
2604
TEST_P(HQDownstreamSessionTestH1qv2HQ,eofControlStream)2605 TEST_P(HQDownstreamSessionTestH1qv2HQ, eofControlStream) {
2606 sendRequest();
2607
2608 InSequence handlerSequence;
2609 auto handler = addSimpleStrictHandler();
2610 handler->expectHeaders();
2611 handler->expectEOM();
2612 handler->expectError([&](const HTTPException& ex) {
2613 EXPECT_EQ(ex.getProxygenError(), kErrorConnection);
2614 });
2615 handler->expectDetachTransaction();
2616 flushRequestsAndLoopN(1);
2617 socketDriver_->addReadEOF(connControlStreamId_);
2618 flushRequestsAndLoop();
2619 }
2620
TEST_P(HQDownstreamSessionTestH1qv2HQ,resetControlStream)2621 TEST_P(HQDownstreamSessionTestH1qv2HQ, resetControlStream) {
2622 sendRequest();
2623
2624 InSequence handlerSequence;
2625 auto handler = addSimpleStrictHandler();
2626 handler->expectHeaders();
2627 handler->expectEOM();
2628 handler->expectError([&](const HTTPException& ex) {
2629 EXPECT_EQ(ex.getProxygenError(), kErrorConnection);
2630 });
2631 handler->expectDetachTransaction();
2632 flushRequestsAndLoopN(1);
2633 socketDriver_->addReadError(connControlStreamId_,
2634 HTTP3::ErrorCode::HTTP_INTERNAL_ERROR);
2635 flushRequestsAndLoop();
2636 EXPECT_EQ(*socketDriver_->streams_[kConnectionStreamId].error,
2637 HTTP3::ErrorCode::HTTP_CLOSED_CRITICAL_STREAM);
2638 }
2639
TEST_P(HQDownstreamSessionTestHQ,controlStreamWriteError)2640 TEST_P(HQDownstreamSessionTestHQ, controlStreamWriteError) {
2641 sendRequest();
2642
2643 InSequence handlerSequence;
2644 auto handler = addSimpleStrictHandler();
2645 handler->expectHeaders();
2646 handler->expectEOM([&handler] { handler->sendHeaders(200, 100); });
2647 handler->expectError([&](const HTTPException& ex) {
2648 EXPECT_EQ(ex.getProxygenError(), kErrorWrite);
2649 });
2650 handler->expectDetachTransaction();
2651 socketDriver_->setWriteError(kQPACKEncoderEgressStreamId);
2652 flushRequestsAndLoop();
2653 EXPECT_EQ(*socketDriver_->streams_[kConnectionStreamId].error,
2654 HTTP3::ErrorCode::HTTP_CLOSED_CRITICAL_STREAM);
2655 }
2656
TEST_P(HQDownstreamSessionTestHQ,TooManyControlStreams)2657 TEST_P(HQDownstreamSessionTestHQ, TooManyControlStreams) {
2658 // This creates a request stream, so that we can check the HTTP3::ErrorCode
2659 // at the end of the test. With no active streams we would drop the
2660 // connection with no error instead.
2661 sendRequest();
2662 InSequence handlerSequence;
2663 auto handler = addSimpleStrictHandler();
2664 handler->expectHeaders();
2665 handler->expectEOM();
2666 handler->expectError([&](const HTTPException& ex) {
2667 EXPECT_EQ(ex.getProxygenError(), kErrorConnection);
2668 });
2669 handler->expectDetachTransaction();
2670 flushRequestsAndLoopN(1);
2671
2672 // Create an extra control stream, that causes the connection to get dropped
2673 folly::IOBufQueue writeBuf{folly::IOBufQueue::cacheChainLength()};
2674 generateStreamPreface(writeBuf, UnidirectionalStreamType::CONTROL);
2675 socketDriver_->addReadEvent(14, writeBuf.move());
2676
2677 flushRequestsAndLoop();
2678 EXPECT_EQ(*socketDriver_->streams_[kConnectionStreamId].error,
2679 HTTP3::ErrorCode::HTTP_STREAM_CREATION_ERROR);
2680 }
2681
TEST_P(HQDownstreamSessionTestHQ,TestGreaseFramePerSession)2682 TEST_P(HQDownstreamSessionTestHQ, TestGreaseFramePerSession) {
2683 // a grease frame will be created in the first bidir stream
2684 auto idh1 = checkRequest();
2685 flushRequestsAndLoop();
2686 EXPECT_GT(socketDriver_->streams_[idh1.first].writeBuf.chainLength(), 110);
2687 FakeHTTPCodecCallback callback1;
2688 std::unique_ptr<HQStreamCodec> upstreamCodec =
2689 std::make_unique<hq::HQStreamCodec>(
2690 idh1.first,
2691 TransportDirection::UPSTREAM,
2692 qpackCodec_,
2693 encoderWriteBuf_,
2694 decoderWriteBuf_,
2695 [] { return std::numeric_limits<uint64_t>::max(); },
2696 ingressSettings_);
2697 upstreamCodec->setCallback(&callback1);
2698 upstreamCodec->onIngress(
2699 *socketDriver_->streams_[idh1.first].writeBuf.front());
2700 EXPECT_EQ(callback1.unknownFrames, 1);
2701 EXPECT_EQ(callback1.greaseFrames, 1);
2702
2703 // no grease frame will be created in the second bidir stream
2704 auto idh2 = checkRequest();
2705 flushRequestsAndLoop();
2706 FakeHTTPCodecCallback callback2;
2707 upstreamCodec->setCallback(&callback2);
2708 upstreamCodec->onIngress(
2709 *socketDriver_->streams_[idh2.first].writeBuf.front());
2710 EXPECT_EQ(callback2.unknownFrames, 0);
2711 EXPECT_EQ(callback2.greaseFrames, 0);
2712 hqSession_->closeWhenIdle();
2713 }
2714
TEST_P(HQDownstreamSessionTest,DelegateResponse)2715 TEST_P(HQDownstreamSessionTest, DelegateResponse) {
2716 if (!IS_HQ) {
2717 hqSession_->closeWhenIdle();
2718 return;
2719 }
2720 auto streamId = sendRequest("/cdn.thing", 0, true);
2721 InSequence handlerSequence;
2722 auto handler = addSimpleStrictHandler();
2723 handler->expectHeaders();
2724 auto dsrRequestSender = std::make_unique<MockQuicDSRRequestSender>();
2725 auto rawDsrSender = dsrRequestSender.get();
2726 handler->expectEOM([&]() {
2727 handler->txn_->setTransportCallback(&transportCallback_);
2728 EXPECT_CALL(*socketDriver_->getSocket(),
2729 setDSRPacketizationRequestSenderRef(_, _))
2730 .Times(1)
2731 .WillOnce(Invoke(
2732 [&](StreamId,
2733 const std::unique_ptr<quic::DSRPacketizationRequestSender>&
2734 sender) {
2735 EXPECT_EQ(rawDsrSender, sender.get());
2736 return folly::unit;
2737 }));
2738 EXPECT_TRUE(handler->sendHeadersWithDelegate(
2739 200, 1000 * 20, std::move(dsrRequestSender)));
2740 EXPECT_GT(transportCallback_.bodyBytesGenerated_, 0);
2741 auto dataFrameHeaderSize = transportCallback_.bodyBytesGenerated_;
2742 EXPECT_TRUE(handler->txn_->isEgressStarted());
2743 handler->txn_->onWriteReady(10 * 1000, 1.0);
2744 EXPECT_EQ(transportCallback_.bodyBytesGenerated_,
2745 10 * 1000 + dataFrameHeaderSize);
2746 // from sendHeadersWithDelegate, to actually doing writeChain and
2747 // writeBufMeta, there is an extra loop. So the verification + abort also
2748 // needs to be placed in a later loop.
2749 eventBase_.runInLoop([&] {
2750 EXPECT_TRUE(transportCallback_.lastByteFlushed_);
2751 // Both onStopSending and terminate the handler can clear the buffered
2752 // BufMetas and make the transaction detachable.
2753 handler->expectDetachTransaction();
2754 hqSession_->onStopSending(streamId,
2755 HTTP3::ErrorCode::HTTP_REQUEST_REJECTED);
2756 });
2757 });
2758 flushRequestsAndLoop();
2759 hqSession_->closeWhenIdle();
2760 }
2761
TEST_P(HQDownstreamSessionTest,H1QRejectsDelegate)2762 TEST_P(HQDownstreamSessionTest, H1QRejectsDelegate) {
2763 if (IS_HQ) {
2764 hqSession_->closeWhenIdle();
2765 return;
2766 }
2767 sendRequest("/cdn.thing", 0, true);
2768 InSequence handlerSequence;
2769 auto handler = addSimpleStrictHandler();
2770 handler->expectHeaders();
2771 auto dsrRequestSender = std::make_unique<MockQuicDSRRequestSender>();
2772 handler->expectEOM([&]() {
2773 EXPECT_FALSE(handler->sendHeadersWithDelegate(
2774 200, 1000 * 20, std::move(dsrRequestSender)));
2775 handler->terminate();
2776 });
2777 handler->expectDetachTransaction();
2778 flushRequestsAndLoop();
2779 hqSession_->closeWhenIdle();
2780 }
2781
TEST_P(HQDownstreamSessionTest,getHTTPPriority)2782 TEST_P(HQDownstreamSessionTest, getHTTPPriority) {
2783 folly::Optional<HTTPPriority> expectedResults = HTTPPriority{1, true};
2784
2785 auto request = getProgressiveGetRequest();
2786 sendRequest(request);
2787 auto handler = addSimpleStrictHandler();
2788 handler->expectHeaders();
2789 handler->expectEOM([&]() {
2790 auto resp = makeResponse(200, 0);
2791
2792 if (!IS_HQ) { // H1Q tests do not support priority
2793 EXPECT_EQ(handler->txn_->getHTTPPriority(), folly::none);
2794 } else {
2795 EXPECT_CALL(*socketDriver_->getSocket(),
2796 getStreamPriority(handler->txn_->getID()))
2797 .WillOnce(Return(quic::Priority(expectedResults.value().urgency,
2798 expectedResults.value().incremental)))
2799 .WillOnce(
2800 Return(folly::makeUnexpected(LocalErrorCode::STREAM_NOT_EXISTS)))
2801 .WillOnce(
2802 Return(quic::Priority(expectedResults.value().urgency,
2803 expectedResults.value().incremental)));
2804
2805 auto urgencyIncremental = handler->txn_->getHTTPPriority().value();
2806 EXPECT_EQ(urgencyIncremental.urgency, expectedResults.value().urgency);
2807 EXPECT_EQ(urgencyIncremental.incremental,
2808 expectedResults.value().incremental);
2809 EXPECT_EQ(handler->txn_->getHTTPPriority(), folly::none);
2810 }
2811 handler->sendRequest(*std::get<0>(resp));
2812 });
2813 handler->expectDetachTransaction();
2814 flushRequestsAndLoop();
2815 hqSession_->closeWhenIdle();
2816 }
2817
2818 /**
2819 * Instantiate the Parametrized test cases
2820 */
2821
2822 // Make sure all the tests keep working with all the supported protocol versions
2823 INSTANTIATE_TEST_CASE_P(HQDownstreamSessionTest,
2824 HQDownstreamSessionTest,
2825 Values(
__anond237a0707c02null2826 [] {
2827 TestParams tp;
2828 tp.alpn_ = "h1q-fb";
2829 return tp;
2830 }(),
__anond237a0707d02null2831 [] {
2832 TestParams tp;
2833 tp.alpn_ = "h1q-fb-v2";
2834 return tp;
2835 }(),
__anond237a0707e02null2836 [] {
2837 TestParams tp;
2838 tp.alpn_ = "h3";
2839 return tp;
2840 }()),
2841 paramsToTestName);
2842
2843 // Instantiate h1q only tests that work on all versions
2844 INSTANTIATE_TEST_CASE_P(HQDownstreamSessionTest,
2845 HQDownstreamSessionTestH1q,
2846 Values(
__anond237a0707f02null2847 [] {
2848 TestParams tp;
2849 tp.alpn_ = "h1q-fb";
2850 return tp;
2851 }(),
__anond237a0708002null2852 [] {
2853 TestParams tp;
2854 tp.alpn_ = "h1q-fb-v2";
2855 return tp;
2856 }()),
2857 paramsToTestName);
2858
2859 // Instantiate common tests for h1q-fb-v2 and hq (goaway)
2860 INSTANTIATE_TEST_CASE_P(HQDownstreamSessionTest,
2861 HQDownstreamSessionTestH1qv2HQ,
2862 Values(
__anond237a0708102null2863 [] {
2864 TestParams tp;
2865 tp.alpn_ = "h1q-fb-v2";
2866 return tp;
2867 }(),
__anond237a0708202null2868 [] {
2869 TestParams tp;
2870 tp.alpn_ = "h3";
2871 return tp;
2872 }()),
2873 paramsToTestName);
2874
2875 INSTANTIATE_TEST_CASE_P(HQDownstreamSessionTest,
2876 HQDownstreamSessionFilterTestHQ,
__anond237a0708302null2877 Values([] {
2878 TestParams tp;
2879 tp.alpn_ = "h3";
2880 tp.createQPACKStreams_ = true;
2881 tp.shouldSendSettings_ = false;
2882 return tp;
2883 }()),
2884 paramsToTestName);
2885
2886 INSTANTIATE_TEST_CASE_P(HQDownstreamSessionBeforeTransportReadyTest,
2887 HQDownstreamSessionBeforeTransportReadyTest,
2888 Values(
__anond237a0708402null2889 [] {
2890 TestParams tp;
2891 tp.alpn_ = "h1q-fb-v2";
2892 return tp;
2893 }(),
__anond237a0708502null2894 [] {
2895 TestParams tp;
2896 tp.alpn_ = "h3";
2897 return tp;
2898 }()),
2899 paramsToTestName);
2900
2901 // Instantiate h1q-fb-v1 only tests
2902 INSTANTIATE_TEST_CASE_P(HQDownstreamSessionTest,
2903 HQDownstreamSessionTestH1qv1,
__anond237a0708602null2904 Values([] {
2905 TestParams tp;
2906 tp.alpn_ = "h1q-fb";
2907 return tp;
2908 }()),
2909 paramsToTestName);
2910
2911 // Instantiate hq only tests
2912 INSTANTIATE_TEST_CASE_P(HQDownstreamSessionTest,
2913 HQDownstreamSessionTestHQ,
__anond237a0708702null2914 Values([] {
2915 TestParams tp;
2916 tp.alpn_ = "h3";
2917 return tp;
2918 }()),
2919 paramsToTestName);
2920
TEST_P(HQDownstreamSessionTestHQPush,SimplePush)2921 TEST_P(HQDownstreamSessionTestHQPush, SimplePush) {
2922 auto id = sendRequest("/", 1);
2923 HTTPMessage promiseReq, res;
2924 promiseReq.getHeaders().set(HTTP_HEADER_HOST, "www.foo.com");
2925 promiseReq.setURL("/");
2926 res.setStatusCode(200);
2927 res.setStatusMessage("Ohai");
2928
2929 auto handler = addSimpleStrictHandler();
2930 StrictMock<MockHTTPPushHandler> pushHandler;
2931 handler->expectHeaders();
2932 HTTPCodec::StreamID pushStreamId = 0;
2933 handler->expectEOM([&] {
2934 // Generate response for the associated stream
2935 handler->txn_->sendHeaders(res);
2936 handler->txn_->sendBody(makeBuf(100));
2937
2938 // Different from H2, this counts as an outgoing stream as soon as the
2939 // txn is created.
2940 // TODO: maybe create the stream lazily when trying to send the real
2941 // headers instead?
2942 auto outgoingStreams = hqSession_->getNumOutgoingStreams();
2943 auto* pushTxn = handler->txn_->newPushedTransaction(&pushHandler);
2944 ASSERT_NE(pushTxn, nullptr);
2945 EXPECT_EQ(hqSession_->getNumOutgoingStreams(), outgoingStreams + 1);
2946 // Generate a push request (PUSH_PROMISE)
2947 pushTxn->sendHeaders(promiseReq);
2948 pushStreamId = pushTxn->getID();
2949 LOG(INFO) << "pushStreamId=" << pushStreamId;
2950 pushTxn->sendHeaders(res);
2951 pushTxn->sendBody(makeBuf(200));
2952 pushTxn->sendEOM();
2953 });
2954 EXPECT_CALL(pushHandler, setTransaction(_))
2955 .WillOnce(Invoke([&](HTTPTransaction* txn) { pushHandler.txn_ = txn; }));
2956 EXPECT_CALL(pushHandler, detachTransaction());
2957
2958 flushRequestsAndLoopN(1);
2959 handler->txn_->sendEOM();
2960 handler->expectDetachTransaction();
2961 flushRequestsAndLoop();
2962 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
2963 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
2964 auto pushIt = pushes_.find(pushStreamId);
2965 ASSERT_TRUE(pushIt != pushes_.end());
2966 EXPECT_GT(socketDriver_->streams_[pushIt->first].writeBuf.chainLength(), 110);
2967 EXPECT_TRUE(socketDriver_->streams_[pushIt->first].writeEOF);
2968 hqSession_->closeWhenIdle();
2969 }
2970
TEST_P(HQDownstreamSessionTestHQPush,PushPriorityCallback)2971 TEST_P(HQDownstreamSessionTestHQPush, PushPriorityCallback) {
2972 auto id = sendRequest("/", 1);
2973 HTTPMessage promiseReq, res;
2974 promiseReq.getHeaders().set(HTTP_HEADER_HOST, "www.foo.com");
2975 promiseReq.setURL("/");
2976 res.setStatusCode(200);
2977 res.setStatusMessage("Ohai");
2978
2979 auto handler = addSimpleStrictHandler();
2980 StrictMock<MockHTTPPushHandler> pushHandler;
2981 handler->expectHeaders();
2982 HTTPCodec::StreamID pushStreamId = 0;
2983 handler->expectEOM([&] {
2984 handler->txn_->sendHeaders(res);
2985 handler->txn_->sendBody(makeBuf(100));
2986
2987 auto outgoingStreams = hqSession_->getNumOutgoingStreams();
2988 auto* pushTxn = handler->txn_->newPushedTransaction(&pushHandler);
2989 ASSERT_NE(pushTxn, nullptr);
2990 EXPECT_EQ(hqSession_->getNumOutgoingStreams(), outgoingStreams + 1);
2991 // Generate a push request (PUSH_PROMISE)
2992 pushTxn->sendHeaders(promiseReq);
2993 pushStreamId = pushTxn->getID();
2994 pushTxn->sendHeaders(res);
2995 pushTxn->sendBody(makeBuf(200));
2996 pushTxn->sendEOM();
2997 });
2998 EXPECT_CALL(pushHandler, setTransaction(_))
2999 .WillOnce(Invoke([&](HTTPTransaction* txn) { pushHandler.txn_ = txn; }));
3000 EXPECT_CALL(pushHandler, detachTransaction());
3001
3002 flushRequestsAndLoopN(1);
3003
3004 // Push stream's priority can be updated either with stream id or push id:
3005 auto pushId = pushes_.find(pushStreamId)->second;
3006 EXPECT_CALL(*socketDriver_->getSocket(),
3007 setStreamPriority(pushStreamId, 6, true));
3008 hqSession_->onPushPriority(pushId, HTTPPriority(6, true));
3009 EXPECT_CALL(*socketDriver_->getSocket(),
3010 setStreamPriority(pushStreamId, 5, true));
3011 hqSession_->onPriority(pushStreamId, HTTPPriority(5, true));
3012
3013 handler->txn_->sendEOM();
3014 handler->expectDetachTransaction();
3015 flushRequestsAndLoop();
3016 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
3017 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
3018 auto pushIt = pushes_.find(pushStreamId);
3019 ASSERT_TRUE(pushIt != pushes_.end());
3020 EXPECT_GT(socketDriver_->streams_[pushIt->first].writeBuf.chainLength(), 110);
3021 EXPECT_TRUE(socketDriver_->streams_[pushIt->first].writeEOF);
3022 hqSession_->closeWhenIdle();
3023 }
3024
TEST_P(HQDownstreamSessionTestHQPush,StopSending)3025 TEST_P(HQDownstreamSessionTestHQPush, StopSending) {
3026 auto id = sendRequest("/", 1);
3027 HTTPMessage req, res;
3028 req.getHeaders().set("HOST", "www.foo.com");
3029 req.setURL("https://www.foo.com/");
3030 res.setStatusCode(200);
3031 res.setStatusMessage("Ohai");
3032
3033 auto handler = addSimpleStrictHandler();
3034 StrictMock<MockHTTPPushHandler> pushHandler;
3035 handler->expectHeaders();
3036 HTTPCodec::StreamID pushStreamId = 0;
3037 handler->expectEOM([&] {
3038 // Generate response for the associated stream
3039 handler->txn_->sendHeaders(res);
3040 handler->txn_->sendBody(makeBuf(100));
3041
3042 // Different from H2, this counts as an outgoing stream as soon as the
3043 // txn is created.
3044 // TODO: maybe create the stream lazily when trying to send the real
3045 // headers instead?
3046 auto outgoingStreams = hqSession_->getNumOutgoingStreams();
3047 auto* pushTxn = handler->txn_->newPushedTransaction(&pushHandler);
3048 ASSERT_NE(pushTxn, nullptr);
3049 EXPECT_EQ(hqSession_->getNumOutgoingStreams(), outgoingStreams + 1);
3050 // Generate a push request (PUSH_PROMISE)
3051 pushTxn->sendHeaders(req);
3052 pushStreamId = pushTxn->getID();
3053 LOG(INFO) << "pushStreamId=" << pushStreamId;
3054 pushTxn->sendHeaders(res);
3055 pushTxn->sendBody(makeBuf(200));
3056 // NO EOM
3057 });
3058 EXPECT_CALL(pushHandler, setTransaction(_))
3059 .WillOnce(Invoke([&](HTTPTransaction* txn) { pushHandler.txn_ = txn; }));
3060 EXPECT_CALL(pushHandler, onError(_));
3061 EXPECT_CALL(pushHandler, detachTransaction());
3062
3063 flushRequestsAndLoopN(1);
3064 handler->txn_->sendEOM();
3065 handler->expectDetachTransaction();
3066 flushRequestsAndLoop();
3067 EXPECT_GT(socketDriver_->streams_[id].writeBuf.chainLength(), 110);
3068 EXPECT_TRUE(socketDriver_->streams_[id].writeEOF);
3069 auto pushIt = pushes_.find(pushStreamId);
3070 ASSERT_TRUE(pushIt != pushes_.end());
3071 EXPECT_GT(socketDriver_->streams_[pushIt->first].writeBuf.chainLength(), 110);
3072 EXPECT_FALSE(socketDriver_->streams_[pushIt->first].writeEOF);
3073 // Cancel the push with stop sending
3074 socketDriver_->addStopSending(pushIt->first,
3075 HTTP3::ErrorCode::HTTP_REQUEST_CANCELLED);
3076 flushRequestsAndLoop();
3077 hqSession_->closeWhenIdle();
3078 }
3079
3080 using DropConnectionInTransportReadyTest =
3081 HQDownstreamSessionBeforeTransportReadyTest;
3082
3083 INSTANTIATE_TEST_CASE_P(DropConnectionInTransportReadyTest,
3084 DropConnectionInTransportReadyTest,
3085 Values(
__anond237a0708e02null3086 [] {
3087 TestParams tp;
3088 tp.alpn_ = "unsupported";
3089 tp.expectOnTransportReady = false;
3090 return tp;
3091 }(),
__anond237a0708f02null3092 [] {
3093 TestParams tp;
3094 tp.alpn_ = "h3";
3095 tp.unidirectionalStreamsCredit = 1;
3096 tp.expectOnTransportReady = false;
3097 return tp;
3098 }(),
__anond237a0709002null3099 [] {
3100 TestParams tp;
3101 tp.alpn_ = "h1q-fb-v2";
3102 tp.unidirectionalStreamsCredit = 0;
3103 tp.expectOnTransportReady = false;
3104 return tp;
3105 }()),
3106 paramsToTestName);
3107 // Instantiate hq server push tests
3108 INSTANTIATE_TEST_CASE_P(HQDownstreamSessionTest,
3109 HQDownstreamSessionTestHQPush,
__anond237a0709102null3110 Values([] {
3111 TestParams tp;
3112 tp.alpn_ = "h3";
3113 tp.unidirectionalStreamsCredit = 8;
3114 return tp;
3115 }()),
3116 paramsToTestName);
3117
3118 // Use this test class for mismatched alpn tests
3119 class HQDownstreamSessionTestUnsupportedAlpn : public HQDownstreamSessionTest {
3120 public:
SetUp()3121 void SetUp() override {
3122 SetUpBase();
3123 }
3124 };
3125
TEST_P(DropConnectionInTransportReadyTest,TransportReadyFailure)3126 TEST_P(DropConnectionInTransportReadyTest, TransportReadyFailure) {
3127 HQDownstreamSession::DestructorGuard dg(hqSession_);
3128 EXPECT_CALL(infoCb_, onTransportReady(_)).Times(0);
3129 EXPECT_CALL(infoCb_, onConnectionError(_))
3130 .WillOnce(Invoke([](const HTTPSessionBase& session) {
3131 const auto hqSession = dynamic_cast<const HQSession*>(&session);
3132 ASSERT_NE(hqSession, nullptr);
3133 ASSERT_NE(hqSession->getQuicSocket(), nullptr);
3134 }));
3135 SetUpOnTransportReady();
3136 EXPECT_EQ(hqSession_->getQuicSocket(), nullptr);
3137 }
3138
TEST_P(HQDownstreamSessionTestHQDeliveryAck,DropConnectionWithDeliveryAckCbSetError)3139 TEST_P(HQDownstreamSessionTestHQDeliveryAck,
3140 DropConnectionWithDeliveryAckCbSetError) {
3141 auto req = getGetRequest();
3142 auto streamId = sendRequest(req);
3143 auto handler = addSimpleStrictHandler();
3144 handler->expectHeaders();
3145
3146 // Start the response.
3147 handler->expectEOM([&]() {
3148 handler->txn_->setTransportCallback(&transportCallback_);
3149 handler->sendHeaders(200, 1723);
3150 });
3151
3152 auto sock = socketDriver_->getSocket();
3153
3154 // This is a copy of the one in MockQuicSocketDriver, only hijacks data stream
3155 // and forces an error.
3156 EXPECT_CALL(*sock,
3157 registerDeliveryCallback(testing::_, testing::_, testing::_))
3158 .WillRepeatedly(
3159 testing::Invoke([streamId, &socketDriver = socketDriver_](
3160 quic::StreamId id,
3161 uint64_t offset,
3162 MockQuicSocket::ByteEventCallback* cb)
3163 -> folly::Expected<folly::Unit, LocalErrorCode> {
3164 if (id == streamId) {
3165 return folly::makeUnexpected(LocalErrorCode::INVALID_OPERATION);
3166 }
3167
3168 socketDriver->checkNotReadOnlyStream(id);
3169 auto it = socketDriver->streams_.find(id);
3170 if (it == socketDriver->streams_.end() ||
3171 it->second.writeOffset >= offset) {
3172 return folly::makeUnexpected(LocalErrorCode::STREAM_NOT_EXISTS);
3173 }
3174 CHECK_NE(it->second.writeState,
3175 MockQuicSocketDriver::StateEnum::CLOSED);
3176 it->second.deliveryCallbacks.push_back({offset, cb});
3177 return folly::unit;
3178 }));
3179
3180 EXPECT_CALL(*handler, onError(_))
3181 .WillOnce(Invoke([](const HTTPException& error) {
3182 EXPECT_TRUE(std::string(error.what())
3183 .find("failed to register delivery callback") !=
3184 std::string::npos);
3185 }));
3186 handler->expectDetachTransaction();
3187
3188 flushRequestsAndLoop();
3189 hqSession_->closeWhenIdle();
3190 }
3191
TEST_P(HQDownstreamSessionTestHQDeliveryAck,TestBodyDeliveryAck)3192 TEST_P(HQDownstreamSessionTestHQDeliveryAck, TestBodyDeliveryAck) {
3193 auto req = getGetRequest();
3194 sendRequest(req);
3195 auto handler = addSimpleStrictHandler();
3196 handler->expectHeaders();
3197
3198 // Start the response.
3199 handler->expectEOM([&]() {
3200 handler->txn_->setTransportCallback(&transportCallback_);
3201 handler->sendHeaders(200, 42);
3202 auto res = handler->txn_->setBodyLastByteDeliveryTrackingEnabled(true);
3203 EXPECT_TRUE(res);
3204 handler->sendBody(42);
3205 handler->sendEOM();
3206 });
3207
3208 handler->expectDetachTransaction();
3209 flushRequestsAndLoop();
3210 EXPECT_EQ(transportCallback_.numBodyBytesDeliveredCalls_, 1);
3211 EXPECT_EQ(transportCallback_.bodyBytesDeliveredOffset_, 41);
3212
3213 hqSession_->closeWhenIdle();
3214 }
3215
TEST_P(HQDownstreamSessionTestHQDeliveryAck,TestBodyDeliveryAckMultiple)3216 TEST_P(HQDownstreamSessionTestHQDeliveryAck, TestBodyDeliveryAckMultiple) {
3217 auto req = getGetRequest();
3218 sendRequest(req);
3219 auto handler = addSimpleStrictHandler();
3220 handler->expectHeaders();
3221
3222 // Start the response.
3223 handler->expectEOM([&]() {
3224 handler->txn_->setTransportCallback(&transportCallback_);
3225 handler->sendHeaders(200, 42 + 17);
3226 auto res = handler->txn_->setBodyLastByteDeliveryTrackingEnabled(true);
3227 EXPECT_TRUE(res);
3228 handler->sendBody(42);
3229 handler->sendBody(17);
3230 handler->sendEOM();
3231 });
3232
3233 handler->expectDetachTransaction();
3234 flushRequestsAndLoop();
3235 EXPECT_EQ(transportCallback_.numBodyBytesDeliveredCalls_, 2);
3236 EXPECT_EQ(transportCallback_.bodyBytesDeliveredOffset_, 41 + 17);
3237
3238 hqSession_->closeWhenIdle();
3239 }
3240
TEST_P(HQDownstreamSessionTestHQDeliveryAck,TestBodyDeliveryErr)3241 TEST_P(HQDownstreamSessionTestHQDeliveryAck, TestBodyDeliveryErr) {
3242 auto req = getGetRequest();
3243 auto streamId = sendRequest(req);
3244 auto handler = addSimpleStrictHandler();
3245 handler->expectHeaders();
3246
3247 // Start the response.
3248 handler->expectEOM([&]() {
3249 handler->txn_->setTransportCallback(&transportCallback_);
3250 handler->sendHeaders(200, 42);
3251 auto res = handler->txn_->setBodyLastByteDeliveryTrackingEnabled(true);
3252 EXPECT_TRUE(res);
3253 });
3254 flushRequestsAndLoop();
3255 EXPECT_TRUE(transportCallback_.lastEgressHeadersByteDelivered_);
3256
3257 // One day, txn_->sendHeaders() will return number of bytes written, and we
3258 // won't need this. For now, H3 frame headers size is 2 bytes.
3259 const uint64_t frameHeaderSize = 2;
3260 const uint64_t streamOffsetAfterHeaders =
3261 (2 * frameHeaderSize) + transportCallback_.headerBytesGenerated_;
3262
3263 auto sock = socketDriver_->getSocket();
3264
3265 // This is a copy of the one in MockQuicSocketDriver, only hijacks data stream
3266 // and forces an error.
3267 EXPECT_CALL(*sock,
3268 registerDeliveryCallback(testing::_, testing::_, testing::_))
3269 .WillRepeatedly(testing::Invoke(
3270 [streamId,
3271 &streamOffsetAfterHeaders = streamOffsetAfterHeaders,
3272 &socketDriver = socketDriver_](quic::StreamId id,
3273 uint64_t offset,
3274 MockQuicSocket::ByteEventCallback* cb)
3275 -> folly::Expected<folly::Unit, LocalErrorCode> {
3276 if (id == streamId && offset > streamOffsetAfterHeaders) {
3277 for (auto& it : socketDriver->streams_) {
3278 auto& stream = it.second;
3279 stream.readState = quic::MockQuicSocketDriver::ERROR;
3280 stream.writeState = quic::MockQuicSocketDriver::ERROR;
3281 }
3282 return folly::makeUnexpected(LocalErrorCode::INVALID_OPERATION);
3283 }
3284
3285 socketDriver->checkNotReadOnlyStream(id);
3286 auto it = socketDriver->streams_.find(id);
3287 if (it == socketDriver->streams_.end() ||
3288 it->second.writeOffset >= offset) {
3289 return folly::makeUnexpected(LocalErrorCode::STREAM_NOT_EXISTS);
3290 }
3291 CHECK_NE(it->second.writeState,
3292 MockQuicSocketDriver::StateEnum::CLOSED);
3293 it->second.deliveryCallbacks.push_back({offset, cb});
3294 return folly::unit;
3295 }));
3296
3297 EXPECT_CALL(*handler, onError(_))
3298 .WillOnce(Invoke([&handler = handler](const HTTPException& error) {
3299 EXPECT_TRUE(std::string(error.what())
3300 .find("failed to register delivery callback") !=
3301 std::string::npos);
3302 handler->txn_->sendAbort();
3303 }));
3304
3305 handler->expectDetachTransaction();
3306
3307 handler->sendBody(42);
3308 flushRequestsAndLoop();
3309 }
3310
TEST_P(HQDownstreamSessionTestHQDeliveryAck,TestBodyDeliveryCancel)3311 TEST_P(HQDownstreamSessionTestHQDeliveryAck, TestBodyDeliveryCancel) {
3312 auto req = getGetRequest();
3313 sendRequest(req);
3314 auto handler = addSimpleStrictHandler();
3315 handler->expectHeaders();
3316
3317 // Start the response.
3318 handler->expectEOM([&]() {
3319 handler->txn_->setTransportCallback(&transportCallback_);
3320 handler->sendHeaders(200, 42);
3321 auto res = handler->txn_->setBodyLastByteDeliveryTrackingEnabled(true);
3322 EXPECT_TRUE(res);
3323 handler->sendBody(42);
3324 // handler->sendEOM();
3325 });
3326
3327 flushRequestsAndLoopN(1);
3328
3329 EXPECT_CALL(*handler, onError(_)).Times(1);
3330 handler->expectDetachTransaction();
3331 socketDriver_->deliverErrorOnAllStreams(
3332 std::make_pair(LocalErrorCode::INVALID_OPERATION, "fake error"));
3333 flushRequestsAndLoop();
3334
3335 EXPECT_EQ(transportCallback_.numBodyBytesCanceledCalls_, 1);
3336 EXPECT_EQ(transportCallback_.bodyBytesCanceledOffset_, 41);
3337 }
3338
3339 INSTANTIATE_TEST_CASE_P(HQDownstreamSessionTest,
3340 HQDownstreamSessionTestHQDeliveryAck,
__anond237a0709c02null3341 Values([] {
3342 TestParams tp;
3343 tp.alpn_ = "h3";
3344 return tp;
3345 }()),
3346 paramsToTestName);
3347