1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
9 #include <proxygen/lib/http/codec/HTTP2Codec.h>
10 
11 #include <folly/io/Cursor.h>
12 #include <proxygen/lib/http/HTTPHeaderSize.h>
13 #include <proxygen/lib/http/HTTPMessage.h>
14 #include <proxygen/lib/http/codec/test/HTTP2FramerTest.h>
15 #include <proxygen/lib/http/codec/test/HTTPParallelCodecTest.h>
16 #include <proxygen/lib/http/codec/test/MockHTTPCodec.h>
17 
18 #include <folly/portability/GMock.h>
19 #include <folly/portability/GTest.h>
20 #include <random>
21 
22 using namespace proxygen;
23 using namespace proxygen::compress;
24 using namespace folly;
25 using namespace folly::io;
26 using namespace std;
27 using namespace testing;
28 
TEST(HTTP2CodecConstantsTest,HTTPContantsAreCommonHeaders)29 TEST(HTTP2CodecConstantsTest, HTTPContantsAreCommonHeaders) {
30   // The purpose of this test is to verify some basic assumptions that should
31   // never change but to make clear that the following http2 header constants
32   // map to the respective common headers.  Should this test ever fail, the
33   // H2Codec would need to be updated in the corresponding places when creating
34   // compress/Header objects.
35   EXPECT_EQ(HTTPCommonHeaders::hash(headers::kMethod),
36             HTTP_HEADER_COLON_METHOD);
37   EXPECT_EQ(HTTPCommonHeaders::hash(headers::kScheme),
38             HTTP_HEADER_COLON_SCHEME);
39   EXPECT_EQ(HTTPCommonHeaders::hash(headers::kPath), HTTP_HEADER_COLON_PATH);
40   EXPECT_EQ(HTTPCommonHeaders::hash(headers::kAuthority),
41             HTTP_HEADER_COLON_AUTHORITY);
42   EXPECT_EQ(HTTPCommonHeaders::hash(headers::kStatus),
43             HTTP_HEADER_COLON_STATUS);
44 }
45 
46 class HTTP2CodecTest : public HTTPParallelCodecTest {
47  public:
HTTP2CodecTest()48   HTTP2CodecTest() : HTTPParallelCodecTest(upstreamCodec_, downstreamCodec_) {
49     upstreamCodec_.enableDoubleGoawayDrain();
50     downstreamCodec_.enableDoubleGoawayDrain();
51   }
52 
SetUp()53   void SetUp() override {
54     HTTPParallelCodecTest::SetUp();
55   }
56   void testHeaderListSize(bool oversized);
57   void testFrameSizeLimit(bool oversized);
58 
writeHeaders(folly::IOBufQueue & writeBuf,std::unique_ptr<folly::IOBuf> headers,uint32_t stream,folly::Optional<http2::PriorityUpdate> priority,folly::Optional<uint8_t> padding,bool endStream,bool endHeaders)59   void writeHeaders(folly::IOBufQueue& writeBuf,
60                     std::unique_ptr<folly::IOBuf> headers,
61                     uint32_t stream,
62                     folly::Optional<http2::PriorityUpdate> priority,
63                     folly::Optional<uint8_t> padding,
64                     bool endStream,
65                     bool endHeaders) {
66     auto headersLen = headers ? headers->computeChainDataLength() : 0;
67     auto headerSize = http2::calculatePreHeaderBlockSize(
68         false, false, priority.has_value(), padding.has_value());
69     auto header = writeBuf.preallocate(headerSize, 32);
70     writeBuf.postallocate(headerSize);
71     writeBuf.append(std::move(headers));
72     http2::writeHeaders((uint8_t*)header.first,
73                         header.second,
74                         writeBuf,
75                         headersLen,
76                         stream,
77                         priority,
78                         padding,
79                         endStream,
80                         endHeaders);
81   }
82 
83  protected:
84   HTTP2Codec upstreamCodec_{TransportDirection::UPSTREAM};
85   HTTP2Codec downstreamCodec_{TransportDirection::DOWNSTREAM};
86 };
87 
TEST_F(HTTP2CodecTest,IgnoreUnknownSettings)88 TEST_F(HTTP2CodecTest, IgnoreUnknownSettings) {
89   auto numSettings = downstreamCodec_.getIngressSettings()->getNumSettings();
90   std::deque<SettingPair> settings;
91   for (uint32_t i = 200; i < (200 + 1024); i++) {
92     settings.push_back(SettingPair(SettingsId(i), i));
93   }
94   http2::writeSettings(output_, settings);
95   parse();
96 
97   EXPECT_EQ(callbacks_.settings, 1);
98   EXPECT_EQ(callbacks_.sessionErrors, 0);
99   EXPECT_EQ(numSettings,
100             downstreamCodec_.getIngressSettings()->getNumSettings());
101 }
102 
TEST_F(HTTP2CodecTest,NoExHeaders)103 TEST_F(HTTP2CodecTest, NoExHeaders) {
104   // do not emit ENABLE_EX_HEADERS setting, if disabled
105   SetUpUpstreamTest();
106 
107   EXPECT_EQ(callbacks_.settings, 0);
108   EXPECT_EQ(callbacks_.numSettings, 0);
109   EXPECT_EQ(false, downstreamCodec_.supportsExTransactions());
110 
111   parseUpstream();
112 
113   EXPECT_EQ(callbacks_.settings, 1);
114   // only 3 standard settings: HEADER_TABLE_SIZE, ENABLE_PUSH, MAX_FRAME_SIZE.
115   EXPECT_EQ(callbacks_.numSettings, 3);
116   EXPECT_EQ(false, downstreamCodec_.supportsExTransactions());
117 }
118 
TEST_F(HTTP2CodecTest,IgnoreExHeadersSetting)119 TEST_F(HTTP2CodecTest, IgnoreExHeadersSetting) {
120   // disable EX_HEADERS on egress
121   downstreamCodec_.getEgressSettings()->setSetting(
122       SettingsId::ENABLE_EX_HEADERS, 0);
123   auto ptr = downstreamCodec_.getEgressSettings()->getSetting(
124       SettingsId::ENABLE_EX_HEADERS);
125   EXPECT_EQ(0, ptr->value);
126 
127   ptr = downstreamCodec_.getIngressSettings()->getSetting(
128       SettingsId::ENABLE_EX_HEADERS);
129   EXPECT_EQ(nullptr, ptr);
130   EXPECT_EQ(false, downstreamCodec_.supportsExTransactions());
131 
132   // attempt to enable EX_HEADERS on ingress
133   http2::writeSettings(output_,
134                        {SettingPair(SettingsId::ENABLE_EX_HEADERS, 1)});
135   parse();
136 
137   EXPECT_EQ(callbacks_.settings, 1);
138   EXPECT_EQ(callbacks_.sessionErrors, 0);
139   ptr = downstreamCodec_.getIngressSettings()->getSetting(
140       SettingsId::ENABLE_EX_HEADERS);
141   EXPECT_EQ(nullptr, ptr);
142   EXPECT_EQ(false, downstreamCodec_.supportsExTransactions());
143 
144   // attempt to disable EX_HEADERS on ingress
145   callbacks_.reset();
146   http2::writeSettings(output_,
147                        {SettingPair(SettingsId::ENABLE_EX_HEADERS, 0)});
148   parse();
149 
150   EXPECT_EQ(callbacks_.settings, 1);
151   EXPECT_EQ(callbacks_.sessionErrors, 0);
152   ptr = downstreamCodec_.getIngressSettings()->getSetting(
153       SettingsId::ENABLE_EX_HEADERS);
154   EXPECT_EQ(nullptr, ptr);
155   EXPECT_EQ(false, downstreamCodec_.supportsExTransactions());
156 }
157 
TEST_F(HTTP2CodecTest,EnableExHeadersSetting)158 TEST_F(HTTP2CodecTest, EnableExHeadersSetting) {
159   // enable EX_HEADERS on egress
160   downstreamCodec_.getEgressSettings()->setSetting(
161       SettingsId::ENABLE_EX_HEADERS, 1);
162 
163   auto ptr = downstreamCodec_.getEgressSettings()->getSetting(
164       SettingsId::ENABLE_EX_HEADERS);
165   EXPECT_EQ(1, ptr->value);
166 
167   ptr = downstreamCodec_.getIngressSettings()->getSetting(
168       SettingsId::ENABLE_EX_HEADERS);
169   EXPECT_EQ(nullptr, ptr);
170   EXPECT_EQ(false, downstreamCodec_.supportsExTransactions());
171 
172   // attempt to enable EX_HEADERS on ingress
173   http2::writeSettings(output_,
174                        {SettingPair(SettingsId::ENABLE_EX_HEADERS, 1)});
175   parse();
176 
177   EXPECT_EQ(callbacks_.settings, 1);
178   EXPECT_EQ(callbacks_.sessionErrors, 0);
179   ptr = downstreamCodec_.getIngressSettings()->getSetting(
180       SettingsId::ENABLE_EX_HEADERS);
181   EXPECT_EQ(1, ptr->value);
182   EXPECT_EQ(true, downstreamCodec_.supportsExTransactions());
183 
184   // attempt to disable EX_HEADERS on ingress
185   callbacks_.reset();
186   http2::writeSettings(output_,
187                        {SettingPair(SettingsId::ENABLE_EX_HEADERS, 0)});
188   parse();
189 
190   EXPECT_EQ(callbacks_.settings, 1);
191   EXPECT_EQ(callbacks_.sessionErrors, 0);
192   ptr = downstreamCodec_.getIngressSettings()->getSetting(
193       SettingsId::ENABLE_EX_HEADERS);
194   EXPECT_EQ(0, ptr->value);
195   EXPECT_EQ(false, downstreamCodec_.supportsExTransactions());
196 }
197 
TEST_F(HTTP2CodecTest,InvalidExHeadersSetting)198 TEST_F(HTTP2CodecTest, InvalidExHeadersSetting) {
199   // enable EX_HEADERS on egress
200   downstreamCodec_.getEgressSettings()->setSetting(
201       SettingsId::ENABLE_EX_HEADERS, 1);
202 
203   // attempt to set a invalid ENABLE_EX_HEADERS value
204   http2::writeSettings(output_,
205                        {SettingPair(SettingsId::ENABLE_EX_HEADERS, 110)});
206   parse();
207 
208   EXPECT_EQ(callbacks_.sessionErrors, 1);
209 }
210 
TEST_F(HTTP2CodecTest,BasicHeader)211 TEST_F(HTTP2CodecTest, BasicHeader) {
212   HTTPMessage req = getGetRequest("/guacamole");
213   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
214   req.getHeaders().add("tab-hdr", "coolio\tv2");
215   // Connection header will get dropped
216   req.getHeaders().add(HTTP_HEADER_CONNECTION, "Love");
217   req.setSecure(true);
218   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
219 
220   parse();
221   callbacks_.expectMessage(true, 3, "/guacamole");
222   EXPECT_TRUE(callbacks_.msg->isSecure());
223   const auto& headers = callbacks_.msg->getHeaders();
224   EXPECT_EQ("coolio", headers.getSingleOrEmpty(HTTP_HEADER_USER_AGENT));
225   EXPECT_EQ("coolio\tv2", headers.getSingleOrEmpty("tab-hdr"));
226   EXPECT_EQ("www.foo.com", headers.getSingleOrEmpty(HTTP_HEADER_HOST));
227 }
228 
TEST_F(HTTP2CodecTest,GenerateExtraHeaders)229 TEST_F(HTTP2CodecTest, GenerateExtraHeaders) {
230   HTTPMessage req = getGetRequest("/fish_taco");
231   req.getHeaders().add(HTTP_HEADER_CONTENT_LENGTH, "157");
232   HTTPHeaders extraHeaders;
233   extraHeaders.add(HTTP_HEADER_PRIORITY, "u=0");
234   upstreamCodec_.generateHeader(
235       output_, 1, req, true, nullptr /* headerSize */, std::move(extraHeaders));
236 
237   parse();
238   // There is also a HOST header
239   callbacks_.expectMessage(true, 3, "/fish_taco");
240   const auto& headers = callbacks_.msg->getHeaders();
241   EXPECT_EQ("157", headers.getSingleOrEmpty(HTTP_HEADER_CONTENT_LENGTH));
242   EXPECT_EQ("u=0", headers.getSingleOrEmpty(HTTP_HEADER_PRIORITY));
243 }
244 
TEST_F(HTTP2CodecTest,RequestFromServer)245 TEST_F(HTTP2CodecTest, RequestFromServer) {
246   // this is to test EX_HEADERS frame, which carrys the HTTP request initiated
247   // by server side
248   upstreamCodec_.getEgressSettings()->setSetting(SettingsId::ENABLE_EX_HEADERS,
249                                                  1);
250   SetUpUpstreamTest();
251   proxygen::http2::writeSettings(
252       output_, {{proxygen::SettingsId::ENABLE_EX_HEADERS, 1}});
253 
254   HTTPMessage req = getGetRequest("/guacamole");
255   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
256   req.getHeaders().add("tab-hdr", "coolio\tv2");
257   // Connection header will get dropped
258   req.getHeaders().add(HTTP_HEADER_CONNECTION, "Love");
259   req.setSecure(true);
260 
261   HTTPCodec::StreamID stream = folly::Random::rand32(10, 1024) * 2;
262   HTTPCodec::StreamID controlStream = folly::Random::rand32(10, 1024) * 2 + 1;
263   upstreamCodec_.generateExHeader(
264       output_, stream, req, HTTPCodec::ExAttributes(controlStream, true), true);
265 
266   parseUpstream();
267   EXPECT_EQ(controlStream, callbacks_.controlStreamId);
268   EXPECT_TRUE(callbacks_.isUnidirectional);
269   callbacks_.expectMessage(true, 3, "/guacamole");
270   EXPECT_TRUE(callbacks_.msg->isSecure());
271   const auto& headers = callbacks_.msg->getHeaders();
272   EXPECT_EQ("coolio", headers.getSingleOrEmpty(HTTP_HEADER_USER_AGENT));
273   EXPECT_EQ("coolio\tv2", headers.getSingleOrEmpty("tab-hdr"));
274   EXPECT_EQ("www.foo.com", headers.getSingleOrEmpty(HTTP_HEADER_HOST));
275 }
276 
TEST_F(HTTP2CodecTest,ResponseFromClient)277 TEST_F(HTTP2CodecTest, ResponseFromClient) {
278   // this is to test EX_HEADERS frame, which carrys the HTTP response replied by
279   // client side
280   downstreamCodec_.getEgressSettings()->setSetting(
281       SettingsId::ENABLE_EX_HEADERS, 1);
282   proxygen::http2::writeSettings(
283       output_, {{proxygen::SettingsId::ENABLE_EX_HEADERS, 1}});
284 
285   HTTPMessage resp;
286   resp.setStatusCode(200);
287   resp.setStatusMessage("nifty-nice");
288   resp.getHeaders().add(HTTP_HEADER_CONTENT_TYPE, "x-coolio");
289 
290   HTTPCodec::StreamID stream = folly::Random::rand32(10, 1024) * 2;
291   HTTPCodec::StreamID controlStream = folly::Random::rand32(10, 1024) * 2 + 1;
292   downstreamCodec_.generateExHeader(
293       output_,
294       stream,
295       resp,
296       HTTPCodec::ExAttributes(controlStream, true),
297       true);
298 
299   parse();
300   EXPECT_EQ(controlStream, callbacks_.controlStreamId);
301   EXPECT_TRUE(callbacks_.isUnidirectional);
302   EXPECT_EQ("OK", callbacks_.msg->getStatusMessage());
303   callbacks_.expectMessage(true, 2, 200);
304   const auto& headers = callbacks_.msg->getHeaders();
305   EXPECT_EQ("OK", callbacks_.msg->getStatusMessage());
306   EXPECT_TRUE(callbacks_.msg->getHeaders().exists(HTTP_HEADER_DATE));
307   EXPECT_EQ("x-coolio", headers.getSingleOrEmpty(HTTP_HEADER_CONTENT_TYPE));
308 }
309 
TEST_F(HTTP2CodecTest,ExHeadersWithPriority)310 TEST_F(HTTP2CodecTest, ExHeadersWithPriority) {
311   downstreamCodec_.getEgressSettings()->setSetting(
312       SettingsId::ENABLE_EX_HEADERS, 1);
313   proxygen::http2::writeSettings(
314       output_, {{proxygen::SettingsId::ENABLE_EX_HEADERS, 1}});
315 
316   auto req = getGetRequest();
317   // Test empty path
318   req.setURL("");
319   auto pri = HTTPMessage::HTTP2Priority(0, false, 7);
320   req.setHTTP2Priority(pri);
321   upstreamCodec_.generateExHeader(
322       output_, 3, req, HTTPCodec::ExAttributes(1, true));
323 
324   parse();
325   EXPECT_EQ(callbacks_.msg->getHTTP2Priority(), pri);
326   EXPECT_EQ(callbacks_.streamErrors, 0);
327   EXPECT_EQ(callbacks_.sessionErrors, 0);
328 }
329 
TEST_F(HTTP2CodecTest,DuplicateExHeaders)330 TEST_F(HTTP2CodecTest, DuplicateExHeaders) {
331   downstreamCodec_.getEgressSettings()->setSetting(
332       SettingsId::ENABLE_EX_HEADERS, 1);
333   proxygen::http2::writeSettings(
334       output_, {{proxygen::SettingsId::ENABLE_EX_HEADERS, 1}});
335 
336   auto req = getGetRequest();
337   upstreamCodec_.generateExHeader(
338       output_, 3, req, HTTPCodec::ExAttributes(1, true), /*eom=*/false);
339   upstreamCodec_.generateExHeader(
340       output_, 3, req, HTTPCodec::ExAttributes(1, true), /*eom=*/true);
341 
342   parse();
343   EXPECT_EQ(callbacks_.streamErrors, 0);
344   EXPECT_EQ(callbacks_.sessionErrors, 1);
345 }
346 
TEST_F(HTTP2CodecTest,IgnoreExHeadersIfNotEnabled)347 TEST_F(HTTP2CodecTest, IgnoreExHeadersIfNotEnabled) {
348   downstreamCodec_.getEgressSettings()->setSetting(
349       SettingsId::ENABLE_EX_HEADERS, 0);
350 
351   HTTPMessage req = getGetRequest("/guacamole");
352   downstreamCodec_.generateExHeader(
353       output_, 3, req, HTTPCodec::ExAttributes(1, true));
354 
355   parse();
356   EXPECT_EQ(callbacks_.streamErrors, 0);
357   EXPECT_EQ(callbacks_.sessionErrors, 0);
358 }
359 
TEST_F(HTTP2CodecTest,BadHeaders)360 TEST_F(HTTP2CodecTest, BadHeaders) {
361   static const std::string v1("GET");
362   static const std::string v2("/");
363   static const std::string v3("http");
364   static const std::string v4("foo.com");
365   static const vector<proxygen::compress::Header> reqHeaders = {
366       Header::makeHeaderForTest(headers::kMethod, v1),
367       Header::makeHeaderForTest(headers::kPath, v2),
368       Header::makeHeaderForTest(headers::kScheme, v3),
369       Header::makeHeaderForTest(headers::kAuthority, v4),
370   };
371 
372   HPACKCodec headerCodec(TransportDirection::UPSTREAM);
373   HTTPCodec::StreamID stream = 1;
374   // missing fields (missing authority is OK)
375   for (size_t i = 0; i < reqHeaders.size(); i++, stream += 2) {
376     std::vector<proxygen::compress::Header> allHeaders = reqHeaders;
377     allHeaders.erase(allHeaders.begin() + i);
378     auto encodedHeaders = headerCodec.encode(allHeaders);
379     writeHeaders(output_,
380                  std::move(encodedHeaders),
381                  stream,
382                  folly::none,
383                  http2::kNoPadding,
384                  true,
385                  true);
386   }
387   // dup fields
388   std::string v("foomonkey");
389   for (size_t i = 0; i < reqHeaders.size(); i++, stream += 2) {
390     std::vector<proxygen::compress::Header> allHeaders = reqHeaders;
391     auto h = allHeaders[i];
392     h.value = &v;
393     allHeaders.push_back(h);
394     auto encodedHeaders = headerCodec.encode(allHeaders);
395     writeHeaders(output_,
396                  std::move(encodedHeaders),
397                  stream,
398                  folly::none,
399                  http2::kNoPadding,
400                  true,
401                  true);
402   }
403 
404   parse();
405   EXPECT_EQ(callbacks_.messageBegin, 1 + 7);
406   EXPECT_EQ(callbacks_.headersComplete, 1);
407   EXPECT_EQ(callbacks_.messageComplete, 1);
408   EXPECT_EQ(callbacks_.streamErrors, 7);
409   EXPECT_EQ(callbacks_.sessionErrors, 0);
410 }
411 
TEST_F(HTTP2CodecTest,BadPseudoHeaders)412 TEST_F(HTTP2CodecTest, BadPseudoHeaders) {
413   static const std::string v1("POST");
414   static const std::string v2("http");
415   static const std::string n3("foo");
416   static const std::string v3("bar");
417   static const std::string v4("/");
418   static const vector<proxygen::compress::Header> reqHeaders = {
419       Header::makeHeaderForTest(headers::kMethod, v1),
420       Header::makeHeaderForTest(headers::kScheme, v2),
421       Header::makeHeaderForTest(n3, v3),
422       Header::makeHeaderForTest(headers::kPath, v4),
423   };
424 
425   HPACKCodec headerCodec(TransportDirection::UPSTREAM);
426   HTTPCodec::StreamID stream = 1;
427   std::vector<proxygen::compress::Header> allHeaders = reqHeaders;
428   auto encodedHeaders = headerCodec.encode(allHeaders);
429   writeHeaders(output_,
430                std::move(encodedHeaders),
431                stream,
432                folly::none,
433                http2::kNoPadding,
434                true,
435                true);
436 
437   parse();
438   EXPECT_EQ(callbacks_.messageBegin, 1);
439   EXPECT_EQ(callbacks_.headersComplete, 0);
440   EXPECT_EQ(callbacks_.messageComplete, 0);
441   EXPECT_EQ(callbacks_.streamErrors, 1);
442   EXPECT_EQ(callbacks_.lastParseError->getProxygenError(), kErrorParseHeader);
443   EXPECT_EQ(callbacks_.sessionErrors, 0);
444 }
445 
TEST_F(HTTP2CodecTest,BadHeaderValues)446 TEST_F(HTTP2CodecTest, BadHeaderValues) {
447   static const std::string v1("--1");
448   static const std::string v2("\13\10protocol-attack");
449   static const std::string v3("\13");
450   static const std::string v4("abc.com\\13\\10");
451   static const vector<proxygen::compress::Header> reqHeaders = {
452       Header::makeHeaderForTest(headers::kMethod, v1),
453       Header::makeHeaderForTest(headers::kPath, v2),
454       Header::makeHeaderForTest(headers::kScheme, v3),
455       Header::makeHeaderForTest(headers::kAuthority, v4),
456   };
457 
458   HPACKCodec headerCodec(TransportDirection::UPSTREAM);
459   HTTPCodec::StreamID stream = 1;
460   for (size_t i = 0; i < reqHeaders.size(); i++, stream += 2) {
461     std::vector<proxygen::compress::Header> allHeaders;
462     allHeaders.push_back(reqHeaders[i]);
463     auto encodedHeaders = headerCodec.encode(allHeaders);
464     writeHeaders(output_,
465                  std::move(encodedHeaders),
466                  stream,
467                  folly::none,
468                  http2::kNoPadding,
469                  true,
470                  true);
471   }
472 
473   parse();
474   EXPECT_EQ(callbacks_.messageBegin, 4);
475   EXPECT_EQ(callbacks_.headersComplete, 0);
476   EXPECT_EQ(callbacks_.messageComplete, 0);
477   EXPECT_EQ(callbacks_.streamErrors, 4);
478   EXPECT_EQ(callbacks_.lastParseError->getProxygenError(), kErrorParseHeader);
479   EXPECT_EQ(callbacks_.sessionErrors, 0);
480 }
481 
TEST_F(HTTP2CodecTest,HighAscii)482 TEST_F(HTTP2CodecTest, HighAscii) {
483   auto g =
484       folly::makeGuard([this] { downstreamCodec_.setStrictValidation(false); });
485   downstreamCodec_.setStrictValidation(true);
486   HTTPMessage req1 = getGetRequest("/guacamole\xff");
487   upstreamCodec_.generateHeader(
488       output_, 1, req1, true, nullptr /* headerSize */);
489   HTTPMessage req2 = getGetRequest("/guacamole");
490   req2.getHeaders().set(HTTP_HEADER_HOST, std::string("foo.com\xff"));
491   upstreamCodec_.generateHeader(
492       output_, 3, req2, true, nullptr /* headerSize */);
493   HTTPMessage req3 = getGetRequest("/guacamole");
494   req3.getHeaders().set(folly::StringPiece("Foo\xff"), "bar");
495   upstreamCodec_.generateHeader(
496       output_, 5, req3, true, nullptr /* headerSize */);
497   HTTPMessage req4 = getGetRequest("/guacamole");
498   req4.getHeaders().set("Foo", std::string("bar\xff"));
499   upstreamCodec_.generateHeader(
500       output_, 7, req4, true, nullptr /* headerSize */);
501 
502   parse();
503   EXPECT_EQ(callbacks_.messageBegin, 4);
504   EXPECT_EQ(callbacks_.headersComplete, 0);
505   EXPECT_EQ(callbacks_.messageComplete, 0);
506   EXPECT_EQ(callbacks_.streamErrors, 4);
507   EXPECT_EQ(callbacks_.lastParseError->getProxygenError(), kErrorParseHeader);
508   EXPECT_EQ(callbacks_.sessionErrors, 0);
509 
510   HTTPMessage req5 = getGetRequest("/guacamole");
511   req5.getHeaders().set(HTTP_HEADER_USER_AGENT, "ꪶ��ꫂ_����������_��2");
512   upstreamCodec_.generateHeader(
513       output_, 9, req5, true, nullptr /* headerSize */);
514   callbacks_.reset();
515   parse();
516   EXPECT_EQ(callbacks_.messageBegin, 1);
517   EXPECT_EQ(callbacks_.headersComplete, 0);
518   EXPECT_EQ(callbacks_.messageComplete, 0);
519   EXPECT_EQ(callbacks_.streamErrors, 1);
520   EXPECT_EQ(callbacks_.lastParseError->getProxygenError(), kErrorParseHeader);
521   EXPECT_EQ(callbacks_.sessionErrors, 0);
522 }
523 
TEST_F(HTTP2CodecTest,EmptyPath)524 TEST_F(HTTP2CodecTest, EmptyPath) {
525   auto g =
526       folly::makeGuard([this] { downstreamCodec_.setStrictValidation(false); });
527   downstreamCodec_.setStrictValidation(true);
528   HTTPMessage req1 = getGetRequest("");
529   upstreamCodec_.generateHeader(
530       output_, 1, req1, true, nullptr /* headerSize */);
531   parse();
532   EXPECT_EQ(callbacks_.messageBegin, 1);
533   EXPECT_EQ(callbacks_.headersComplete, 0);
534   EXPECT_EQ(callbacks_.messageComplete, 0);
535   EXPECT_EQ(callbacks_.streamErrors, 1);
536   EXPECT_EQ(callbacks_.lastParseError->getProxygenError(), kErrorParseHeader);
537   EXPECT_EQ(callbacks_.sessionErrors, 0);
538 }
539 
540 /**
541  * Ingress bytes with an empty header name
542  */
543 const uint8_t kBufEmptyHeader[] = {
544     0x00, 0x00, 0x1d, 0x01, 0x04, 0x00, 0x00, 0x00, 0x01, 0x82,
545     0x87, 0x44, 0x87, 0x62, 0x6b, 0x46, 0x41, 0xd2, 0x7a, 0x0b,
546     0x41, 0x89, 0xf1, 0xe3, 0xc2, 0xf2, 0x9c, 0xeb, 0x90, 0xf4,
547     0xff, 0x40, 0x80, 0x84, 0x2d, 0x35, 0xa7, 0xd7};
548 
TEST_F(HTTP2CodecTest,EmptyHeaderName)549 TEST_F(HTTP2CodecTest, EmptyHeaderName) {
550   output_.append(IOBuf::copyBuffer(kBufEmptyHeader, sizeof(kBufEmptyHeader)));
551   parse();
552   EXPECT_EQ(callbacks_.messageBegin, 1);
553   EXPECT_EQ(callbacks_.headersComplete, 0);
554   EXPECT_EQ(callbacks_.messageComplete, 0);
555   EXPECT_EQ(callbacks_.streamErrors, 1);
556   EXPECT_EQ(callbacks_.lastParseError->getProxygenError(), kErrorParseHeader);
557   EXPECT_EQ(callbacks_.sessionErrors, 0);
558 }
559 
TEST_F(HTTP2CodecTest,BasicConnect)560 TEST_F(HTTP2CodecTest, BasicConnect) {
561   std::string authority = "myhost:1234";
562   HTTPMessage request;
563   request.setMethod(HTTPMethod::CONNECT);
564   request.getHeaders().add(proxygen::HTTP_HEADER_HOST, authority);
565   upstreamCodec_.generateHeader(output_, 1, request, false /* eom */);
566 
567   parse();
568   callbacks_.expectMessage(false, 1, "");
569   EXPECT_EQ(HTTPMethod::CONNECT, callbacks_.msg->getMethod());
570   const auto& headers = callbacks_.msg->getHeaders();
571   EXPECT_EQ(authority, headers.getSingleOrEmpty(proxygen::HTTP_HEADER_HOST));
572 }
573 
TEST_F(HTTP2CodecTest,BadConnect)574 TEST_F(HTTP2CodecTest, BadConnect) {
575   std::string v1 = "CONNECT";
576   std::string v2 = "somehost:576";
577   std::vector<proxygen::compress::Header> goodHeaders = {
578       Header::makeHeaderForTest(headers::kMethod, v1),
579       Header::makeHeaderForTest(headers::kAuthority, v2),
580   };
581 
582   // See https://tools.ietf.org/html/rfc7540#section-8.3
583   std::string v3 = "/foobar";
584   std::vector<proxygen::compress::Header> badHeaders = {
585       Header::makeHeaderForTest(headers::kScheme, headers::kHttp),
586       Header::makeHeaderForTest(headers::kPath, v3),
587   };
588 
589   HPACKCodec headerCodec(TransportDirection::UPSTREAM);
590   HTTPCodec::StreamID stream = 1;
591 
592   for (size_t i = 0; i < badHeaders.size(); i++, stream += 2) {
593     auto allHeaders = goodHeaders;
594     allHeaders.push_back(badHeaders[i]);
595     auto encodedHeaders = headerCodec.encode(allHeaders);
596     writeHeaders(output_,
597                  std::move(encodedHeaders),
598                  stream,
599                  folly::none,
600                  http2::kNoPadding,
601                  true,
602                  true);
603   }
604 
605   parse();
606   EXPECT_EQ(callbacks_.messageBegin, badHeaders.size());
607   EXPECT_EQ(callbacks_.headersComplete, 0);
608   EXPECT_EQ(callbacks_.messageComplete, 0);
609   EXPECT_EQ(callbacks_.streamErrors, badHeaders.size());
610   EXPECT_EQ(callbacks_.sessionErrors, 0);
611 }
612 
testHeaderListSize(bool oversized)613 void HTTP2CodecTest::testHeaderListSize(bool oversized) {
614   if (oversized) {
615     auto settings = downstreamCodec_.getEgressSettings();
616     settings->setSetting(SettingsId::MAX_HEADER_LIST_SIZE, 37);
617   }
618 
619   HTTPMessage req = getGetRequest("/guacamole");
620   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
621   req.getHeaders().add("x-long-long-header",
622                        "supercalafragalisticexpialadoshus");
623   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
624 
625   parse();
626   // session error
627   EXPECT_EQ(callbacks_.messageBegin, oversized ? 0 : 1);
628   EXPECT_EQ(callbacks_.headersComplete, oversized ? 0 : 1);
629   EXPECT_EQ(callbacks_.messageComplete, oversized ? 0 : 1);
630   EXPECT_EQ(callbacks_.streamErrors, 0);
631   EXPECT_EQ(callbacks_.sessionErrors, oversized ? 1 : 0);
632 }
633 
testFrameSizeLimit(bool oversized)634 void HTTP2CodecTest::testFrameSizeLimit(bool oversized) {
635   HTTPMessage req = getBigGetRequest("/guacamole");
636   auto settings = downstreamCodec_.getEgressSettings();
637 
638   parse(); // consume preface
639   if (oversized) {
640     // trick upstream for sending a 2x bigger HEADERS frame
641     settings->setSetting(SettingsId::MAX_FRAME_SIZE,
642                          http2::kMaxFramePayloadLengthMin * 2);
643     downstreamCodec_.generateSettings(output_);
644     parseUpstream();
645   }
646 
647   settings->setSetting(SettingsId::MAX_FRAME_SIZE,
648                        http2::kMaxFramePayloadLengthMin);
649   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
650 
651   parse();
652   // session error
653   EXPECT_EQ(callbacks_.messageBegin, oversized ? 0 : 1);
654   EXPECT_EQ(callbacks_.headersComplete, oversized ? 0 : 1);
655   EXPECT_EQ(callbacks_.messageComplete, oversized ? 0 : 1);
656   EXPECT_EQ(callbacks_.streamErrors, 0);
657   EXPECT_EQ(callbacks_.sessionErrors, oversized ? 1 : 0);
658 }
659 
TEST_F(HTTP2CodecTest,NormalSizeHeader)660 TEST_F(HTTP2CodecTest, NormalSizeHeader) {
661   testHeaderListSize(false);
662 }
663 
TEST_F(HTTP2CodecTest,OversizedHeader)664 TEST_F(HTTP2CodecTest, OversizedHeader) {
665   testHeaderListSize(true);
666 }
667 
TEST_F(HTTP2CodecTest,NormalSizeFrame)668 TEST_F(HTTP2CodecTest, NormalSizeFrame) {
669   testFrameSizeLimit(false);
670 }
671 
TEST_F(HTTP2CodecTest,OversizedFrame)672 TEST_F(HTTP2CodecTest, OversizedFrame) {
673   testFrameSizeLimit(true);
674 }
675 
TEST_F(HTTP2CodecTest,BigHeaderCompressed)676 TEST_F(HTTP2CodecTest, BigHeaderCompressed) {
677   SetUpUpstreamTest();
678   auto settings = downstreamCodec_.getEgressSettings();
679   settings->setSetting(SettingsId::MAX_HEADER_LIST_SIZE, 37);
680   downstreamCodec_.generateSettings(output_);
681   parseUpstream();
682 
683   SetUp();
684   HTTPMessage req = getGetRequest("/guacamole");
685   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
686   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
687 
688   parse();
689   // session error
690   EXPECT_EQ(callbacks_.messageBegin, 0);
691   EXPECT_EQ(callbacks_.headersComplete, 0);
692   EXPECT_EQ(callbacks_.messageComplete, 0);
693   EXPECT_EQ(callbacks_.streamErrors, 0);
694   EXPECT_EQ(callbacks_.sessionErrors, 1);
695 }
696 
TEST_F(HTTP2CodecTest,BasicHeaderReply)697 TEST_F(HTTP2CodecTest, BasicHeaderReply) {
698   SetUpUpstreamTest();
699   HTTPMessage resp;
700   resp.setStatusCode(200);
701   resp.setStatusMessage("nifty-nice");
702   resp.getHeaders().add(HTTP_HEADER_CONTENT_TYPE, "x-coolio");
703   downstreamCodec_.generateHeader(output_, 1, resp);
704   downstreamCodec_.generateEOM(output_, 1);
705 
706   parseUpstream();
707   callbacks_.expectMessage(true, 2, 200);
708   const auto& headers = callbacks_.msg->getHeaders();
709   // HTTP/2 doesnt support serialization - instead you get the default
710   EXPECT_EQ("OK", callbacks_.msg->getStatusMessage());
711   EXPECT_TRUE(callbacks_.msg->getHeaders().exists(HTTP_HEADER_DATE));
712   EXPECT_EQ("x-coolio", headers.getSingleOrEmpty(HTTP_HEADER_CONTENT_TYPE));
713 }
714 
TEST_F(HTTP2CodecTest,DontDoubleDate)715 TEST_F(HTTP2CodecTest, DontDoubleDate) {
716   SetUpUpstreamTest();
717   HTTPMessage resp;
718   resp.setStatusCode(200);
719   resp.setStatusMessage("nifty-nice");
720   resp.getHeaders().add(HTTP_HEADER_DATE, "Today!");
721   resp.getHeaders().add(HTTP_HEADER_CONTENT_TYPE, "x-coolio");
722   downstreamCodec_.generateHeader(output_, 1, resp);
723   downstreamCodec_.generateEOM(output_, 1);
724 
725   parseUpstream();
726   callbacks_.expectMessage(true, 2, 200);
727   const auto& headers = callbacks_.msg->getHeaders();
728   // HTTP/2 doesnt support serialization - instead you get the default
729   EXPECT_EQ("OK", callbacks_.msg->getStatusMessage());
730   EXPECT_EQ(1,
731             callbacks_.msg->getHeaders().getNumberOfValues(HTTP_HEADER_DATE));
732   EXPECT_EQ("x-coolio", headers.getSingleOrEmpty(HTTP_HEADER_CONTENT_TYPE));
733 }
734 
TEST_F(HTTP2CodecTest,BadHeadersReply)735 TEST_F(HTTP2CodecTest, BadHeadersReply) {
736   static const std::string v1("200");
737   static const vector<proxygen::compress::Header> respHeaders = {
738       Header::makeHeaderForTest(headers::kStatus, v1),
739   };
740 
741   HPACKCodec headerCodec(TransportDirection::DOWNSTREAM);
742   HTTPCodec::StreamID stream = 1;
743   // missing fields (missing authority is OK)
744   for (size_t i = 0; i < respHeaders.size(); i++, stream += 2) {
745     std::vector<proxygen::compress::Header> allHeaders = respHeaders;
746     allHeaders.erase(allHeaders.begin() + i);
747     auto encodedHeaders = headerCodec.encode(allHeaders);
748     writeHeaders(output_,
749                  std::move(encodedHeaders),
750                  stream,
751                  folly::none,
752                  http2::kNoPadding,
753                  true,
754                  true);
755   }
756   // dup fields
757   std::string v("foomonkey");
758   for (size_t i = 0; i < respHeaders.size(); i++, stream += 2) {
759     std::vector<proxygen::compress::Header> allHeaders = respHeaders;
760     auto h = allHeaders[i];
761     h.value = &v;
762     allHeaders.push_back(h);
763     auto encodedHeaders = headerCodec.encode(allHeaders);
764     writeHeaders(output_,
765                  std::move(encodedHeaders),
766                  stream,
767                  folly::none,
768                  http2::kNoPadding,
769                  true,
770                  true);
771   }
772 
773   parse();
774   EXPECT_EQ(callbacks_.messageBegin, 2);
775   EXPECT_EQ(callbacks_.headersComplete, 0);
776   EXPECT_EQ(callbacks_.messageComplete, 0);
777   EXPECT_EQ(callbacks_.streamErrors, 2);
778   EXPECT_EQ(callbacks_.sessionErrors, 0);
779 }
780 
TEST_F(HTTP2CodecTest,Cookies)781 TEST_F(HTTP2CodecTest, Cookies) {
782   HTTPMessage req = getGetRequest("/guacamole");
783   req.getHeaders().add("Cookie", "chocolate-chip=1");
784   req.getHeaders().add("Cookie", "rainbow-chip=2");
785   req.getHeaders().add("Cookie", "butterscotch=3");
786   req.getHeaders().add("Cookie", "oatmeal-raisin=4");
787   req.setSecure(true);
788   upstreamCodec_.generateHeader(output_, 1, req);
789 
790   parse();
791   callbacks_.expectMessage(false, 2, "/guacamole");
792   EXPECT_EQ(callbacks_.msg->getCookie("chocolate-chip"), "1");
793   EXPECT_EQ(callbacks_.msg->getCookie("rainbow-chip"), "2");
794   EXPECT_EQ(callbacks_.msg->getCookie("butterscotch"), "3");
795   EXPECT_EQ(callbacks_.msg->getCookie("oatmeal-raisin"), "4");
796 }
797 
TEST_F(HTTP2CodecTest,BasicContinuation)798 TEST_F(HTTP2CodecTest, BasicContinuation) {
799   HTTPMessage req = getBigGetRequest();
800   upstreamCodec_.generateHeader(output_, 1, req);
801 
802   parse();
803   callbacks_.expectMessage(false, -1, "/");
804 #ifndef NDEBUG
805   EXPECT_GT(downstreamCodec_.getReceivedFrameCount(), 1);
806 #endif
807   const auto& headers = callbacks_.msg->getHeaders();
808   EXPECT_EQ("coolio", headers.getSingleOrEmpty(HTTP_HEADER_USER_AGENT));
809   EXPECT_EQ(callbacks_.messageBegin, 1);
810   EXPECT_EQ(callbacks_.headersComplete, 1);
811   EXPECT_EQ(callbacks_.messageComplete, 0);
812   EXPECT_EQ(callbacks_.streamErrors, 0);
813   EXPECT_EQ(callbacks_.sessionErrors, 0);
814 }
815 
TEST_F(HTTP2CodecTest,BasicContinuationEndStream)816 TEST_F(HTTP2CodecTest, BasicContinuationEndStream) {
817   // CONTINUATION with END_STREAM flag set on the preceding HEADERS frame
818   HTTPMessage req = getBigGetRequest();
819   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
820 
821   parse();
822   callbacks_.expectMessage(true, -1, "/");
823 #ifndef NDEBUG
824   EXPECT_GT(downstreamCodec_.getReceivedFrameCount(), 1);
825 #endif
826   const auto& headers = callbacks_.msg->getHeaders();
827   EXPECT_EQ("coolio", headers.getSingleOrEmpty(HTTP_HEADER_USER_AGENT));
828   EXPECT_EQ(callbacks_.messageBegin, 1);
829   EXPECT_EQ(callbacks_.headersComplete, 1);
830   EXPECT_EQ(callbacks_.messageComplete, 1);
831   EXPECT_EQ(callbacks_.streamErrors, 0);
832   EXPECT_EQ(callbacks_.sessionErrors, 0);
833 }
834 
TEST_F(HTTP2CodecTest,BadContinuation)835 TEST_F(HTTP2CodecTest, BadContinuation) {
836   // CONTINUATION with no preceding HEADERS
837   auto fakeHeaders = makeBuf(5);
838   http2::writeContinuation(output_, 3, true, std::move(fakeHeaders));
839 
840   parse();
841   EXPECT_EQ(callbacks_.messageBegin, 0);
842   EXPECT_EQ(callbacks_.headersComplete, 0);
843   EXPECT_EQ(callbacks_.messageComplete, 0);
844   EXPECT_EQ(callbacks_.streamErrors, 0);
845   EXPECT_EQ(callbacks_.sessionErrors, 1);
846 }
847 
TEST_F(HTTP2CodecTest,MissingContinuation)848 TEST_F(HTTP2CodecTest, MissingContinuation) {
849   IOBufQueue output(IOBufQueue::cacheChainLength());
850   HTTPMessage req = getBigGetRequest();
851 
852   upstreamCodec_.generateHeader(output_, 1, req);
853   // empirically determined the size of continuation frame, and strip it
854   output_.trimEnd(http2::kFrameHeaderSize + 4134);
855 
856   // insert a non-continuation (but otherwise valid) frame
857   http2::writeGoaway(output_, 17, ErrorCode::ENHANCE_YOUR_CALM);
858 
859   parse();
860   EXPECT_EQ(callbacks_.messageBegin, 0);
861   EXPECT_EQ(callbacks_.headersComplete, 0);
862   EXPECT_EQ(callbacks_.messageComplete, 0);
863   EXPECT_EQ(callbacks_.streamErrors, 0);
864   EXPECT_EQ(callbacks_.sessionErrors, 1);
865 #ifndef NDEBUG
866   EXPECT_EQ(downstreamCodec_.getReceivedFrameCount(), 2);
867 #endif
868 }
869 
TEST_F(HTTP2CodecTest,MissingContinuationBadFrame)870 TEST_F(HTTP2CodecTest, MissingContinuationBadFrame) {
871   IOBufQueue output(IOBufQueue::cacheChainLength());
872   HTTPMessage req = getBigGetRequest();
873   upstreamCodec_.generateHeader(output_, 1, req);
874 
875   // empirically determined the size of continuation frame, and fake it
876   output_.trimEnd(http2::kFrameHeaderSize + 4134);
877 
878   // insert an invalid frame
879   auto frame = makeBuf(http2::kFrameHeaderSize + 4134);
880   *((uint32_t*)frame->writableData()) = 0xfa000000;
881   output_.append(std::move(frame));
882 
883   parse();
884   EXPECT_EQ(callbacks_.messageBegin, 0);
885   EXPECT_EQ(callbacks_.headersComplete, 0);
886   EXPECT_EQ(callbacks_.messageComplete, 0);
887   EXPECT_EQ(callbacks_.streamErrors, 0);
888   EXPECT_EQ(callbacks_.sessionErrors, 1);
889 #ifndef NDEBUG
890   EXPECT_EQ(downstreamCodec_.getReceivedFrameCount(), 2);
891 #endif
892 }
893 
TEST_F(HTTP2CodecTest,BadContinuationStream)894 TEST_F(HTTP2CodecTest, BadContinuationStream) {
895   HTTPMessage req = getBigGetRequest();
896   upstreamCodec_.generateHeader(output_, 1, req);
897 
898   // empirically determined the size of continuation frame, and fake it
899   output_.trimEnd(http2::kFrameHeaderSize + 4134);
900   auto fakeHeaders = makeBuf(4134);
901   http2::writeContinuation(output_, 3, true, std::move(fakeHeaders));
902 
903   parse();
904   EXPECT_EQ(callbacks_.messageBegin, 0);
905   EXPECT_EQ(callbacks_.headersComplete, 0);
906   EXPECT_EQ(callbacks_.messageComplete, 0);
907   EXPECT_EQ(callbacks_.streamErrors, 0);
908   EXPECT_EQ(callbacks_.sessionErrors, 1);
909 #ifndef NDEBUG
910   EXPECT_EQ(downstreamCodec_.getReceivedFrameCount(), 2);
911 #endif
912 }
913 
TEST_F(HTTP2CodecTest,FrameTooLarge)914 TEST_F(HTTP2CodecTest, FrameTooLarge) {
915   writeFrameHeaderManual(output_, 1 << 15, 0, 0, 1);
916 
917   parse();
918   EXPECT_EQ(callbacks_.messageBegin, 0);
919   EXPECT_EQ(callbacks_.headersComplete, 0);
920   EXPECT_EQ(callbacks_.messageComplete, 0);
921   EXPECT_EQ(callbacks_.streamErrors, 0);
922   EXPECT_EQ(callbacks_.sessionErrors, 1);
923   EXPECT_TRUE(callbacks_.lastParseError->hasCodecStatusCode());
924   EXPECT_EQ(callbacks_.lastParseError->getCodecStatusCode(),
925             ErrorCode::FRAME_SIZE_ERROR);
926 }
927 
TEST_F(HTTP2CodecTest,UnknownFrameType)928 TEST_F(HTTP2CodecTest, UnknownFrameType) {
929   HTTPMessage req = getGetRequest();
930   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
931 
932   // unknown frame type 17
933   writeFrameHeaderManual(output_, 17, 37, 0, 1);
934   output_.append("wicked awesome!!!");
935   upstreamCodec_.generateHeader(output_, 1, req);
936 
937   parse();
938   callbacks_.expectMessage(false, 2, ""); // + host
939 }
940 
TEST_F(HTTP2CodecTest,JunkAfterConnError)941 TEST_F(HTTP2CodecTest, JunkAfterConnError) {
942   HTTPMessage req = getGetRequest();
943   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
944 
945   // write headers frame for stream 0
946   writeFrameHeaderManual(output_, 0, (uint8_t)http2::FrameType::HEADERS, 0, 0);
947   // now write a valid headers frame, should never be parsed
948   upstreamCodec_.generateHeader(output_, 1, req);
949 
950   parse();
951   EXPECT_EQ(callbacks_.messageBegin, 0);
952   EXPECT_EQ(callbacks_.headersComplete, 0);
953   EXPECT_EQ(callbacks_.messageComplete, 0);
954   EXPECT_EQ(callbacks_.streamErrors, 0);
955   EXPECT_EQ(callbacks_.sessionErrors, 1);
956 }
957 
TEST_F(HTTP2CodecTest,BasicData)958 TEST_F(HTTP2CodecTest, BasicData) {
959   string data("abcde");
960   auto buf = folly::IOBuf::copyBuffer(data.data(), data.length());
961   upstreamCodec_.generateBody(
962       output_, 2, std::move(buf), HTTPCodec::NoPadding, true);
963 
964   parse();
965   EXPECT_EQ(callbacks_.messageBegin, 0);
966   EXPECT_EQ(callbacks_.headersComplete, 0);
967   EXPECT_EQ(callbacks_.messageComplete, 1);
968   EXPECT_EQ(callbacks_.bodyCalls, 1);
969   EXPECT_EQ(callbacks_.bodyLength, 5);
970   EXPECT_EQ(callbacks_.streamErrors, 0);
971   EXPECT_EQ(callbacks_.sessionErrors, 0);
972   EXPECT_EQ(callbacks_.data_.move()->moveToFbString(), data);
973 }
974 
TEST_F(HTTP2CodecTest,LongData)975 TEST_F(HTTP2CodecTest, LongData) {
976   // Hack the max frame size artificially low
977   HTTPSettings* settings = (HTTPSettings*)upstreamCodec_.getIngressSettings();
978   settings->setSetting(SettingsId::MAX_FRAME_SIZE, 16);
979   auto buf = makeBuf(100);
980   upstreamCodec_.generateBody(
981       output_, 1, buf->clone(), HTTPCodec::NoPadding, true);
982 
983   parse();
984   EXPECT_EQ(callbacks_.messageBegin, 0);
985   EXPECT_EQ(callbacks_.headersComplete, 0);
986   EXPECT_EQ(callbacks_.messageComplete, 1);
987   EXPECT_EQ(callbacks_.bodyCalls, 7);
988   EXPECT_EQ(callbacks_.bodyLength, 100);
989   EXPECT_EQ(callbacks_.streamErrors, 0);
990   EXPECT_EQ(callbacks_.sessionErrors, 0);
991   EXPECT_EQ(callbacks_.data_.move()->moveToFbString(), buf->moveToFbString());
992 }
993 
TEST_F(HTTP2CodecTest,PushPromiseContinuation)994 TEST_F(HTTP2CodecTest, PushPromiseContinuation) {
995   auto settings = upstreamCodec_.getEgressSettings();
996   settings->setSetting(SettingsId::ENABLE_PUSH, 1);
997   upstreamCodec_.generateSettings(output_);
998 
999   SetUpUpstreamTest();
1000   // Hack the max frame size artificially low
1001   settings = (HTTPSettings*)downstreamCodec_.getIngressSettings();
1002   settings->setSetting(SettingsId::MAX_FRAME_SIZE, 5);
1003   HTTPHeaderSize size;
1004   HTTPMessage req = getGetRequest();
1005   req.getHeaders().add("foomonkey", "george");
1006   downstreamCodec_.generatePushPromise(output_, 2, req, 1, false, &size);
1007 
1008   parseUpstream();
1009   EXPECT_EQ(callbacks_.messageBegin, 1);
1010   EXPECT_EQ(callbacks_.headersComplete, 1);
1011   EXPECT_EQ(callbacks_.pushId, 2);
1012   EXPECT_EQ(callbacks_.assocStreamId, 1);
1013   EXPECT_EQ(callbacks_.headersCompleteId, 2);
1014   EXPECT_EQ(callbacks_.streamErrors, 0);
1015   EXPECT_EQ(callbacks_.sessionErrors, 0);
1016 }
1017 
TEST_F(HTTP2CodecTest,MalformedPaddingLength)1018 TEST_F(HTTP2CodecTest, MalformedPaddingLength) {
1019   const uint8_t badInput[] = {0x50,
1020                               0x52,
1021                               0x49,
1022                               0x20,
1023                               0x2a,
1024                               0x20,
1025                               0x48,
1026                               0x54,
1027                               0x54,
1028                               0x50,
1029                               0x2f,
1030                               0x32,
1031                               0x2e,
1032                               0x30,
1033                               0x0d,
1034                               0x0a,
1035                               0x0d,
1036                               0x0a,
1037                               0x53,
1038                               0x4d,
1039                               0x0d,
1040                               0x0a,
1041                               0x0d,
1042                               0x0a,
1043                               0x00,
1044                               0x00,
1045                               0x7e,
1046                               0x00,
1047                               0x6f,
1048                               0x6f,
1049                               0x6f,
1050                               0x6f,
1051                               // The padding length byte below is 0x82 (130
1052                               // in decimal) which is greater than the length
1053                               // specified by the header's length field, 126
1054                               0x01,
1055                               0x82,
1056                               0x87,
1057                               0x44,
1058                               0x87,
1059                               0x92,
1060                               0x97,
1061                               0x92,
1062                               0x92,
1063                               0x92,
1064                               0x7a,
1065                               0x0b,
1066                               0x41,
1067                               0x89,
1068                               0xf1,
1069                               0xe3,
1070                               0xc0,
1071                               0xf2,
1072                               0x9c,
1073                               0xdd,
1074                               0x90,
1075                               0xf4,
1076                               0xff,
1077                               0x40,
1078                               0x80,
1079                               0x84,
1080                               0x2d,
1081                               0x35,
1082                               0xa7,
1083                               0xd7};
1084   output_.reset();
1085   output_.append(badInput, sizeof(badInput));
1086   EXPECT_EQ(output_.chainLength(), sizeof(badInput));
1087 
1088   EXPECT_FALSE(parse());
1089 }
1090 
TEST_F(HTTP2CodecTest,MalformedPadding)1091 TEST_F(HTTP2CodecTest, MalformedPadding) {
1092   const uint8_t badInput[] = {0x00, 0x00, 0x0d, 0x01, 0xbe, 0x63, 0x0d, 0x0a,
1093                               0x0d, 0x0a, 0x00, 0x73, 0x00, 0x00, 0x06, 0x08,
1094                               0x72, 0x00, 0x24, 0x00, 0xfa, 0x4d, 0x0d};
1095   output_.append(badInput, sizeof(badInput));
1096 
1097   EXPECT_FALSE(parse());
1098 }
1099 
TEST_F(HTTP2CodecTest,NoAppByte)1100 TEST_F(HTTP2CodecTest, NoAppByte) {
1101   const uint8_t noAppByte[] = {
1102       0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32,
1103       0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a,
1104       0x00, 0x00, 0x56, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x01, 0x55, 0x00};
1105   output_.reset();
1106   output_.append(noAppByte, sizeof(noAppByte));
1107   EXPECT_EQ(output_.chainLength(), sizeof(noAppByte));
1108 
1109   EXPECT_TRUE(parse());
1110   EXPECT_EQ(callbacks_.messageBegin, 0);
1111   EXPECT_EQ(callbacks_.headersComplete, 0);
1112   EXPECT_EQ(callbacks_.messageComplete, 0);
1113   EXPECT_EQ(callbacks_.bodyCalls, 1);
1114   EXPECT_EQ(callbacks_.bodyLength, 0);
1115   EXPECT_EQ(callbacks_.streamErrors, 0);
1116   EXPECT_EQ(callbacks_.sessionErrors, 0);
1117 }
1118 
TEST_F(HTTP2CodecTest,DataFramePartialDataOnFrameHeaderCall)1119 TEST_F(HTTP2CodecTest, DataFramePartialDataOnFrameHeaderCall) {
1120   using namespace testing;
1121   NiceMock<MockHTTPCodecCallback> mockCallback;
1122   EXPECT_CALL(mockCallback, onFrameHeader(_, _, _, _, _));
1123 
1124   const size_t bufSize = 10;
1125   auto buf = makeBuf(bufSize);
1126   const size_t padding = 10;
1127   upstreamCodec_.generateBody(output_, 1, buf->clone(), padding, true);
1128   EXPECT_EQ(output_.chainLength(), 54);
1129 
1130   downstreamCodec_.setCallback(&mockCallback);
1131 
1132   auto ingress = output_.move();
1133   ingress->coalesce();
1134   // Copy partial byte to a new buffer
1135   auto ingress1 = IOBuf::copyBuffer(ingress->data(), 34);
1136   downstreamCodec_.onIngress(*ingress1);
1137 }
1138 
TEST_F(HTTP2CodecTest,DataFramePartialDataWithNoAppByte)1139 TEST_F(HTTP2CodecTest, DataFramePartialDataWithNoAppByte) {
1140   const size_t bufSize = 10;
1141   auto buf = makeBuf(bufSize);
1142   const size_t padding = 10;
1143   upstreamCodec_.generateBody(output_, 1, buf->clone(), padding, true);
1144   EXPECT_EQ(output_.chainLength(), 54);
1145 
1146   auto ingress = output_.move();
1147   ingress->coalesce();
1148   // Copy up to the padding length byte to a new buffer
1149   auto ingress1 = IOBuf::copyBuffer(ingress->data(), 34);
1150   size_t parsed = downstreamCodec_.onIngress(*ingress1);
1151   // The 34th byte is the padding length byte which should not be parsed
1152   EXPECT_EQ(parsed, 33);
1153   // Copy from the padding length byte to the end
1154   auto ingress2 = IOBuf::copyBuffer(ingress->data() + 33, 21);
1155   parsed = downstreamCodec_.onIngress(*ingress2);
1156   // The padding length byte should be parsed this time along with 10 bytes of
1157   // application data and 10 bytes of padding
1158   EXPECT_EQ(parsed, 21);
1159 
1160   EXPECT_EQ(callbacks_.messageBegin, 0);
1161   EXPECT_EQ(callbacks_.headersComplete, 0);
1162   EXPECT_EQ(callbacks_.messageComplete, 1);
1163   EXPECT_EQ(callbacks_.bodyCalls, 1);
1164   EXPECT_EQ(callbacks_.bodyLength, bufSize);
1165   // Total padding is the padding length byte and the padding bytes
1166   EXPECT_EQ(callbacks_.paddingBytes, padding + 1);
1167   EXPECT_EQ(callbacks_.streamErrors, 0);
1168   EXPECT_EQ(callbacks_.sessionErrors, 0);
1169   EXPECT_EQ(callbacks_.data_.move()->moveToFbString(), buf->moveToFbString());
1170 }
1171 
TEST_F(HTTP2CodecTest,BasicRst)1172 TEST_F(HTTP2CodecTest, BasicRst) {
1173   upstreamCodec_.generateRstStream(output_, 2, ErrorCode::ENHANCE_YOUR_CALM);
1174   parse();
1175   EXPECT_EQ(callbacks_.messageBegin, 0);
1176   EXPECT_EQ(callbacks_.headersComplete, 0);
1177   EXPECT_EQ(callbacks_.messageComplete, 0);
1178   EXPECT_EQ(callbacks_.bodyCalls, 0);
1179   EXPECT_EQ(callbacks_.aborts, 1);
1180   EXPECT_EQ(callbacks_.streamErrors, 0);
1181   EXPECT_EQ(callbacks_.sessionErrors, 0);
1182 }
1183 
TEST_F(HTTP2CodecTest,BasicRstInvalidCode)1184 TEST_F(HTTP2CodecTest, BasicRstInvalidCode) {
1185   upstreamCodec_.generateRstStream(output_, 2, ErrorCode::STREAM_CLOSED);
1186   parse();
1187   EXPECT_EQ(callbacks_.messageBegin, 0);
1188   EXPECT_EQ(callbacks_.headersComplete, 0);
1189   EXPECT_EQ(callbacks_.messageComplete, 0);
1190   EXPECT_EQ(callbacks_.bodyCalls, 0);
1191   EXPECT_EQ(callbacks_.aborts, 1);
1192   EXPECT_EQ(callbacks_.streamErrors, 0);
1193   EXPECT_EQ(callbacks_.sessionErrors, 0);
1194 }
1195 
TEST_F(HTTP2CodecTest,BasicPing)1196 TEST_F(HTTP2CodecTest, BasicPing) {
1197   upstreamCodec_.generatePingRequest(output_);
1198   upstreamCodec_.generatePingReply(output_, 17);
1199 
1200   uint64_t pingReq;
1201   parse([&](IOBuf* ingress) {
1202     folly::io::Cursor c(ingress);
1203     c.skip(http2::kFrameHeaderSize + http2::kConnectionPreface.length());
1204     pingReq = c.read<uint64_t>();
1205   });
1206 
1207   EXPECT_EQ(callbacks_.messageBegin, 0);
1208   EXPECT_EQ(callbacks_.headersComplete, 0);
1209   EXPECT_EQ(callbacks_.messageComplete, 0);
1210   EXPECT_EQ(callbacks_.bodyCalls, 0);
1211   EXPECT_EQ(callbacks_.recvPingRequest, pingReq);
1212   EXPECT_EQ(callbacks_.recvPingReply, 17);
1213   EXPECT_EQ(callbacks_.streamErrors, 0);
1214   EXPECT_EQ(callbacks_.sessionErrors, 0);
1215 }
1216 
TEST_F(HTTP2CodecTest,BasicWindow)1217 TEST_F(HTTP2CodecTest, BasicWindow) {
1218   // This test would fail if the codec had window state
1219   upstreamCodec_.generateWindowUpdate(output_, 0, 10);
1220   upstreamCodec_.generateWindowUpdate(output_, 0, http2::kMaxWindowUpdateSize);
1221   upstreamCodec_.generateWindowUpdate(output_, 1, 12);
1222   upstreamCodec_.generateWindowUpdate(output_, 1, http2::kMaxWindowUpdateSize);
1223 
1224   parse();
1225   EXPECT_EQ(callbacks_.windowUpdateCalls, 4);
1226   EXPECT_EQ(callbacks_.windowUpdates[0],
1227             std::vector<uint32_t>({10, http2::kMaxWindowUpdateSize}));
1228   EXPECT_EQ(callbacks_.windowUpdates[1],
1229             std::vector<uint32_t>({12, http2::kMaxWindowUpdateSize}));
1230   EXPECT_EQ(callbacks_.streamErrors, 0);
1231   EXPECT_EQ(callbacks_.sessionErrors, 0);
1232 }
1233 
TEST_F(HTTP2CodecTest,ZeroWindow)1234 TEST_F(HTTP2CodecTest, ZeroWindow) {
1235   auto streamID = HTTPCodec::StreamID(1);
1236   // First generate a frame with delta=1 so as to pass the checks, and then
1237   // hack the frame so that delta=0 without modifying other checks
1238   upstreamCodec_.generateWindowUpdate(output_, streamID, 1);
1239   output_.trimEnd(http2::kFrameWindowUpdateSize);
1240   QueueAppender appender(&output_, http2::kFrameWindowUpdateSize);
1241   appender.writeBE<uint32_t>(0);
1242 
1243   parse();
1244   // This test doesn't ensure that RST_STREAM is generated
1245   EXPECT_EQ(callbacks_.windowUpdateCalls, 0);
1246   EXPECT_EQ(callbacks_.streamErrors, 1);
1247   EXPECT_EQ(callbacks_.lastParseError->getCodecStatusCode(),
1248             ErrorCode::PROTOCOL_ERROR);
1249 }
1250 
TEST_F(HTTP2CodecTest,BasicGoaway)1251 TEST_F(HTTP2CodecTest, BasicGoaway) {
1252   std::unique_ptr<folly::IOBuf> debugData =
1253       folly::IOBuf::copyBuffer("debugData");
1254   upstreamCodec_.generateGoaway(
1255       output_, 17, ErrorCode::ENHANCE_YOUR_CALM, std::move(debugData));
1256 
1257   parse();
1258   EXPECT_EQ(callbacks_.goaways, 1);
1259   EXPECT_EQ(callbacks_.data_.move()->moveToFbString(), "debugData");
1260   EXPECT_EQ(callbacks_.streamErrors, 0);
1261   EXPECT_EQ(callbacks_.sessionErrors, 0);
1262 }
1263 
TEST_F(HTTP2CodecTest,BadGoaway)1264 TEST_F(HTTP2CodecTest, BadGoaway) {
1265   std::unique_ptr<folly::IOBuf> debugData =
1266       folly::IOBuf::copyBuffer("debugData");
1267   upstreamCodec_.generateGoaway(
1268       output_, 17, ErrorCode::ENHANCE_YOUR_CALM, std::move(debugData));
1269   auto bytes =
1270       upstreamCodec_.generateGoaway(output_, 27, ErrorCode::ENHANCE_YOUR_CALM);
1271   ;
1272   EXPECT_EQ(bytes, 0);
1273 }
1274 
TEST_F(HTTP2CodecTest,DoubleGoaway)1275 TEST_F(HTTP2CodecTest, DoubleGoaway) {
1276   parse();
1277   SetUpUpstreamTest();
1278   downstreamCodec_.generateGoaway(output_);
1279   EXPECT_TRUE(downstreamCodec_.isWaitingToDrain());
1280   EXPECT_TRUE(downstreamCodec_.isReusable());
1281   EXPECT_TRUE(downstreamCodec_.isStreamIngressEgressAllowed(0));
1282   EXPECT_TRUE(downstreamCodec_.isStreamIngressEgressAllowed(1));
1283   EXPECT_TRUE(downstreamCodec_.isStreamIngressEgressAllowed(2));
1284 
1285   EXPECT_TRUE(upstreamCodec_.isStreamIngressEgressAllowed(0));
1286   EXPECT_TRUE(upstreamCodec_.isStreamIngressEgressAllowed(1));
1287   EXPECT_TRUE(upstreamCodec_.isStreamIngressEgressAllowed(2));
1288   EXPECT_TRUE(upstreamCodec_.isReusable());
1289 
1290   parseUpstream();
1291   EXPECT_TRUE(upstreamCodec_.isStreamIngressEgressAllowed(0));
1292   EXPECT_TRUE(upstreamCodec_.isStreamIngressEgressAllowed(1));
1293   EXPECT_TRUE(upstreamCodec_.isStreamIngressEgressAllowed(2));
1294   EXPECT_FALSE(upstreamCodec_.isReusable());
1295   EXPECT_EQ(callbacks_.goaways, 1);
1296   EXPECT_EQ(callbacks_.streamErrors, 0);
1297   EXPECT_EQ(callbacks_.sessionErrors, 0);
1298 
1299   downstreamCodec_.generateGoaway(output_, 0, ErrorCode::NO_ERROR);
1300   EXPECT_FALSE(downstreamCodec_.isWaitingToDrain());
1301   EXPECT_FALSE(downstreamCodec_.isReusable());
1302   EXPECT_TRUE(downstreamCodec_.isStreamIngressEgressAllowed(0));
1303   EXPECT_FALSE(downstreamCodec_.isStreamIngressEgressAllowed(1));
1304   EXPECT_TRUE(downstreamCodec_.isStreamIngressEgressAllowed(2));
1305 
1306   parseUpstream();
1307   EXPECT_TRUE(upstreamCodec_.isStreamIngressEgressAllowed(0));
1308   EXPECT_FALSE(upstreamCodec_.isStreamIngressEgressAllowed(1));
1309   EXPECT_TRUE(upstreamCodec_.isStreamIngressEgressAllowed(2));
1310   EXPECT_EQ(callbacks_.goaways, 2);
1311   EXPECT_EQ(callbacks_.streamErrors, 0);
1312   EXPECT_EQ(callbacks_.sessionErrors, 0);
1313 
1314   upstreamCodec_.generateGoaway(output_, 0, ErrorCode::NO_ERROR);
1315   EXPECT_TRUE(upstreamCodec_.isStreamIngressEgressAllowed(0));
1316   EXPECT_FALSE(upstreamCodec_.isStreamIngressEgressAllowed(1));
1317   EXPECT_FALSE(upstreamCodec_.isStreamIngressEgressAllowed(2));
1318   parse();
1319   EXPECT_TRUE(downstreamCodec_.isStreamIngressEgressAllowed(0));
1320   EXPECT_FALSE(downstreamCodec_.isStreamIngressEgressAllowed(1));
1321   EXPECT_FALSE(downstreamCodec_.isStreamIngressEgressAllowed(2));
1322 }
1323 
TEST_F(HTTP2CodecTest,DoubleGoawayWithError)1324 TEST_F(HTTP2CodecTest, DoubleGoawayWithError) {
1325   SetUpUpstreamTest();
1326   std::unique_ptr<folly::IOBuf> debugData =
1327       folly::IOBuf::copyBuffer("debugData");
1328   downstreamCodec_.generateGoaway(output_,
1329                                   std::numeric_limits<int32_t>::max(),
1330                                   ErrorCode::ENHANCE_YOUR_CALM,
1331                                   std::move(debugData));
1332   EXPECT_FALSE(downstreamCodec_.isWaitingToDrain());
1333   EXPECT_FALSE(downstreamCodec_.isReusable());
1334   auto ret = downstreamCodec_.generateGoaway(output_, 0, ErrorCode::NO_ERROR);
1335   EXPECT_EQ(ret, 0);
1336 
1337   parseUpstream();
1338   EXPECT_EQ(callbacks_.goaways, 1);
1339   EXPECT_EQ(callbacks_.data_.move()->moveToFbString(), "debugData");
1340   EXPECT_EQ(callbacks_.streamErrors, 0);
1341   EXPECT_EQ(callbacks_.sessionErrors, 0);
1342 }
1343 
TEST_F(HTTP2CodecTest,GoawayHandling)1344 TEST_F(HTTP2CodecTest, GoawayHandling) {
1345   auto settings = upstreamCodec_.getEgressSettings();
1346   settings->setSetting(SettingsId::ENABLE_PUSH, 1);
1347   upstreamCodec_.generateSettings(output_);
1348 
1349   // send request
1350   HTTPMessage req = getGetRequest();
1351   HTTPHeaderSize size;
1352   size.uncompressed = size.compressed = 0;
1353   upstreamCodec_.generateHeader(output_, 1, req, true, &size);
1354   EXPECT_GT(size.uncompressed, 0);
1355   parse();
1356   callbacks_.expectMessage(true, 1, "/");
1357   callbacks_.reset();
1358 
1359   SetUpUpstreamTest();
1360   // drain after this message
1361   downstreamCodec_.generateGoaway(output_, 1, ErrorCode::NO_ERROR);
1362   parseUpstream();
1363   // upstream cannot generate id > 1
1364   upstreamCodec_.generateHeader(output_, 3, req, false, &size);
1365   EXPECT_EQ(size.uncompressed, 0);
1366   upstreamCodec_.generateWindowUpdate(output_, 3, 100);
1367   upstreamCodec_.generateBody(
1368       output_, 3, makeBuf(10), HTTPCodec::NoPadding, false);
1369   upstreamCodec_.generatePriority(
1370       output_, 3, HTTPMessage::HTTP2Priority(0, true, 1));
1371   upstreamCodec_.generateEOM(output_, 3);
1372   upstreamCodec_.generateRstStream(output_, 3, ErrorCode::CANCEL);
1373   EXPECT_EQ(output_.chainLength(), 0);
1374 
1375   // send a push promise that will be rejected by downstream
1376   req.getHeaders().add("foomonkey", "george");
1377   downstreamCodec_.generatePushPromise(output_, 2, req, 1, false, &size);
1378   EXPECT_GT(size.uncompressed, 0);
1379   HTTPMessage resp;
1380   resp.setStatusCode(200);
1381   // send a push response that will be ignored
1382   downstreamCodec_.generateHeader(output_, 2, resp, false, &size);
1383   // window update for push doesn't make any sense, but whatever
1384   downstreamCodec_.generateWindowUpdate(output_, 2, 100);
1385   downstreamCodec_.generateBody(
1386       output_, 2, makeBuf(10), HTTPCodec::NoPadding, false);
1387   writeFrameHeaderManual(output_, 20, (uint8_t)http2::FrameType::DATA, 0, 2);
1388   output_.append(makeBuf(10));
1389 
1390   // tell the upstream no pushing, and parse the first batch
1391   IOBufQueue dummy;
1392   upstreamCodec_.generateGoaway(dummy, 0, ErrorCode::NO_ERROR);
1393   parseUpstream();
1394 
1395   output_.append(makeBuf(10));
1396   downstreamCodec_.generatePriority(
1397       output_, 2, HTTPMessage::HTTP2Priority(0, true, 1));
1398   downstreamCodec_.generateEOM(output_, 2);
1399   downstreamCodec_.generateRstStream(output_, 2, ErrorCode::CANCEL);
1400 
1401   // send a response that will be accepted, headers should be ok
1402   downstreamCodec_.generateHeader(output_, 1, resp, true, &size);
1403   EXPECT_GT(size.uncompressed, 0);
1404 
1405   // parse the remainder
1406   parseUpstream();
1407   callbacks_.expectMessage(true, 1, 200);
1408 }
1409 
TEST_F(HTTP2CodecTest,GoawayReply)1410 TEST_F(HTTP2CodecTest, GoawayReply) {
1411   upstreamCodec_.generateGoaway(output_, 0, ErrorCode::NO_ERROR);
1412 
1413   parse();
1414   EXPECT_EQ(callbacks_.goaways, 1);
1415   EXPECT_EQ(callbacks_.streamErrors, 0);
1416   EXPECT_EQ(callbacks_.sessionErrors, 0);
1417 
1418   SetUpUpstreamTest();
1419   HTTPMessage resp;
1420   resp.setStatusCode(200);
1421   resp.setStatusMessage("nifty-nice");
1422   downstreamCodec_.generateHeader(output_, 1, resp);
1423   downstreamCodec_.generateEOM(output_, 1);
1424   parseUpstream();
1425   callbacks_.expectMessage(true, 1, 200);
1426   EXPECT_TRUE(callbacks_.msg->getHeaders().exists(HTTP_HEADER_DATE));
1427 }
1428 
TEST_F(HTTP2CodecTest,BasicSetting)1429 TEST_F(HTTP2CodecTest, BasicSetting) {
1430   auto settings = upstreamCodec_.getEgressSettings();
1431   settings->setSetting(SettingsId::MAX_CONCURRENT_STREAMS, 37);
1432   settings->setSetting(SettingsId::INITIAL_WINDOW_SIZE, 12345);
1433   upstreamCodec_.generateSettings(output_);
1434 
1435   parse();
1436   EXPECT_EQ(callbacks_.settings, 1);
1437   EXPECT_EQ(callbacks_.maxStreams, 37);
1438   EXPECT_EQ(callbacks_.windowSize, 12345);
1439   EXPECT_EQ(callbacks_.streamErrors, 0);
1440   EXPECT_EQ(callbacks_.sessionErrors, 0);
1441 }
1442 
TEST_F(HTTP2CodecTest,SettingsAck)1443 TEST_F(HTTP2CodecTest, SettingsAck) {
1444   upstreamCodec_.generateSettingsAck(output_);
1445 
1446   parse();
1447   EXPECT_EQ(callbacks_.settings, 0);
1448   EXPECT_EQ(callbacks_.settingsAcks, 1);
1449   EXPECT_EQ(callbacks_.streamErrors, 0);
1450   EXPECT_EQ(callbacks_.sessionErrors, 0);
1451 }
1452 
TEST_F(HTTP2CodecTest,BadSettings)1453 TEST_F(HTTP2CodecTest, BadSettings) {
1454   auto settings = upstreamCodec_.getEgressSettings();
1455   settings->setSetting(SettingsId::INITIAL_WINDOW_SIZE, 0xffffffff);
1456   upstreamCodec_.generateSettings(output_);
1457 
1458   parse();
1459   EXPECT_EQ(callbacks_.settings, 0);
1460   EXPECT_EQ(callbacks_.streamErrors, 0);
1461   EXPECT_EQ(callbacks_.sessionErrors, 1);
1462 }
1463 
TEST_F(HTTP2CodecTest,BadPushSettings)1464 TEST_F(HTTP2CodecTest, BadPushSettings) {
1465   auto settings = downstreamCodec_.getEgressSettings();
1466   settings->clearSettings();
1467   settings->setSetting(SettingsId::ENABLE_PUSH, 0);
1468   SetUpUpstreamTest();
1469 
1470   parseUpstream([&](IOBuf* ingress) {
1471     EXPECT_EQ(ingress->computeChainDataLength(), http2::kFrameHeaderSize);
1472   });
1473   EXPECT_FALSE(upstreamCodec_.supportsPushTransactions());
1474   // Only way to disable push for downstreamCodec_ is to read
1475   // ENABLE_PUSH:0 from client
1476   EXPECT_TRUE(downstreamCodec_.supportsPushTransactions());
1477   EXPECT_EQ(callbacks_.settings, 1);
1478   EXPECT_EQ(callbacks_.streamErrors, 0);
1479   EXPECT_EQ(callbacks_.sessionErrors, 0);
1480 }
1481 
TEST_F(HTTP2CodecTest,SettingsTableSize)1482 TEST_F(HTTP2CodecTest, SettingsTableSize) {
1483   auto settings = upstreamCodec_.getEgressSettings();
1484   settings->setSetting(SettingsId::HEADER_TABLE_SIZE, 8192);
1485   upstreamCodec_.generateSettings(output_);
1486 
1487   parse();
1488   EXPECT_EQ(callbacks_.settings, 1);
1489   EXPECT_EQ(callbacks_.streamErrors, 0);
1490   EXPECT_EQ(callbacks_.sessionErrors, 0);
1491   downstreamCodec_.generateSettingsAck(output_);
1492   parseUpstream();
1493 
1494   callbacks_.reset();
1495 
1496   HTTPMessage resp;
1497   resp.setStatusCode(200);
1498   resp.setStatusMessage("nifty-nice");
1499   resp.getHeaders().add(HTTP_HEADER_CONTENT_TYPE, "x-coolio");
1500   SetUpUpstreamTest();
1501   downstreamCodec_.generateHeader(output_, 1, resp);
1502 
1503   parseUpstream();
1504   callbacks_.expectMessage(false, 2, 200);
1505   const auto& headers = callbacks_.msg->getHeaders();
1506   EXPECT_TRUE(callbacks_.msg->getHeaders().exists(HTTP_HEADER_DATE));
1507   EXPECT_EQ("x-coolio", headers.getSingleOrEmpty(HTTP_HEADER_CONTENT_TYPE));
1508 }
1509 
TEST_F(HTTP2CodecTest,BadSettingsTableSize)1510 TEST_F(HTTP2CodecTest, BadSettingsTableSize) {
1511   auto settings = upstreamCodec_.getEgressSettings();
1512   settings->setSetting(SettingsId::HEADER_TABLE_SIZE, 8192);
1513   // This sets the max decoder table size to 8k
1514   upstreamCodec_.generateSettings(output_);
1515 
1516   parse();
1517   EXPECT_EQ(callbacks_.settings, 1);
1518   EXPECT_EQ(callbacks_.streamErrors, 0);
1519   EXPECT_EQ(callbacks_.sessionErrors, 0);
1520 
1521   callbacks_.reset();
1522 
1523   // Attempt to set a new max table size.  This is a no-op because the first,
1524   // setting is unacknowledged.  The upstream encoder will up the table size to
1525   // 8k per the first settings frame and the HPACK codec will send a code to
1526   // update the decoder.
1527   settings->setSetting(SettingsId::HEADER_TABLE_SIZE, 4096);
1528 
1529   HTTPMessage resp;
1530   resp.setStatusCode(200);
1531   resp.setStatusMessage("nifty-nice");
1532   resp.getHeaders().add(HTTP_HEADER_CONTENT_TYPE, "x-coolio");
1533   SetUpUpstreamTest();
1534   downstreamCodec_.generateHeader(output_, 1, resp);
1535 
1536   parseUpstream();
1537   EXPECT_EQ(callbacks_.streamErrors, 0);
1538   EXPECT_EQ(callbacks_.sessionErrors, 1);
1539   EXPECT_EQ(callbacks_.messageBegin, 0);
1540   EXPECT_EQ(callbacks_.headersComplete, 0);
1541 }
1542 
TEST_F(HTTP2CodecTest,SettingsTableSizeEarlyShrink)1543 TEST_F(HTTP2CodecTest, SettingsTableSizeEarlyShrink) {
1544   // Lower size to 2k
1545   auto settings = upstreamCodec_.getEgressSettings();
1546   settings->setSetting(SettingsId::HEADER_TABLE_SIZE, 2048);
1547   upstreamCodec_.generateSettings(output_);
1548 
1549   parse();
1550   EXPECT_EQ(callbacks_.settings, 1);
1551   EXPECT_EQ(callbacks_.streamErrors, 0);
1552   EXPECT_EQ(callbacks_.sessionErrors, 0);
1553   downstreamCodec_.generateSettingsAck(output_);
1554   // Parsing SETTINGS ack updates upstream decoder to 2k
1555   parseUpstream();
1556 
1557   callbacks_.reset();
1558 
1559   HTTPMessage resp;
1560   resp.setStatusCode(200);
1561   resp.setStatusMessage("nifty-nice");
1562   resp.getHeaders().add(HTTP_HEADER_CONTENT_TYPE, "x-coolio");
1563   SetUpUpstreamTest();
1564   // downstream encoder will send TSU/2k
1565   downstreamCodec_.generateHeader(output_, 1, resp);
1566 
1567   // sets pending table size to 512, but doesn't update it yet
1568   settings = upstreamCodec_.getEgressSettings();
1569   settings->setSetting(SettingsId::HEADER_TABLE_SIZE, 512);
1570   IOBufQueue tmp{IOBufQueue::cacheChainLength()};
1571   upstreamCodec_.generateSettings(tmp);
1572 
1573   // Previous code would barf here, since TSU/2k is a violation of the current
1574   // max=512
1575   parseUpstream();
1576   callbacks_.expectMessage(false, 2, 200);
1577   const auto& headers = callbacks_.msg->getHeaders();
1578   EXPECT_TRUE(callbacks_.msg->getHeaders().exists(HTTP_HEADER_DATE));
1579   EXPECT_EQ("x-coolio", headers.getSingleOrEmpty(HTTP_HEADER_CONTENT_TYPE));
1580 }
1581 
TEST_F(HTTP2CodecTest,ConcurrentStreams)1582 TEST_F(HTTP2CodecTest, ConcurrentStreams) {
1583   auto settings = upstreamCodec_.getEgressSettings();
1584   settings->setSetting(SettingsId::ENABLE_PUSH, 1);
1585   settings->setSetting(SettingsId::MAX_CONCURRENT_STREAMS, 0);
1586   upstreamCodec_.generateSettings(output_);
1587   HTTPMessage req = getGetRequest();
1588   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
1589 
1590   parse();
1591   EXPECT_EQ(callbacks_.maxStreams, 0);
1592 
1593   callbacks_.reset();
1594   SetUpUpstreamTest();
1595   HTTPMessage resp;
1596   resp.setStatusCode(200);
1597   downstreamCodec_.generateHeader(output_, 1, resp, false /* eom */);
1598   downstreamCodec_.generatePushPromise(output_, 2, req, 1);
1599   parseUpstream();
1600   EXPECT_EQ(callbacks_.messageBegin, 2);
1601   EXPECT_EQ(callbacks_.headersComplete, 2);
1602   EXPECT_EQ(callbacks_.messageComplete, 0);
1603   EXPECT_EQ(callbacks_.streamErrors, 0);
1604   EXPECT_EQ(callbacks_.sessionErrors, 0);
1605   EXPECT_NE(callbacks_.msg, nullptr);
1606   EXPECT_EQ(callbacks_.pushId, 2);
1607   EXPECT_EQ(callbacks_.assocStreamId, 1);
1608   callbacks_.reset();
1609   downstreamCodec_.generateHeader(output_, 2, resp, true /* eom */);
1610   parseUpstream();
1611   EXPECT_EQ(callbacks_.headersComplete, 0);
1612   EXPECT_EQ(callbacks_.streamErrors, 1);
1613   EXPECT_EQ(callbacks_.lastParseError->getCodecStatusCode(),
1614             ErrorCode::REFUSED_STREAM);
1615 }
1616 
TEST_F(HTTP2CodecTest,BasicPriority)1617 TEST_F(HTTP2CodecTest, BasicPriority) {
1618   auto pri = HTTPMessage::HTTP2Priority(0, true, 1);
1619   upstreamCodec_.generatePriority(output_, 1, pri);
1620 
1621   EXPECT_TRUE(parse());
1622   EXPECT_EQ(callbacks_.priority, pri);
1623   EXPECT_EQ(callbacks_.streamErrors, 0);
1624   EXPECT_EQ(callbacks_.sessionErrors, 0);
1625 }
1626 
TEST_F(HTTP2CodecTest,BadHeaderPriority)1627 TEST_F(HTTP2CodecTest, BadHeaderPriority) {
1628   HTTPMessage req = getGetRequest();
1629   req.setHTTP2Priority(HTTPMessage::HTTP2Priority(0, false, 7));
1630   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
1631 
1632   // hack ingress with cirular dep
1633   EXPECT_TRUE(parse([&](IOBuf* ingress) {
1634     folly::io::RWPrivateCursor c(ingress);
1635     c.skip(http2::kFrameHeaderSize + http2::kConnectionPreface.length());
1636     c.writeBE<uint32_t>(1);
1637   }));
1638 
1639   EXPECT_EQ(callbacks_.streamErrors, 1);
1640   EXPECT_EQ(callbacks_.sessionErrors, 0);
1641 }
1642 
TEST_F(HTTP2CodecTest,CircularHeaderPriority)1643 TEST_F(HTTP2CodecTest, CircularHeaderPriority) {
1644   HTTPMessage req = getGetRequest();
1645   req.setHTTP2Priority(HTTPMessage::HTTP2Priority(1, false, 7));
1646   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
1647 }
1648 
TEST_F(HTTP2CodecTest,DuplicateBadHeaderPriority)1649 TEST_F(HTTP2CodecTest, DuplicateBadHeaderPriority) {
1650   // Sent an initial header with a circular dependency
1651   HTTPMessage req = getGetRequest();
1652   req.setHTTP2Priority(HTTPMessage::HTTP2Priority(0, false, 7));
1653   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
1654 
1655   // Hack ingress with circular dependency.
1656   EXPECT_TRUE(parse([&](IOBuf* ingress) {
1657     folly::io::RWPrivateCursor c(ingress);
1658     c.skip(http2::kFrameHeaderSize + http2::kConnectionPreface.length());
1659     c.writeBE<uint32_t>(1);
1660   }));
1661 
1662   EXPECT_EQ(callbacks_.streamErrors, 1);
1663   EXPECT_EQ(callbacks_.sessionErrors, 0);
1664 
1665   // On the same stream, send another request.
1666   HTTPMessage nextRequest = getGetRequest();
1667   upstreamCodec_.generateHeader(output_, 1, nextRequest, true /* eom */);
1668   parse();
1669   EXPECT_EQ(callbacks_.streamErrors, 2);
1670   EXPECT_EQ(callbacks_.sessionErrors, 0);
1671 }
1672 
TEST_F(HTTP2CodecTest,BadPriority)1673 TEST_F(HTTP2CodecTest, BadPriority) {
1674   auto pri = HTTPMessage::HTTP2Priority(0, true, 1);
1675   upstreamCodec_.generatePriority(output_, 1, pri);
1676 
1677   // hack ingress with cirular dep
1678   EXPECT_TRUE(parse([&](IOBuf* ingress) {
1679     folly::io::RWPrivateCursor c(ingress);
1680     c.skip(http2::kFrameHeaderSize + http2::kConnectionPreface.length());
1681     c.writeBE<uint32_t>(1);
1682   }));
1683 
1684   EXPECT_EQ(callbacks_.streamErrors, 1);
1685   EXPECT_EQ(callbacks_.sessionErrors, 0);
1686 }
1687 
1688 class DummyQueue : public HTTPCodec::PriorityQueue {
1689  public:
DummyQueue()1690   DummyQueue() {
1691   }
~DummyQueue()1692   ~DummyQueue() override {
1693   }
addPriorityNode(HTTPCodec::StreamID id,HTTPCodec::StreamID)1694   void addPriorityNode(HTTPCodec::StreamID id, HTTPCodec::StreamID) override {
1695     nodes_.push_back(id);
1696   }
1697 
1698   std::vector<HTTPCodec::StreamID> nodes_;
1699 };
1700 
TEST_F(HTTP2CodecTest,VirtualNodes)1701 TEST_F(HTTP2CodecTest, VirtualNodes) {
1702   DummyQueue queue;
1703   uint8_t level = 30;
1704   upstreamCodec_.addPriorityNodes(queue, output_, level);
1705 
1706   EXPECT_TRUE(parse());
1707   for (int i = 0; i < level; i++) {
1708     EXPECT_EQ(queue.nodes_[i], upstreamCodec_.mapPriorityToDependency(i));
1709   }
1710 
1711   // Out-of-range priorites are mapped to the lowest level of virtual nodes.
1712   EXPECT_EQ(queue.nodes_[level - 1],
1713             upstreamCodec_.mapPriorityToDependency(level));
1714   EXPECT_EQ(queue.nodes_[level - 1],
1715             upstreamCodec_.mapPriorityToDependency(level + 1));
1716 }
1717 
TEST_F(HTTP2CodecTest,BasicPushPromise)1718 TEST_F(HTTP2CodecTest, BasicPushPromise) {
1719   upstreamCodec_.generateSettings(output_);
1720   parse();
1721   EXPECT_FALSE(upstreamCodec_.supportsPushTransactions());
1722   EXPECT_FALSE(downstreamCodec_.supportsPushTransactions());
1723 
1724   auto settings = upstreamCodec_.getEgressSettings();
1725   settings->setSetting(SettingsId::ENABLE_PUSH, 1);
1726   upstreamCodec_.generateSettings(output_);
1727   parse();
1728   EXPECT_TRUE(upstreamCodec_.supportsPushTransactions());
1729   EXPECT_TRUE(downstreamCodec_.supportsPushTransactions());
1730 
1731   SetUpUpstreamTest();
1732 
1733   HTTPCodec::StreamID assocStream = 7;
1734   for (auto i = 0; i < 2; i++) {
1735     // Push promise
1736     HTTPCodec::StreamID pushStream = downstreamCodec_.createStream();
1737     HTTPMessage req = getGetRequest();
1738     req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
1739     downstreamCodec_.generatePushPromise(output_, pushStream, req, assocStream);
1740 
1741     parseUpstream();
1742     callbacks_.expectMessage(false, 2, "/"); // + host
1743     EXPECT_EQ(callbacks_.assocStreamId, assocStream);
1744     EXPECT_EQ(callbacks_.headersCompleteId, pushStream);
1745     auto& headers = callbacks_.msg->getHeaders();
1746     EXPECT_EQ("coolio", headers.getSingleOrEmpty(HTTP_HEADER_USER_AGENT));
1747     callbacks_.reset();
1748 
1749     // Actual reply headers
1750     HTTPMessage resp;
1751     resp.setStatusCode(200);
1752     resp.getHeaders().add(HTTP_HEADER_CONTENT_TYPE, "text/plain");
1753     downstreamCodec_.generateHeader(output_, pushStream, resp);
1754 
1755     parseUpstream();
1756     callbacks_.expectMessage(false, 2, 200);
1757     EXPECT_EQ(callbacks_.headersCompleteId, pushStream);
1758     EXPECT_EQ(callbacks_.assocStreamId, 0);
1759     EXPECT_TRUE(callbacks_.msg->getHeaders().exists(HTTP_HEADER_DATE));
1760     EXPECT_EQ("text/plain",
1761               callbacks_.msg->getHeaders().getSingleOrEmpty(
1762                   HTTP_HEADER_CONTENT_TYPE));
1763     callbacks_.reset();
1764   }
1765 }
1766 
TEST_F(HTTP2CodecTest,DuplicatePushPromise)1767 TEST_F(HTTP2CodecTest, DuplicatePushPromise) {
1768   auto settings = upstreamCodec_.getEgressSettings();
1769   settings->setSetting(SettingsId::ENABLE_PUSH, 1);
1770   upstreamCodec_.generateSettings(output_);
1771   parse();
1772   EXPECT_TRUE(upstreamCodec_.supportsPushTransactions());
1773   EXPECT_TRUE(downstreamCodec_.supportsPushTransactions());
1774 
1775   SetUpUpstreamTest();
1776 
1777   HTTPCodec::StreamID assocStream = 7;
1778   HTTPCodec::StreamID pushStream = downstreamCodec_.createStream();
1779   HTTPMessage req = getGetRequest();
1780   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
1781   downstreamCodec_.generatePushPromise(output_, pushStream, req, assocStream);
1782   downstreamCodec_.generatePushPromise(output_, pushStream, req, assocStream);
1783 
1784   parseUpstream();
1785 
1786   EXPECT_EQ(callbacks_.streamErrors, 0);
1787   EXPECT_EQ(callbacks_.sessionErrors, 1);
1788 }
1789 
TEST_F(HTTP2CodecTest,BadPushPromise)1790 TEST_F(HTTP2CodecTest, BadPushPromise) {
1791   // ENABLE_PUSH is now 0 by default
1792   SetUpUpstreamTest();
1793   HTTPMessage req = getGetRequest();
1794   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
1795   downstreamCodec_.generatePushPromise(output_, 2, req, 1);
1796 
1797   parseUpstream();
1798   EXPECT_EQ(callbacks_.messageBegin, 0);
1799   EXPECT_EQ(callbacks_.headersComplete, 0);
1800   EXPECT_EQ(callbacks_.messageComplete, 0);
1801   EXPECT_EQ(callbacks_.assocStreamId, 0);
1802   EXPECT_EQ(callbacks_.streamErrors, 0);
1803   EXPECT_EQ(callbacks_.sessionErrors, 1);
1804 }
1805 
TEST_F(HTTP2CodecTest,BadPushPromiseResets)1806 TEST_F(HTTP2CodecTest, BadPushPromiseResets) {
1807   auto settings = upstreamCodec_.getEgressSettings();
1808   settings->setSetting(SettingsId::ENABLE_PUSH, 1);
1809   upstreamCodec_.generateSettings(output_);
1810   SetUpUpstreamTest();
1811   HTTPMessage req = getGetRequest();
1812   req.getHeaders().add(HTTP_HEADER_CONTENT_LENGTH, "100");
1813   req.getHeaders().add(HTTP_HEADER_CONTENT_LENGTH, "200");
1814   downstreamCodec_.generatePushPromise(output_, 2, req, 1);
1815 
1816   parseUpstream();
1817   EXPECT_EQ(callbacks_.messageBegin, 1);
1818   EXPECT_EQ(callbacks_.headersComplete, 0);
1819   EXPECT_EQ(callbacks_.messageComplete, 0);
1820   EXPECT_EQ(callbacks_.assocStreamId, 1);
1821   EXPECT_EQ(callbacks_.streamErrors, 2);
1822   EXPECT_EQ(callbacks_.sessionErrors, 0);
1823 }
1824 
TEST_F(HTTP2CodecTest,BasicCertificateRequest)1825 TEST_F(HTTP2CodecTest, BasicCertificateRequest) {
1826   uint16_t requestId = 17;
1827   std::unique_ptr<folly::IOBuf> authRequest =
1828       folly::IOBuf::copyBuffer("authRequestData");
1829   upstreamCodec_.generateCertificateRequest(
1830       output_, requestId, std::move(authRequest));
1831 
1832   parse();
1833   EXPECT_EQ(callbacks_.certificateRequests, 1);
1834   EXPECT_EQ(callbacks_.lastCertRequestId, requestId);
1835   EXPECT_EQ(callbacks_.data_.move()->moveToFbString(), "authRequestData");
1836   EXPECT_EQ(callbacks_.streamErrors, 0);
1837   EXPECT_EQ(callbacks_.sessionErrors, 0);
1838 }
1839 
TEST_F(HTTP2CodecTest,BasicCertificate)1840 TEST_F(HTTP2CodecTest, BasicCertificate) {
1841   uint16_t certId = 17;
1842   std::unique_ptr<folly::IOBuf> authenticator =
1843       folly::IOBuf::copyBuffer("authenticatorData");
1844   upstreamCodec_.generateCertificate(output_, certId, std::move(authenticator));
1845 
1846   parse();
1847   EXPECT_EQ(callbacks_.certificates, 1);
1848   EXPECT_EQ(callbacks_.lastCertId, certId);
1849   EXPECT_EQ(callbacks_.data_.move()->moveToFbString(), "authenticatorData");
1850   EXPECT_EQ(callbacks_.streamErrors, 0);
1851   EXPECT_EQ(callbacks_.sessionErrors, 0);
1852 }
1853 
TEST_F(HTTP2CodecTest,BadServerPreface)1854 TEST_F(HTTP2CodecTest, BadServerPreface) {
1855   output_.reset();
1856   downstreamCodec_.generateWindowUpdate(output_, 0, 10);
1857   parseUpstream();
1858   EXPECT_EQ(callbacks_.messageBegin, 0);
1859   EXPECT_EQ(callbacks_.headersComplete, 0);
1860   EXPECT_EQ(callbacks_.messageComplete, 0);
1861   EXPECT_EQ(callbacks_.assocStreamId, 0);
1862   EXPECT_EQ(callbacks_.streamErrors, 0);
1863   EXPECT_EQ(callbacks_.sessionErrors, 1);
1864 }
1865 
TEST_F(HTTP2CodecTest,Normal1024Continuation)1866 TEST_F(HTTP2CodecTest, Normal1024Continuation) {
1867   HTTPMessage req = getGetRequest();
1868   string bigval(8691, '!');
1869   bigval.append(8691, '@');
1870   req.getHeaders().add("x-headr", bigval);
1871   req.setHTTP2Priority(HTTPMessage::HTTP2Priority(0, false, 7));
1872   upstreamCodec_.generateHeader(output_, 1, req);
1873 
1874   parse();
1875   callbacks_.expectMessage(false, -1, "/");
1876   const auto& headers = callbacks_.msg->getHeaders();
1877   EXPECT_EQ(bigval, headers.getSingleOrEmpty("x-headr"));
1878   EXPECT_EQ(callbacks_.messageBegin, 1);
1879   EXPECT_EQ(callbacks_.headersComplete, 1);
1880   EXPECT_EQ(callbacks_.messageComplete, 0);
1881   EXPECT_EQ(callbacks_.streamErrors, 0);
1882   EXPECT_EQ(callbacks_.sessionErrors, 0);
1883 
1884   upstreamCodec_.generateSettingsAck(output_);
1885   parse();
1886   EXPECT_EQ(callbacks_.settingsAcks, 1);
1887 }
1888 
TEST_F(HTTP2CodecTest,StreamIdOverflow)1889 TEST_F(HTTP2CodecTest, StreamIdOverflow) {
1890   HTTP2Codec codec(TransportDirection::UPSTREAM);
1891 
1892   HTTPCodec::StreamID streamId;
1893   codec.setNextEgressStreamId(std::numeric_limits<int32_t>::max() - 10);
1894   while (codec.isReusable()) {
1895     streamId = codec.createStream();
1896   }
1897   EXPECT_EQ(streamId, std::numeric_limits<int32_t>::max() - 2);
1898 }
1899 
TEST_F(HTTP2CodecTest,TestMultipleDifferentContentLengthHeaders)1900 TEST_F(HTTP2CodecTest, TestMultipleDifferentContentLengthHeaders) {
1901   // Generate a POST request with two Content-Length headers
1902   // NOTE: getPostRequest already adds the content-length
1903   HTTPMessage req = getPostRequest();
1904   req.getHeaders().add(HTTP_HEADER_CONTENT_LENGTH, "300");
1905   EXPECT_EQ(req.getHeaders().getNumberOfValues(HTTP_HEADER_CONTENT_LENGTH), 2);
1906 
1907   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
1908   parse();
1909 
1910   // Check that the request fails before the codec finishes parsing the headers
1911   EXPECT_EQ(callbacks_.streamErrors, 1);
1912   EXPECT_EQ(callbacks_.headersComplete, 0);
1913   EXPECT_EQ(callbacks_.lastParseError->getHttpStatusCode(), 400);
1914 }
1915 
TEST_F(HTTP2CodecTest,TestMultipleIdenticalContentLengthHeaders)1916 TEST_F(HTTP2CodecTest, TestMultipleIdenticalContentLengthHeaders) {
1917   // Generate a POST request with two Content-Length headers
1918   // NOTE: getPostRequest already adds the content-length
1919   HTTPMessage req = getPostRequest();
1920   req.getHeaders().add("content-length", "200");
1921   EXPECT_EQ(req.getHeaders().getNumberOfValues("content-length"), 2);
1922 
1923   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
1924   parse();
1925 
1926   // Check that the headers parsing completes correctly
1927   EXPECT_EQ(callbacks_.streamErrors, 0);
1928   EXPECT_EQ(callbacks_.headersComplete, 1);
1929 }
1930 
1931 namespace {
testRequestUpgrade(HTTPMessage & req,size_t numConnectionHeaders)1932 void testRequestUpgrade(HTTPMessage& req, size_t numConnectionHeaders) {
1933   HTTP2Codec::requestUpgrade(req);
1934   EXPECT_EQ(req.getHeaders().getSingleOrEmpty(HTTP_HEADER_UPGRADE), "h2c");
1935   EXPECT_TRUE(
1936       req.checkForHeaderToken(HTTP_HEADER_CONNECTION, "Upgrade", false));
1937   EXPECT_TRUE(req.checkForHeaderToken(
1938       HTTP_HEADER_CONNECTION, http2::kProtocolSettingsHeader.c_str(), false));
1939   EXPECT_EQ(req.getHeaders().getNumberOfValues(HTTP_HEADER_CONNECTION),
1940             numConnectionHeaders);
1941   EXPECT_GT(req.getHeaders()
1942                 .getSingleOrEmpty(http2::kProtocolSettingsHeader)
1943                 .length(),
1944             0);
1945 }
1946 } // namespace
1947 
TEST_F(HTTP2CodecTest,CleartextUpgrade)1948 TEST_F(HTTP2CodecTest, CleartextUpgrade) {
1949   HTTPMessage req = getGetRequest("/guacamole");
1950   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
1951   testRequestUpgrade(req, 1);
1952   req.stripPerHopHeaders();
1953 
1954   req.getHeaders().add(HTTP_HEADER_CONNECTION, "Foo");
1955   testRequestUpgrade(req, 2);
1956   req.stripPerHopHeaders();
1957 
1958   req.getHeaders().add(HTTP_HEADER_CONNECTION, "Upgrade");
1959   testRequestUpgrade(req, 2);
1960   req.stripPerHopHeaders();
1961 
1962   req.getHeaders().add(HTTP_HEADER_CONNECTION, "HTTP2-Settings");
1963   testRequestUpgrade(req, 2);
1964 }
1965 
TEST_F(HTTP2CodecTest,HTTP2SettingsSuccess)1966 TEST_F(HTTP2CodecTest, HTTP2SettingsSuccess) {
1967   HTTPMessage req = getGetRequest("/guacamole");
1968 
1969   // empty settings
1970   req.getHeaders().add(http2::kProtocolSettingsHeader, "");
1971   EXPECT_TRUE(downstreamCodec_.onIngressUpgradeMessage(req));
1972 
1973   // real settings (overwrites empty)
1974   HTTP2Codec::requestUpgrade(req);
1975   EXPECT_TRUE(downstreamCodec_.onIngressUpgradeMessage(req));
1976 }
1977 
TEST_F(HTTP2CodecTest,HTTP2SettingsFailure)1978 TEST_F(HTTP2CodecTest, HTTP2SettingsFailure) {
1979   HTTPMessage req = getGetRequest("/guacamole");
1980   // no settings
1981   EXPECT_FALSE(downstreamCodec_.onIngressUpgradeMessage(req));
1982 
1983   HTTPHeaders& headers = req.getHeaders();
1984 
1985   // Not base64_url settings
1986   headers.set(http2::kProtocolSettingsHeader, "????");
1987   EXPECT_FALSE(downstreamCodec_.onIngressUpgradeMessage(req));
1988   headers.set(http2::kProtocolSettingsHeader, "AAA");
1989   EXPECT_FALSE(downstreamCodec_.onIngressUpgradeMessage(req));
1990 
1991   // Too big
1992   string bigSettings((http2::kMaxFramePayloadLength + 1) * 4 / 3, 'A');
1993   headers.set(http2::kProtocolSettingsHeader, bigSettings);
1994   EXPECT_FALSE(downstreamCodec_.onIngressUpgradeMessage(req));
1995 
1996   // Malformed (not a multiple of 6)
1997   headers.set(http2::kProtocolSettingsHeader, "AAAA");
1998   EXPECT_FALSE(downstreamCodec_.onIngressUpgradeMessage(req));
1999 
2000   // Two headers
2001   headers.set(http2::kProtocolSettingsHeader, "AAAAAAAA");
2002   headers.add(http2::kProtocolSettingsHeader, "AAAAAAAA");
2003   EXPECT_FALSE(downstreamCodec_.onIngressUpgradeMessage(req));
2004 }
2005 
TEST_F(HTTP2CodecTest,HTTP2EnableConnect)2006 TEST_F(HTTP2CodecTest, HTTP2EnableConnect) {
2007   SetUpUpstreamTest();
2008   // egress settings have no connect settings.
2009   auto ws_enable = upstreamCodec_.getEgressSettings()->getSetting(
2010       SettingsId::ENABLE_CONNECT_PROTOCOL);
2011   // enable connect settings, and check.
2012   upstreamCodec_.getEgressSettings()->setSetting(
2013       SettingsId::ENABLE_CONNECT_PROTOCOL, 1);
2014   ws_enable = upstreamCodec_.getEgressSettings()->getSetting(
2015       SettingsId::ENABLE_CONNECT_PROTOCOL);
2016   EXPECT_EQ(ws_enable->value, 1);
2017   // generateSettings.
2018   // pass the buffer to be parsed by the codec and check for ingress settings.
2019   upstreamCodec_.generateSettings(output_);
2020   parseUpstream();
2021   EXPECT_EQ(1, upstreamCodec_.peerHasWebsockets());
2022 }
2023 
TEST_F(HTTP2CodecTest,WebsocketUpgrade)2024 TEST_F(HTTP2CodecTest, WebsocketUpgrade) {
2025   HTTPMessage req = getGetRequest("/apples");
2026   req.setSecure(true);
2027   req.setEgressWebsocketUpgrade();
2028 
2029   upstreamCodec_.generateHeader(output_, 1, req, false);
2030   parse();
2031 
2032   EXPECT_TRUE(callbacks_.msg->isIngressWebsocketUpgrade());
2033   EXPECT_NE(nullptr, callbacks_.msg->getUpgradeProtocol());
2034   EXPECT_EQ(headers::kWebsocketString, *callbacks_.msg->getUpgradeProtocol());
2035 }
2036 
TEST_F(HTTP2CodecTest,WebsocketBadHeader)2037 TEST_F(HTTP2CodecTest, WebsocketBadHeader) {
2038   const std::string kConnect{"CONNECT"};
2039   const std::string kWebsocketPath{"/websocket"};
2040   const std::string kSchemeHttps{"https"};
2041   vector<proxygen::compress::Header> reqHeaders = {
2042       Header::makeHeaderForTest(headers::kMethod, kConnect),
2043       Header::makeHeaderForTest(headers::kProtocol, headers::kWebsocketString),
2044   };
2045   vector<proxygen::compress::Header> optionalHeaders = {
2046       Header::makeHeaderForTest(headers::kPath, kWebsocketPath),
2047       Header::makeHeaderForTest(headers::kScheme, kSchemeHttps),
2048   };
2049 
2050   HPACKCodec headerCodec(TransportDirection::UPSTREAM);
2051   int stream = 1;
2052   for (size_t i = 0; i < optionalHeaders.size(); ++i, stream += 2) {
2053     auto headers = reqHeaders;
2054     headers.push_back(optionalHeaders[i]);
2055     auto encodedHeaders = headerCodec.encode(headers);
2056     writeHeaders(output_,
2057                  std::move(encodedHeaders),
2058                  stream,
2059                  folly::none,
2060                  http2::kNoPadding,
2061                  false,
2062                  true);
2063     parse();
2064   }
2065 
2066   EXPECT_EQ(callbacks_.messageBegin, optionalHeaders.size());
2067   EXPECT_EQ(callbacks_.headersComplete, 0);
2068   EXPECT_EQ(callbacks_.messageComplete, 0);
2069   EXPECT_EQ(callbacks_.streamErrors, optionalHeaders.size());
2070   EXPECT_EQ(callbacks_.sessionErrors, 0);
2071 }
2072 
TEST_F(HTTP2CodecTest,WebsocketDupProtocol)2073 TEST_F(HTTP2CodecTest, WebsocketDupProtocol) {
2074   const std::string kConnect{"CONNECT"};
2075   const std::string kWebsocketPath{"/websocket"};
2076   const std::string kSchemeHttps{"https"};
2077   vector<proxygen::compress::Header> headers = {
2078       Header::makeHeaderForTest(headers::kMethod, kConnect),
2079       Header::makeHeaderForTest(headers::kProtocol, headers::kWebsocketString),
2080       Header::makeHeaderForTest(headers::kProtocol, headers::kWebsocketString),
2081       Header::makeHeaderForTest(headers::kPath, kWebsocketPath),
2082       Header::makeHeaderForTest(headers::kScheme, kSchemeHttps),
2083   };
2084   HPACKCodec headerCodec(TransportDirection::UPSTREAM);
2085   auto encodedHeaders = headerCodec.encode(headers);
2086   writeHeaders(output_,
2087                std::move(encodedHeaders),
2088                1,
2089                folly::none,
2090                http2::kNoPadding,
2091                false,
2092                true);
2093   parse();
2094   EXPECT_EQ(callbacks_.messageBegin, 1);
2095   EXPECT_EQ(callbacks_.headersComplete, 0);
2096   EXPECT_EQ(callbacks_.messageComplete, 0);
2097   EXPECT_EQ(callbacks_.streamErrors, 1);
2098   EXPECT_EQ(callbacks_.sessionErrors, 0);
2099 }
2100 
TEST_F(HTTP2CodecTest,WebsocketIncorrectResponse)2101 TEST_F(HTTP2CodecTest, WebsocketIncorrectResponse) {
2102   output_.reset();
2103   HTTPMessage resp;
2104   resp.setStatusCode(400);
2105   resp.setStatusMessage("Bad Request");
2106   downstreamCodec_.generateHeader(output_, 1, resp);
2107   parseUpstream();
2108   EXPECT_EQ(callbacks_.streamErrors, 0);
2109 }
2110 
TEST_F(HTTP2CodecTest,TestAllEgressFrameTypeCallbacks)2111 TEST_F(HTTP2CodecTest, TestAllEgressFrameTypeCallbacks) {
2112   class CallbackTypeTracker {
2113     std::set<uint8_t> types;
2114 
2115    public:
2116     void add(uint8_t, uint8_t type, uint64_t, uint16_t) {
2117       types.insert(type);
2118     }
2119 
2120     bool isAllFrameTypesReceived() {
2121       http2::FrameType expectedTypes[] = {
2122           http2::FrameType::DATA,
2123           http2::FrameType::HEADERS,
2124           http2::FrameType::PRIORITY,
2125           http2::FrameType::RST_STREAM,
2126           http2::FrameType::SETTINGS,
2127           http2::FrameType::PUSH_PROMISE,
2128           http2::FrameType::PING,
2129           http2::FrameType::GOAWAY,
2130           http2::FrameType::WINDOW_UPDATE,
2131           http2::FrameType::CONTINUATION,
2132           http2::FrameType::EX_HEADERS,
2133       };
2134 
2135       for (http2::FrameType type : expectedTypes) {
2136         EXPECT_TRUE(types.find(static_cast<uint8_t>(type)) != types.end())
2137             << "callback missing for type " << static_cast<uint8_t>(type);
2138       }
2139       return types.size() == (sizeof(expectedTypes) / sizeof(http2::FrameType));
2140     }
2141   };
2142 
2143   CallbackTypeTracker callbackTypeTracker;
2144 
2145   NiceMock<MockHTTPCodecCallback> mockCallback;
2146   upstreamCodec_.setCallback(&mockCallback);
2147   downstreamCodec_.setCallback(&mockCallback);
2148   EXPECT_CALL(mockCallback, onGenerateFrameHeader(_, _, _, _))
2149       .WillRepeatedly(Invoke(&callbackTypeTracker, &CallbackTypeTracker::add));
2150 
2151   // DATA frame
2152   string data("abcde");
2153   auto buf = folly::IOBuf::copyBuffer(data.data(), data.length());
2154   upstreamCodec_.generateBody(
2155       output_, 2, std::move(buf), HTTPCodec::NoPadding, true);
2156 
2157   HTTPHeaderSize size;
2158   size.uncompressed = size.compressed = 0;
2159   HTTPMessage req = getGetRequest();
2160   upstreamCodec_.generateHeader(output_, 1, req, true, &size);
2161 
2162   upstreamCodec_.generatePriority(
2163       output_, 3, HTTPMessage::HTTP2Priority(0, true, 1));
2164   upstreamCodec_.generateRstStream(output_, 2, ErrorCode::ENHANCE_YOUR_CALM);
2165   upstreamCodec_.generateSettings(output_);
2166   downstreamCodec_.generatePushPromise(output_, 2, req, 1);
2167   upstreamCodec_.generatePingRequest(output_);
2168 
2169   std::unique_ptr<folly::IOBuf> debugData =
2170       folly::IOBuf::copyBuffer("debugData");
2171   upstreamCodec_.generateGoaway(
2172       output_, 17, ErrorCode::ENHANCE_YOUR_CALM, std::move(debugData));
2173 
2174   upstreamCodec_.generateWindowUpdate(output_, 0, 10);
2175 
2176   HTTPCodec::StreamID stream = folly::Random::rand32(10, 1024) * 2;
2177   HTTPCodec::StreamID controlStream = folly::Random::rand32(10, 1024) * 2 + 1;
2178   downstreamCodec_.generateExHeader(
2179       output_, stream, req, HTTPCodec::ExAttributes(controlStream, true));
2180 
2181   // Tests the continuation frame
2182   req = getBigGetRequest();
2183   upstreamCodec_.generateHeader(output_, 1, req, true /* eom */);
2184 
2185   EXPECT_TRUE(callbackTypeTracker.isAllFrameTypesReceived());
2186 }
2187 
TEST_F(HTTP2CodecTest,Trailers)2188 TEST_F(HTTP2CodecTest, Trailers) {
2189   HTTPMessage req = getGetRequest("/guacamole");
2190   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
2191   upstreamCodec_.generateHeader(output_, 1, req);
2192 
2193   string data("abcde");
2194   auto buf = folly::IOBuf::copyBuffer(data.data(), data.length());
2195   upstreamCodec_.generateBody(
2196       output_, 1, std::move(buf), HTTPCodec::NoPadding, false /* eom */);
2197 
2198   HTTPHeaders trailers;
2199   trailers.add("x-trailer-1", "pico-de-gallo");
2200   upstreamCodec_.generateTrailers(output_, 1, trailers);
2201 
2202   parse();
2203 
2204   EXPECT_EQ(callbacks_.messageBegin, 1);
2205   EXPECT_EQ(callbacks_.headersComplete, 1);
2206   EXPECT_EQ(callbacks_.bodyCalls, 1);
2207   EXPECT_EQ(callbacks_.bodyLength, 5);
2208   EXPECT_EQ(callbacks_.trailers, 1);
2209   EXPECT_NE(nullptr, callbacks_.msg->getTrailers());
2210   EXPECT_EQ("pico-de-gallo",
2211             callbacks_.msg->getTrailers()->getSingleOrEmpty("x-trailer-1"));
2212   EXPECT_EQ(callbacks_.messageComplete, 1);
2213   EXPECT_EQ(callbacks_.streamErrors, 0);
2214   EXPECT_EQ(callbacks_.sessionErrors, 0);
2215 #ifndef NDEBUG
2216   EXPECT_EQ(downstreamCodec_.getReceivedFrameCount(), 3);
2217 #endif
2218 }
2219 
TEST_F(HTTP2CodecTest,TrailersWithPseudoHeaders)2220 TEST_F(HTTP2CodecTest, TrailersWithPseudoHeaders) {
2221   HTTPMessage req = getGetRequest("/guacamole");
2222   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
2223   upstreamCodec_.generateHeader(output_, 1, req);
2224 
2225   string data("abcde");
2226   auto buf = folly::IOBuf::copyBuffer(data.data(), data.length());
2227   upstreamCodec_.generateBody(
2228       output_, 1, std::move(buf), HTTPCodec::NoPadding, false /* eom */);
2229 
2230   HPACKCodec headerCodec(TransportDirection::UPSTREAM);
2231   std::string post("POST");
2232   std::vector<proxygen::compress::Header> trailers = {
2233       Header::makeHeaderForTest(headers::kMethod, post)};
2234   auto encodedTrailers = headerCodec.encode(trailers);
2235   writeHeaders(output_,
2236                std::move(encodedTrailers),
2237                1,
2238                folly::none,
2239                http2::kNoPadding,
2240                true,
2241                true);
2242 
2243   parse();
2244 
2245   EXPECT_EQ(callbacks_.messageBegin, 1);
2246   EXPECT_EQ(callbacks_.headersComplete, 1);
2247   EXPECT_EQ(callbacks_.bodyCalls, 1);
2248   EXPECT_EQ(callbacks_.bodyLength, 5);
2249   EXPECT_EQ(callbacks_.trailers, 0);
2250   EXPECT_EQ(nullptr, callbacks_.msg->getTrailers());
2251   EXPECT_EQ(callbacks_.messageComplete, 0);
2252   EXPECT_EQ(callbacks_.streamErrors, 1);
2253 }
2254 
TEST_F(HTTP2CodecTest,TrailersNoBody)2255 TEST_F(HTTP2CodecTest, TrailersNoBody) {
2256   HTTPMessage req = getGetRequest("/guacamole");
2257   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
2258   upstreamCodec_.generateHeader(output_, 1, req);
2259 
2260   HTTPHeaders trailers;
2261   trailers.add("x-trailer-1", "pico-de-gallo");
2262   upstreamCodec_.generateTrailers(output_, 1, trailers);
2263 
2264   parse();
2265 
2266   EXPECT_EQ(callbacks_.messageBegin, 1);
2267   EXPECT_EQ(callbacks_.headersComplete, 1);
2268   EXPECT_EQ(callbacks_.bodyCalls, 0);
2269   EXPECT_EQ(callbacks_.bodyLength, 0);
2270   EXPECT_EQ(callbacks_.trailers, 1);
2271   EXPECT_NE(nullptr, callbacks_.msg->getTrailers());
2272   EXPECT_EQ("pico-de-gallo",
2273             callbacks_.msg->getTrailers()->getSingleOrEmpty("x-trailer-1"));
2274   EXPECT_EQ(callbacks_.messageComplete, 1);
2275   EXPECT_EQ(callbacks_.streamErrors, 0);
2276   EXPECT_EQ(callbacks_.sessionErrors, 0);
2277 #ifndef NDEBUG
2278   EXPECT_EQ(downstreamCodec_.getReceivedFrameCount(), 2);
2279 #endif
2280 }
2281 
TEST_F(HTTP2CodecTest,TrailersContinuation)2282 TEST_F(HTTP2CodecTest, TrailersContinuation) {
2283   HTTPMessage req = getGetRequest("/guacamole");
2284   upstreamCodec_.generateHeader(output_, 1, req);
2285 
2286   HTTPHeaders trailers;
2287   trailers.add("x-trailer-1", "pico-de-gallo");
2288   trailers.add("x-huge-trailer",
2289                std::string(http2::kMaxFramePayloadLengthMin, '!'));
2290   upstreamCodec_.generateTrailers(output_, 1, trailers);
2291 
2292   parse();
2293 
2294   EXPECT_EQ(callbacks_.messageBegin, 1);
2295   EXPECT_EQ(callbacks_.headersComplete, 1);
2296   EXPECT_EQ(callbacks_.messageComplete, 1);
2297   EXPECT_EQ(callbacks_.streamErrors, 0);
2298   EXPECT_EQ(callbacks_.sessionErrors, 0);
2299   EXPECT_NE(callbacks_.msg, nullptr);
2300   EXPECT_EQ(callbacks_.trailers, 1);
2301   EXPECT_NE(callbacks_.msg->getTrailers(), nullptr);
2302   EXPECT_EQ("pico-de-gallo",
2303             callbacks_.msg->getTrailers()->getSingleOrEmpty("x-trailer-1"));
2304   EXPECT_EQ(std::string(http2::kMaxFramePayloadLengthMin, '!'),
2305             callbacks_.msg->getTrailers()->getSingleOrEmpty("x-huge-trailer"));
2306 #ifndef NDEBUG
2307   EXPECT_EQ(downstreamCodec_.getReceivedFrameCount(), 3);
2308 #endif
2309 }
2310 
TEST_F(HTTP2CodecTest,TrailersReply)2311 TEST_F(HTTP2CodecTest, TrailersReply) {
2312   SetUpUpstreamTest();
2313   HTTPMessage resp;
2314   resp.setStatusCode(200);
2315   resp.setStatusMessage("nifty-nice");
2316   resp.getHeaders().add(HTTP_HEADER_CONTENT_TYPE, "x-coolio");
2317   downstreamCodec_.generateHeader(output_, 1, resp);
2318 
2319   string data("abcde");
2320   auto buf = folly::IOBuf::copyBuffer(data.data(), data.length());
2321   downstreamCodec_.generateBody(
2322       output_, 1, std::move(buf), HTTPCodec::NoPadding, false);
2323 
2324   HTTPHeaders trailers;
2325   trailers.add("x-trailer-1", "pico-de-gallo");
2326   trailers.add("x-trailer-2", "chicken-kyiv");
2327   downstreamCodec_.generateTrailers(output_, 1, trailers);
2328 
2329   parseUpstream();
2330 
2331   callbacks_.expectMessage(true, 2, 200);
2332   EXPECT_EQ(callbacks_.bodyCalls, 1);
2333   EXPECT_EQ(callbacks_.bodyLength, 5);
2334   const auto& headers = callbacks_.msg->getHeaders();
2335   EXPECT_TRUE(callbacks_.msg->getHeaders().exists(HTTP_HEADER_DATE));
2336   EXPECT_EQ("x-coolio", headers.getSingleOrEmpty(HTTP_HEADER_CONTENT_TYPE));
2337   EXPECT_EQ(1, callbacks_.trailers);
2338   EXPECT_NE(nullptr, callbacks_.msg->getTrailers());
2339   EXPECT_EQ("pico-de-gallo",
2340             callbacks_.msg->getTrailers()->getSingleOrEmpty("x-trailer-1"));
2341   EXPECT_EQ("chicken-kyiv",
2342             callbacks_.msg->getTrailers()->getSingleOrEmpty("x-trailer-2"));
2343 #ifndef NDEBUG
2344   EXPECT_EQ(upstreamCodec_.getReceivedFrameCount(), 4);
2345 #endif
2346 }
2347 
TEST_F(HTTP2CodecTest,TrailersReplyWithNoData)2348 TEST_F(HTTP2CodecTest, TrailersReplyWithNoData) {
2349   SetUpUpstreamTest();
2350   HTTPMessage resp;
2351   resp.setStatusCode(200);
2352   resp.setStatusMessage("nifty-nice");
2353   resp.getHeaders().add(HTTP_HEADER_CONTENT_TYPE, "x-coolio");
2354   downstreamCodec_.generateHeader(output_, 1, resp);
2355 
2356   HTTPHeaders trailers;
2357   trailers.add("x-trailer-1", "pico-de-gallo");
2358   downstreamCodec_.generateTrailers(output_, 1, trailers);
2359 
2360   parseUpstream();
2361 
2362   callbacks_.expectMessage(true, 2, 200);
2363   EXPECT_EQ(callbacks_.bodyCalls, 0);
2364   EXPECT_EQ(callbacks_.bodyLength, 0);
2365   const auto& headers = callbacks_.msg->getHeaders();
2366   EXPECT_TRUE(callbacks_.msg->getHeaders().exists(HTTP_HEADER_DATE));
2367   EXPECT_EQ("x-coolio", headers.getSingleOrEmpty(HTTP_HEADER_CONTENT_TYPE));
2368   EXPECT_EQ(1, callbacks_.trailers);
2369   EXPECT_NE(nullptr, callbacks_.msg->getTrailers());
2370   EXPECT_EQ("pico-de-gallo",
2371             callbacks_.msg->getTrailers()->getSingleOrEmpty("x-trailer-1"));
2372 #ifndef NDEBUG
2373   EXPECT_EQ(upstreamCodec_.getReceivedFrameCount(), 3);
2374 #endif
2375 }
2376 
TEST_F(HTTP2CodecTest,TrailersReplyWithPseudoHeaders)2377 TEST_F(HTTP2CodecTest, TrailersReplyWithPseudoHeaders) {
2378   SetUpUpstreamTest();
2379   HTTPMessage resp;
2380   resp.setStatusCode(200);
2381   resp.setStatusMessage("nifty-nice");
2382   resp.getHeaders().add(HTTP_HEADER_CONTENT_TYPE, "x-coolio");
2383   downstreamCodec_.generateHeader(output_, 1, resp);
2384 
2385   string data("abcde");
2386   auto buf = folly::IOBuf::copyBuffer(data.data(), data.length());
2387   downstreamCodec_.generateBody(
2388       output_, 1, std::move(buf), HTTPCodec::NoPadding, false);
2389 
2390   HPACKCodec headerCodec(TransportDirection::DOWNSTREAM);
2391   std::string post("POST");
2392   std::vector<proxygen::compress::Header> trailers = {
2393       Header::makeHeaderForTest(headers::kMethod, post)};
2394   auto encodedTrailers = headerCodec.encode(trailers);
2395   writeHeaders(output_,
2396                std::move(encodedTrailers),
2397                1,
2398                folly::none,
2399                http2::kNoPadding,
2400                true,
2401                true);
2402   parseUpstream();
2403 
2404   // Unfortunately, you get 2x messageBegin calls for parse error in
2405   // upstream trailers
2406   EXPECT_EQ(callbacks_.messageBegin, 2);
2407   EXPECT_EQ(callbacks_.headersComplete, 1);
2408   EXPECT_EQ(callbacks_.trailers, 0);
2409   EXPECT_EQ(nullptr, callbacks_.msg->getTrailers());
2410   EXPECT_EQ(callbacks_.streamErrors, 1);
2411   EXPECT_EQ(callbacks_.sessionErrors, 0);
2412 }
2413 
TEST_F(HTTP2CodecTest,TrailersReplyContinuation)2414 TEST_F(HTTP2CodecTest, TrailersReplyContinuation) {
2415   SetUpUpstreamTest();
2416   HTTPMessage resp;
2417   resp.setStatusCode(200);
2418   downstreamCodec_.generateHeader(output_, 1, resp);
2419 
2420   HTTPHeaders trailers;
2421   trailers.add("x-trailer-1", "pico-de-gallo");
2422   trailers.add("x-huge-trailer",
2423                std::string(http2::kMaxFramePayloadLengthMin, '!'));
2424   downstreamCodec_.generateTrailers(output_, 1, trailers);
2425 
2426   parseUpstream();
2427 
2428   EXPECT_EQ(callbacks_.messageBegin, 1);
2429   EXPECT_EQ(callbacks_.headersComplete, 1);
2430   EXPECT_EQ(callbacks_.messageComplete, 1);
2431   EXPECT_EQ(callbacks_.streamErrors, 0);
2432   EXPECT_EQ(callbacks_.sessionErrors, 0);
2433   EXPECT_NE(callbacks_.msg, nullptr);
2434   EXPECT_EQ(callbacks_.msg->getStatusCode(), 200);
2435   EXPECT_EQ(1, callbacks_.trailers);
2436   EXPECT_NE(nullptr, callbacks_.msg->getTrailers());
2437   EXPECT_EQ("pico-de-gallo",
2438             callbacks_.msg->getTrailers()->getSingleOrEmpty("x-trailer-1"));
2439   EXPECT_EQ(std::string(http2::kMaxFramePayloadLengthMin, '!'),
2440             callbacks_.msg->getTrailers()->getSingleOrEmpty("x-huge-trailer"));
2441 #ifndef NDEBUG
2442   EXPECT_EQ(upstreamCodec_.getReceivedFrameCount(), 4);
2443 #endif
2444 }
2445 
TEST_F(HTTP2CodecTest,TrailersReplyMissingContinuation)2446 TEST_F(HTTP2CodecTest, TrailersReplyMissingContinuation) {
2447   SetUpUpstreamTest();
2448   HTTPMessage resp;
2449   resp.setStatusCode(200);
2450   downstreamCodec_.generateHeader(output_, 1, resp);
2451 
2452   HTTPHeaders trailers;
2453   trailers.add("x-trailer-1", "pico-de-gallo");
2454   trailers.add("x-huge-trailer",
2455                std::string(http2::kMaxFramePayloadLengthMin, '!'));
2456   downstreamCodec_.generateTrailers(output_, 1, trailers);
2457   // empirically determined the size of continuation frame, and strip it
2458   output_.trimEnd(http2::kFrameHeaderSize + 4132);
2459 
2460   // insert a non-continuation (but otherwise valid) frame
2461   http2::writeGoaway(output_, 17, ErrorCode::ENHANCE_YOUR_CALM);
2462 
2463   parseUpstream();
2464 
2465   EXPECT_EQ(callbacks_.messageBegin, 1);
2466   EXPECT_EQ(callbacks_.headersComplete, 1);
2467   EXPECT_EQ(callbacks_.messageComplete, 0);
2468   EXPECT_EQ(callbacks_.streamErrors, 0);
2469   EXPECT_EQ(callbacks_.sessionErrors, 1);
2470 #ifndef NDEBUG
2471   EXPECT_EQ(upstreamCodec_.getReceivedFrameCount(), 4);
2472 #endif
2473 }
2474 
TEST_F(HTTP2CodecTest,TrailersNotLatest)2475 TEST_F(HTTP2CodecTest, TrailersNotLatest) {
2476   HTTPMessage req = getGetRequest("/guacamole");
2477   req.getHeaders().add(HTTP_HEADER_USER_AGENT, "coolio");
2478   upstreamCodec_.generateHeader(output_, 1, req);
2479   upstreamCodec_.generateHeader(output_, 3, req);
2480 
2481   HTTPHeaders trailers;
2482   trailers.add("x-trailer-1", "pico-de-gallo");
2483   upstreamCodec_.generateTrailers(output_, 1, trailers);
2484   upstreamCodec_.generateHeader(output_, 3, req);
2485 
2486   parse();
2487 
2488   EXPECT_EQ(callbacks_.messageBegin, 2);
2489   EXPECT_EQ(callbacks_.headersComplete, 2);
2490   EXPECT_EQ(callbacks_.bodyCalls, 0);
2491   EXPECT_EQ(callbacks_.trailers, 1);
2492   EXPECT_NE(nullptr, callbacks_.msg->getTrailers());
2493   EXPECT_EQ("pico-de-gallo",
2494             callbacks_.msg->getTrailers()->getSingleOrEmpty("x-trailer-1"));
2495   EXPECT_EQ(callbacks_.messageComplete, 1);
2496   EXPECT_EQ(callbacks_.streamErrors, 1);
2497   EXPECT_EQ(callbacks_.sessionErrors, 0);
2498 }
2499 
2500 class HTTP2CodecDoubleGoawayTest : public HTTPParallelCodecTest {
2501  public:
HTTP2CodecDoubleGoawayTest()2502   HTTP2CodecDoubleGoawayTest()
2503       : HTTPParallelCodecTest(upstreamCodec_, downstreamCodec_) {
2504   }
2505 
SetUp()2506   void SetUp() override {
2507     HTTPParallelCodecTest::SetUp();
2508   }
2509 
2510  protected:
2511   HTTP2Codec upstreamCodec_{TransportDirection::UPSTREAM};
2512   HTTP2Codec downstreamCodec_{TransportDirection::DOWNSTREAM};
2513 };
2514 
TEST_F(HTTP2CodecDoubleGoawayTest,EnableDoubleGoawayAfterClose)2515 TEST_F(HTTP2CodecDoubleGoawayTest, EnableDoubleGoawayAfterClose) {
2516   SetUpUpstreamTest();
2517   upstreamCodec_.generateGoaway(output_);
2518   // Now a no-op
2519   upstreamCodec_.enableDoubleGoawayDrain();
2520   parseUpstream();
2521   EXPECT_EQ(callbacks_.goaways, 1);
2522   EXPECT_EQ(callbacks_.streamErrors, 0);
2523   EXPECT_EQ(callbacks_.sessionErrors, 0);
2524 }
2525