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