Re-sync with internal repository
[hiphop-php.git] / third-party / proxygen / src / proxygen / lib / http / codec / test / HTTPBinaryCodecTest.cpp
blobb6f782c977281aa90cc0daa09d7d5365669ec8b1
1 /*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
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 */
9 #include <proxygen/lib/http/codec/HTTPBinaryCodec.h>
10 #include <quic/codec/QuicInteger.h>
12 #include <folly/String.h>
13 #include <folly/portability/GTest.h>
15 namespace proxygen::test {
17 class HTTPBinaryCodecForTest : public HTTPBinaryCodec {
18 public:
19 explicit HTTPBinaryCodecForTest(TransportDirection direction)
20 : HTTPBinaryCodec{direction} {
22 ParseResult parseFramingIndicator(folly::io::Cursor& cursor,
23 bool& request,
24 bool& knownLength) {
25 return HTTPBinaryCodec::parseFramingIndicator(cursor, request, knownLength);
28 ParseResult parseRequestControlData(folly::io::Cursor& cursor,
29 size_t remaining,
30 HTTPMessage& msg) {
31 return HTTPBinaryCodec::parseRequestControlData(cursor, remaining, msg);
34 ParseResult parseResponseControlData(folly::io::Cursor& cursor,
35 size_t remaining,
36 HTTPMessage& msg) {
37 return HTTPBinaryCodec::parseResponseControlData(cursor, remaining, msg);
40 ParseResult parseHeaders(folly::io::Cursor& cursor,
41 size_t remaining,
42 HeaderDecodeInfo& decodeInfo) {
43 return HTTPBinaryCodec::parseHeaders(cursor, remaining, decodeInfo);
46 ParseResult parseContent(folly::io::Cursor& cursor,
47 size_t remaining,
48 HTTPMessage& msg) {
49 return HTTPBinaryCodec::parseContent(cursor, remaining, msg);
52 folly::IOBuf& getMsgBody() {
53 return *msgBody_;
57 class HTTPBinaryCodecCallback : public HTTPCodec::Callback {
58 public:
59 HTTPBinaryCodecCallback() = default;
61 void onMessageBegin(HTTPCodec::StreamID /*stream*/,
62 HTTPMessage* /*msg*/) override {
64 void onPushMessageBegin(HTTPCodec::StreamID /*stream*/,
65 HTTPCodec::StreamID /*assocStream*/,
66 HTTPMessage* /*msg*/) override {
68 void onHeadersComplete(HTTPCodec::StreamID /*stream*/,
69 std::unique_ptr<HTTPMessage> msg) override {
70 msg_ = std::move(msg);
72 void onBody(HTTPCodec::StreamID /*stream*/,
73 std::unique_ptr<folly::IOBuf> chain,
74 uint16_t /*padding*/) override {
75 if (chain) {
76 body_ = chain->clone();
79 void onChunkHeader(HTTPCodec::StreamID /*stream*/,
80 size_t /*length*/) override {
82 void onChunkComplete(HTTPCodec::StreamID /*stream*/) override {
84 void onTrailersComplete(HTTPCodec::StreamID /*stream*/,
85 std::unique_ptr<HTTPHeaders> trailers) override {
86 trailers_ = std::move(trailers);
88 void onMessageComplete(HTTPCodec::StreamID /*stream*/,
89 bool /*upgrade*/) override {
91 void onError(HTTPCodec::StreamID /*stream*/,
92 const HTTPException& error,
93 bool /*newTxn*/) override {
94 error_ = std::make_unique<HTTPException>(error);
97 std::unique_ptr<HTTPMessage> msg_;
98 std::unique_ptr<folly::IOBuf> body_;
99 std::unique_ptr<HTTPHeaders> trailers_;
100 std::unique_ptr<HTTPException> error_;
103 class HTTPBinaryCodecTest : public ::testing::Test {
104 protected:
105 void SetUp() override {
106 downstreamBinaryCodec_ = std::make_unique<HTTPBinaryCodecForTest>(
107 TransportDirection::DOWNSTREAM);
110 void TearDown() override {
113 std::unique_ptr<HTTPBinaryCodecForTest> downstreamBinaryCodec_;
116 TEST_F(HTTPBinaryCodecTest, testParseFramingIndicatorSuccess) {
117 // Test Known Length Request
118 const std::vector<uint8_t> framingIndicatorKnownRequest{0x00};
119 auto framingIndicatorIOBuf = folly::IOBuf::wrapBuffer(
120 folly::ByteRange(framingIndicatorKnownRequest.data(),
121 framingIndicatorKnownRequest.size()));
122 folly::io::Cursor cursor(framingIndicatorIOBuf.get());
124 bool request = false;
125 bool knownLength = false;
126 EXPECT_EQ(downstreamBinaryCodec_
127 ->parseFramingIndicator(cursor, request, knownLength)
128 .value(),
130 EXPECT_EQ(request, true);
131 EXPECT_EQ(knownLength, true);
133 // Test Indeterminate Length Response
134 const std::vector<uint8_t> framingIndicatorIndeterminateResponse{0x03};
135 framingIndicatorIOBuf = folly::IOBuf::wrapBuffer(
136 folly::ByteRange(framingIndicatorIndeterminateResponse.data(),
137 framingIndicatorIndeterminateResponse.size()));
138 cursor = folly::io::Cursor(framingIndicatorIOBuf.get());
140 EXPECT_EQ(downstreamBinaryCodec_
141 ->parseFramingIndicator(cursor, request, knownLength)
142 .error(),
143 "Unsupported indeterminate length Binary HTTP Request");
144 EXPECT_EQ(request, false);
145 EXPECT_EQ(knownLength, false);
148 TEST_F(HTTPBinaryCodecTest, testParseFramingIndicatorFailure) {
149 // Test Invalid Framing Indicator
150 const std::vector<uint8_t> framingIndicatorInvalidResponse{0x04};
151 auto framingIndicatorIOBuf = folly::IOBuf::wrapBuffer(
152 folly::ByteRange(framingIndicatorInvalidResponse.data(),
153 framingIndicatorInvalidResponse.size()));
154 folly::io::Cursor cursor(framingIndicatorIOBuf.get());
156 bool request = false;
157 bool knownLength = false;
158 EXPECT_EQ(downstreamBinaryCodec_
159 ->parseFramingIndicator(cursor, request, knownLength)
160 .error(),
161 "Invalid Framing Indicator: 4");
164 TEST_F(HTTPBinaryCodecTest, testParseRequestControlDataSuccess) {
165 // Format is `.GET.https.www.example.com./hello.txt` where `.` represents the
166 // length of each subsequent string
167 const std::vector<uint8_t> controlDataRequest{
168 0x03, 0x47, 0x45, 0x54, 0x05, 0x68, 0x74, 0x74, 0x70, 0x73,
169 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70,
170 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x0a, 0x2f, 0x68, 0x65,
171 0x6c, 0x6c, 0x6f, 0x2e, 0x74, 0x78, 0x74};
172 auto controlDataIOBuf = folly::IOBuf::wrapBuffer(
173 folly::ByteRange(controlDataRequest.data(), controlDataRequest.size()));
174 folly::io::Cursor cursor(controlDataIOBuf.get());
176 HTTPMessage msg;
177 EXPECT_EQ(
178 downstreamBinaryCodec_
179 ->parseRequestControlData(cursor, controlDataRequest.size(), msg)
180 .value(),
181 controlDataRequest.size());
182 EXPECT_EQ(msg.isSecure(), true);
183 EXPECT_EQ(msg.getMethod(), proxygen::HTTPMethod::GET);
184 EXPECT_EQ(msg.getURL(), "/hello.txt");
187 TEST_F(HTTPBinaryCodecTest, testParseRequestControlDataFailure) {
188 // Format is `.GET.https.www.example.com./hello.txt` where `.` before
189 // /hello.txt is value 11 instead of value 10, which should cause the parsing
190 // to error
191 const std::vector<uint8_t> controlDataInvalidRequest{
192 0x03, 0x47, 0x45, 0x54, 0x05, 0x68, 0x74, 0x74, 0x70, 0x73,
193 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70,
194 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x0b, 0x2f, 0x68, 0x65,
195 0x6c, 0x6c, 0x6f, 0x2e, 0x74, 0x78, 0x74};
196 auto controlDataIOBuf = folly::IOBuf::wrapBuffer(folly::ByteRange(
197 controlDataInvalidRequest.data(), controlDataInvalidRequest.size()));
198 folly::io::Cursor cursor(controlDataIOBuf.get());
200 HTTPMessage msg;
201 EXPECT_EQ(downstreamBinaryCodec_
202 ->parseRequestControlData(
203 cursor, controlDataInvalidRequest.size(), msg)
204 .error(),
205 "Failure to parse: path");
207 // Format is `.GET.httpt.www.example.com./hello.txt` where `.` represents the
208 // length of each subsequent string.
209 const std::vector<uint8_t> controlDataInvalidScheme{
210 0x03, 0x47, 0x45, 0x54, 0x05, 0x68, 0x74, 0x74, 0x70, 0x74,
211 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70,
212 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x0a, 0x2f, 0x68, 0x65,
213 0x6c, 0x6c, 0x6f, 0x2e, 0x74, 0x78, 0x74};
214 controlDataIOBuf = folly::IOBuf::wrapBuffer(folly::ByteRange(
215 controlDataInvalidScheme.data(), controlDataInvalidScheme.size()));
216 cursor = folly::io::Cursor(controlDataIOBuf.get());
218 EXPECT_EQ(downstreamBinaryCodec_
219 ->parseRequestControlData(
220 cursor, controlDataInvalidScheme.size(), msg)
221 .error(),
222 "Failure to parse: scheme. Should be 'http' or 'https'");
224 // Format is `.GET.https.www.example.com.hello.tx[\x1]` where `.` represents
225 // the length of each subsequent string.
226 const std::vector<uint8_t> controlDataInvalidPath{
227 0x03, 0x47, 0x45, 0x54, 0x05, 0x68, 0x74, 0x74, 0x70, 0x73, 0x0f, 0x77,
228 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63,
229 0x6f, 0x6d, 0x09, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x74, 0x78, 0x01};
230 controlDataIOBuf = folly::IOBuf::wrapBuffer(folly::ByteRange(
231 controlDataInvalidPath.data(), controlDataInvalidPath.size()));
232 cursor = folly::io::Cursor(controlDataIOBuf.get());
234 EXPECT_EQ(
235 downstreamBinaryCodec_
236 ->parseRequestControlData(cursor, controlDataInvalidPath.size(), msg)
237 .error(),
238 "Failure to parse: invalid URL path 'hello.tx\x1'");
241 TEST_F(HTTPBinaryCodecTest, testParseResponseControlDataSuccess) {
242 // Reponse Code 200 OK
243 folly::IOBufQueue controlDataIOBuf;
244 folly::io::QueueAppender appender(&controlDataIOBuf, 0);
245 auto parsedBytes = quic::encodeQuicInteger(200, [&appender](auto val) {
246 appender.writeBE(val);
247 }).value();
248 folly::io::Cursor cursor(controlDataIOBuf.front());
250 HTTPMessage msg;
251 EXPECT_EQ(
252 downstreamBinaryCodec_->parseResponseControlData(cursor, parsedBytes, msg)
253 .value(),
254 parsedBytes);
255 EXPECT_EQ(msg.getStatusCode(), 200);
258 TEST_F(HTTPBinaryCodecTest, testParseResponseControlDataFailure) {
259 // Invalid Status Code
260 folly::IOBufQueue controlInvalidDataIOBuf;
261 folly::io::QueueAppender appender(&controlInvalidDataIOBuf, 0);
262 auto parsedBytes = quic::encodeQuicInteger(600, [&appender](auto val) {
263 appender.writeBE(val);
264 }).value();
265 folly::io::Cursor cursor(controlInvalidDataIOBuf.front());
267 HTTPMessage msg;
268 EXPECT_EQ(
269 downstreamBinaryCodec_->parseResponseControlData(cursor, parsedBytes, msg)
270 .error(),
271 "Invalid response status code: 600");
273 folly::IOBufQueue controlInvalidDataInformationalResponseIOBuf;
274 appender = folly::io::QueueAppender(
275 &controlInvalidDataInformationalResponseIOBuf, 0);
276 parsedBytes = quic::encodeQuicInteger(101, [&appender](auto val) {
277 appender.writeBE(val);
278 }).value();
279 cursor =
280 folly::io::Cursor(controlInvalidDataInformationalResponseIOBuf.front());
282 EXPECT_EQ(
283 downstreamBinaryCodec_->parseResponseControlData(cursor, parsedBytes, msg)
284 .error(),
285 "Invalid response status code: 101");
288 TEST_F(HTTPBinaryCodecTest, testParseHeadersSuccess) {
289 // Format is `..user-agent.curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l
290 // zlib/1.2.3.host.www.example.com.accept-language.en, mi` where `.`
291 // represents the length of the each subsequent string. The first `.` is
292 // actually a Quic Integer that takes 2 bytes (and encodes the length of the
293 // overall header as 108)
294 const std::vector<uint8_t> headers{
295 0x40, 0x6c, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65,
296 0x6e, 0x74, 0x34, 0x63, 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x31,
297 0x36, 0x2e, 0x33, 0x20, 0x6c, 0x69, 0x62, 0x63, 0x75, 0x72, 0x6c,
298 0x2f, 0x37, 0x2e, 0x31, 0x36, 0x2e, 0x33, 0x20, 0x4f, 0x70, 0x65,
299 0x6e, 0x53, 0x53, 0x4c, 0x2f, 0x30, 0x2e, 0x39, 0x2e, 0x37, 0x6c,
300 0x20, 0x7a, 0x6c, 0x69, 0x62, 0x2f, 0x31, 0x2e, 0x32, 0x2e, 0x33,
301 0x04, 0x68, 0x6f, 0x73, 0x74, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65,
302 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x0f,
303 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67,
304 0x75, 0x61, 0x67, 0x65, 0x06, 0x65, 0x6e, 0x2c, 0x20, 0x6d, 0x69};
305 auto headersIOBuf = folly::IOBuf::wrapBuffer(
306 folly::ByteRange(headers.data(), headers.size()));
307 folly::io::Cursor cursor(headersIOBuf.get());
309 HeaderDecodeInfo decodeInfo;
310 decodeInfo.init(true /* request */,
311 false /* isRequestTrailers */,
312 true /* validate */,
313 false /* strictValidation */,
314 false /* allowEmptyPath */);
315 EXPECT_EQ(
316 downstreamBinaryCodec_->parseHeaders(cursor, headers.size(), decodeInfo)
317 .value(),
318 headers.size());
320 HTTPHeaders httpHeaders = decodeInfo.msg->getHeaders();
321 EXPECT_EQ(httpHeaders.exists("user-agent"), true);
322 EXPECT_EQ(httpHeaders.exists("host"), true);
323 EXPECT_EQ(httpHeaders.exists("accept-language"), true);
324 EXPECT_EQ(httpHeaders.getSingleOrEmpty("user-agent"),
325 "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3");
326 EXPECT_EQ(httpHeaders.getSingleOrEmpty("host"), "www.example.com");
327 EXPECT_EQ(httpHeaders.getSingleOrEmpty("accept-language"), "en, mi");
330 TEST_F(HTTPBinaryCodecTest, testParseHeadersFailure) {
331 // Number of headers should be >= 1
332 const std::vector<uint8_t> invalidHeadersCount{0x00};
333 auto headersIOBuf = folly::IOBuf::wrapBuffer(
334 folly::ByteRange(invalidHeadersCount.data(), invalidHeadersCount.size()));
335 folly::io::Cursor cursor(headersIOBuf.get());
337 HeaderDecodeInfo decodeInfo;
338 decodeInfo.init(true /* request */,
339 false /* isRequestTrailers */,
340 true /* validate */,
341 false /* strictValidation */,
342 false /* allowEmptyPath */);
343 EXPECT_EQ(
344 downstreamBinaryCodec_
345 ->parseHeaders(cursor, invalidHeadersCount.size(), decodeInfo)
346 .error(),
347 "Number of headers (key value pairs) should be >= 1. Header count is 0");
349 // Format is `..user-agent.curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l
350 // zlib/1.2.3.host.www.example.com.accept-language.en, mi` where the `.` after
351 // accept-language is value 7 instead of 6 which should cause parsing to fail
352 const std::vector<uint8_t> invalidHeadersLength{
353 0x40, 0x6c, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65,
354 0x6e, 0x74, 0x34, 0x63, 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x31,
355 0x36, 0x2e, 0x33, 0x20, 0x6c, 0x69, 0x62, 0x63, 0x75, 0x72, 0x6c,
356 0x2f, 0x37, 0x2e, 0x31, 0x36, 0x2e, 0x33, 0x20, 0x4f, 0x70, 0x65,
357 0x6e, 0x53, 0x53, 0x4c, 0x2f, 0x30, 0x2e, 0x39, 0x2e, 0x37, 0x6c,
358 0x20, 0x7a, 0x6c, 0x69, 0x62, 0x2f, 0x31, 0x2e, 0x32, 0x2e, 0x33,
359 0x04, 0x68, 0x6f, 0x73, 0x74, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65,
360 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x0f,
361 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67,
362 0x75, 0x61, 0x67, 0x65, 0x07, 0x65, 0x6e, 0x2c, 0x20, 0x6d, 0x69};
363 headersIOBuf = folly::IOBuf::wrapBuffer(folly::ByteRange(
364 invalidHeadersLength.data(), invalidHeadersLength.size()));
365 cursor = folly::io::Cursor(headersIOBuf.get());
367 EXPECT_EQ(downstreamBinaryCodec_
368 ->parseHeaders(cursor, invalidHeadersLength.size(), decodeInfo)
369 .error(),
370 "Failure to parse: headerValue");
372 // Format is `..a.b` where the first `.` represents a too long length
373 const std::vector<uint8_t> invalidHeadersUnderflow{
374 0x09, 0x01, 0x61, 0x01, 0x62};
375 headersIOBuf = folly::IOBuf::wrapBuffer(folly::ByteRange(
376 invalidHeadersUnderflow.data(), invalidHeadersUnderflow.size()));
377 cursor = folly::io::Cursor(headersIOBuf.get());
379 EXPECT_EQ(
380 downstreamBinaryCodec_
381 ->parseHeaders(cursor, invalidHeadersUnderflow.size(), decodeInfo)
382 .error(),
383 "Header parsing underflowed! Headers length in bytes (9) is "
384 "inconsistent with remaining buffer length (4)");
386 // Format is `.` where the first `.` represents an underflowed quic integer
387 const std::vector<uint8_t> invalidHeadersUnderflowQuic{0x99};
388 headersIOBuf = folly::IOBuf::wrapBuffer(folly::ByteRange(
389 invalidHeadersUnderflowQuic.data(), invalidHeadersUnderflowQuic.size()));
390 cursor = folly::io::Cursor(headersIOBuf.get());
392 EXPECT_EQ(
393 downstreamBinaryCodec_
394 ->parseHeaders(cursor, invalidHeadersUnderflowQuic.size(), decodeInfo)
395 .error(),
396 "Failure to parse number of headers");
399 TEST_F(HTTPBinaryCodecTest, testParseContentSuccess) {
400 // Format is `.hello`
401 const std::vector<uint8_t> content{
402 0x07, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0d, 0x0a};
403 auto contentIOBuf = folly::IOBuf::wrapBuffer(
404 folly::ByteRange(content.data(), content.size()));
405 folly::io::Cursor cursor(contentIOBuf.get());
407 HTTPMessage msg;
408 EXPECT_EQ(
409 downstreamBinaryCodec_->parseContent(cursor, content.size(), msg).value(),
410 content.size());
411 EXPECT_EQ(downstreamBinaryCodec_->getMsgBody().moveToFbString().toStdString(),
412 "hello\r\n");
415 TEST_F(HTTPBinaryCodecTest, testParseContentFailure) {
416 // Format is `.hello` where . is value 8 instead of 7
417 const std::vector<uint8_t> contentInvalid{
418 0x08, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0d, 0x0a};
419 auto contentIOBuf = folly::IOBuf::wrapBuffer(
420 folly::ByteRange(contentInvalid.data(), contentInvalid.size()));
421 folly::io::Cursor cursor(contentIOBuf.get());
423 HTTPMessage msg;
424 EXPECT_EQ(
425 downstreamBinaryCodec_->parseContent(cursor, contentInvalid.size(), msg)
426 .error(),
427 "Failure to parse content");
430 TEST_F(HTTPBinaryCodecTest, testOnIngressSuccess) {
431 // Format is `..GET.https.www.example.com./hello.txt..user-agent.curl/7.16.3
432 // libcurl/7.16.3 OpenSSL/0.9.7l
433 // zlib/1.2.3.host.www.example.com.accept-language.en, mi`
434 const std::vector<uint8_t> binaryHTTPMessage{
435 0x00, 0x03, 0x47, 0x45, 0x54, 0x05, 0x68, 0x74, 0x74, 0x70, 0x73, 0x0f,
436 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
437 0x63, 0x6f, 0x6d, 0x0a, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x74,
438 0x78, 0x74, 0x40, 0x6c, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x61, 0x67,
439 0x65, 0x6e, 0x74, 0x34, 0x63, 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x31,
440 0x36, 0x2e, 0x33, 0x20, 0x6c, 0x69, 0x62, 0x63, 0x75, 0x72, 0x6c, 0x2f,
441 0x37, 0x2e, 0x31, 0x36, 0x2e, 0x33, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x53,
442 0x53, 0x4c, 0x2f, 0x30, 0x2e, 0x39, 0x2e, 0x37, 0x6c, 0x20, 0x7a, 0x6c,
443 0x69, 0x62, 0x2f, 0x31, 0x2e, 0x32, 0x2e, 0x33, 0x04, 0x68, 0x6f, 0x73,
444 0x74, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
445 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74,
446 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x06, 0x65, 0x6e,
447 0x2c, 0x20, 0x6d, 0x69, 0x00, 0x00};
448 auto binaryHTTPMessageIOBuf = folly::IOBuf::wrapBuffer(
449 folly::ByteRange(binaryHTTPMessage.data(), binaryHTTPMessage.size()));
450 folly::io::Cursor cursor(binaryHTTPMessageIOBuf.get());
452 HTTPBinaryCodecCallback callback;
453 downstreamBinaryCodec_->setCallback(&callback);
454 downstreamBinaryCodec_->onIngress(*binaryHTTPMessageIOBuf);
455 downstreamBinaryCodec_->onIngressEOF();
457 // Check onError was not called for the callback
458 EXPECT_EQ(callback.error_, nullptr);
460 // Check msg and header fields
461 EXPECT_EQ(callback.msg_->isSecure(), true);
462 EXPECT_EQ(callback.msg_->getMethod(), proxygen::HTTPMethod::GET);
463 EXPECT_EQ(callback.msg_->getURL(), "/hello.txt");
464 HTTPHeaders httpHeaders = callback.msg_->getHeaders();
465 EXPECT_EQ(httpHeaders.exists("user-agent"), true);
466 EXPECT_EQ(httpHeaders.exists("host"), true);
467 EXPECT_EQ(httpHeaders.exists("accept-language"), true);
468 EXPECT_EQ(httpHeaders.getSingleOrEmpty("user-agent"),
469 "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3");
470 EXPECT_EQ(httpHeaders.getSingleOrEmpty("host"), "www.example.com");
471 EXPECT_EQ(httpHeaders.getSingleOrEmpty("accept-language"), "en, mi");
474 TEST_F(HTTPBinaryCodecTest, testOnIngressSuccessForControlData) {
475 // Format is `..GET.https.www.example.com./`
476 const std::vector<uint8_t> binaryHTTPMessage{
477 0x00, 0x03, 0x47, 0x45, 0x54, 0x05, 0x68, 0x74, 0x74,
478 0x70, 0x73, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
479 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x01, 0x2f};
480 auto binaryHTTPMessageIOBuf = folly::IOBuf::wrapBuffer(
481 folly::ByteRange(binaryHTTPMessage.data(), binaryHTTPMessage.size()));
482 folly::io::Cursor cursor(binaryHTTPMessageIOBuf.get());
484 HTTPBinaryCodecCallback callback;
485 downstreamBinaryCodec_->setCallback(&callback);
486 downstreamBinaryCodec_->onIngress(*binaryHTTPMessageIOBuf);
487 downstreamBinaryCodec_->onIngressEOF();
489 // Check onError was not called for the callback
490 EXPECT_EQ(callback.error_, nullptr);
492 // Check msg and header fields
493 EXPECT_EQ(callback.msg_->isSecure(), true);
494 EXPECT_EQ(callback.msg_->getMethod(), proxygen::HTTPMethod::GET);
495 EXPECT_EQ(callback.msg_->getURL(), "/");
498 TEST_F(HTTPBinaryCodecTest, testOnIngressFailure) {
499 // Format is `..GET.https.www.example.com./hello.txt..user-agent.curl/7.16.3
500 // libcurl/7.16.3 OpenSSL/0.9.7l
501 // zlib/1.2.3.host.www.example.com.accept-language.en, mi` where the last `.`
502 // is value 10 instead of 6
503 const std::vector<uint8_t> binaryInvalidHTTPMessage{
504 0x00, 0x03, 0x47, 0x45, 0x54, 0x05, 0x68, 0x74, 0x74, 0x70, 0x73, 0x0f,
505 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
506 0x63, 0x6f, 0x6d, 0x0a, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x74,
507 0x78, 0x74, 0x40, 0x6c, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x61, 0x67,
508 0x65, 0x6e, 0x74, 0x34, 0x63, 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x31,
509 0x36, 0x2e, 0x33, 0x20, 0x6c, 0x69, 0x62, 0x63, 0x75, 0x72, 0x6c, 0x2f,
510 0x37, 0x2e, 0x31, 0x36, 0x2e, 0x33, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x53,
511 0x53, 0x4c, 0x2f, 0x30, 0x2e, 0x39, 0x2e, 0x37, 0x6c, 0x20, 0x7a, 0x6c,
512 0x69, 0x62, 0x2f, 0x31, 0x2e, 0x32, 0x2e, 0x33, 0x04, 0x68, 0x6f, 0x73,
513 0x74, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
514 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74,
515 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x0a, 0x65, 0x6e,
516 0x2c, 0x20, 0x6d, 0x69, 0x00, 0x00};
517 auto binaryHTTPMessageIOBuf = folly::IOBuf::wrapBuffer(folly::ByteRange(
518 binaryInvalidHTTPMessage.data(), binaryInvalidHTTPMessage.size()));
519 folly::io::Cursor cursor(binaryHTTPMessageIOBuf.get());
521 HTTPBinaryCodecCallback callback;
522 downstreamBinaryCodec_->setCallback(&callback);
523 downstreamBinaryCodec_->onIngress(*binaryHTTPMessageIOBuf);
524 downstreamBinaryCodec_->onIngressEOF();
526 // Check onError was called with the correct error
527 EXPECT_EQ(std::string(callback.error_.get()->what()),
528 "Invalid Message: Failure to parse: headerValue");
531 TEST_F(HTTPBinaryCodecTest, testGenerateHeaders) {
532 // Create HTTPMessage and encode it to a buffer
533 HTTPMessage msgEncoded;
534 msgEncoded.setMethod("GET");
535 msgEncoded.setSecure(true);
536 msgEncoded.setURL("/hello.txt");
537 HTTPHeaders& headersEncoded = msgEncoded.getHeaders();
538 headersEncoded.set("user-agent",
539 "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3");
540 headersEncoded.set("host", "www.example.com");
541 headersEncoded.set("accept-language", "en, mi");
543 folly::IOBufQueue writeBuffer;
544 downstreamBinaryCodec_->generateHeader(writeBuffer, 0, msgEncoded);
546 // Now, decode the HTTPMessage from the buffer and check values
547 HTTPBinaryCodecCallback callback;
548 downstreamBinaryCodec_->setCallback(&callback);
549 downstreamBinaryCodec_->onIngress(*writeBuffer.front());
550 downstreamBinaryCodec_->onIngressEOF();
552 EXPECT_EQ(callback.msg_->getMethod(), msgEncoded.getMethod());
553 EXPECT_EQ(callback.msg_->isSecure(), msgEncoded.isSecure());
554 EXPECT_EQ(callback.msg_->getURL(), msgEncoded.getURL());
555 auto headersDecoded = callback.msg_->getHeaders();
556 EXPECT_EQ(headersDecoded.size(), headersEncoded.size());
557 headersEncoded.forEach([&headersDecoded](const std::string& headerName,
558 const std::string& headerValue) {
559 EXPECT_EQ(headersDecoded.exists(headerName), true);
560 EXPECT_EQ(headersDecoded.getSingleOrEmpty(headerName), headerValue);
564 TEST_F(HTTPBinaryCodecTest, testGenerateBody) {
565 // Create Test Body and encode
566 std::string body = "Sample Test Body!";
567 std::unique_ptr<folly::IOBuf> testBody =
568 folly::IOBuf::wrapBuffer(body.data(), body.size());
570 folly::IOBufQueue writeBuffer;
571 downstreamBinaryCodec_->generateBody(writeBuffer, 0, std::move(testBody));
573 // Decode Test Body and check
574 folly::io::Cursor cursor(writeBuffer.front());
575 HTTPMessage msg;
576 EXPECT_EQ(downstreamBinaryCodec_->parseContent(cursor, 18, msg).value(), 18);
577 EXPECT_EQ(downstreamBinaryCodec_->getMsgBody().moveToFbString().toStdString(),
578 "Sample Test Body!");
581 TEST_F(HTTPBinaryCodecTest, testEncodeAndDecodeRequest) {
582 // Create full request encode it to a buffer
583 folly::IOBufQueue writeBuffer;
585 // Encode Framing Indicator, Control Data, and Headers
586 HTTPMessage msgEncoded;
587 msgEncoded.setMethod("POST");
588 msgEncoded.setSecure(false);
589 msgEncoded.setURL("/hello.txt");
590 HTTPHeaders& headersEncoded = msgEncoded.getHeaders();
591 headersEncoded.set("user-agent",
592 "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3");
593 headersEncoded.set("host", "www.example.com");
594 headersEncoded.set("accept-language", "en, mi");
595 downstreamBinaryCodec_->generateHeader(writeBuffer, 0, msgEncoded);
597 // Encode Body
598 std::string body = "Sample Test Body!";
599 std::unique_ptr<folly::IOBuf> testBody =
600 folly::IOBuf::wrapBuffer(body.data(), body.size());
601 downstreamBinaryCodec_->generateBody(writeBuffer, 0, std::move(testBody));
603 // Encode Trailing Headers
604 std::unique_ptr<HTTPHeaders> trailersEncoded =
605 std::make_unique<HTTPHeaders>();
606 trailersEncoded->set("test-trailer", "test-trailer-value");
607 msgEncoded.setTrailers(std::move(trailersEncoded));
608 downstreamBinaryCodec_->generateTrailers(
609 writeBuffer, 0, *msgEncoded.getTrailers());
611 // Now, decode the request and check values
612 HTTPBinaryCodecCallback callback;
613 downstreamBinaryCodec_->setCallback(&callback);
614 downstreamBinaryCodec_->onIngress(*writeBuffer.front());
615 downstreamBinaryCodec_->onIngressEOF();
617 EXPECT_EQ(callback.msg_->getMethod(), msgEncoded.getMethod());
618 EXPECT_EQ(callback.msg_->isSecure(), msgEncoded.isSecure());
619 EXPECT_EQ(callback.msg_->getURL(), msgEncoded.getURL());
620 auto headersDecoded = callback.msg_->getHeaders();
621 EXPECT_EQ(headersDecoded.size(), headersEncoded.size());
622 headersEncoded.forEach([&headersDecoded](const std::string& headerName,
623 const std::string& headerValue) {
624 EXPECT_EQ(headersDecoded.exists(headerName), true);
625 EXPECT_EQ(headersDecoded.getSingleOrEmpty(headerName), headerValue);
628 EXPECT_EQ(callback.body_->moveToFbString().toStdString(),
629 "Sample Test Body!");
631 auto trailersDecoded = *callback.trailers_;
632 EXPECT_EQ(trailersDecoded.size(), 1);
633 EXPECT_EQ(trailersDecoded.exists("test-trailer"), true);
634 EXPECT_EQ(trailersDecoded.getSingleOrEmpty("test-trailer"),
635 "test-trailer-value");
638 } // namespace proxygen::test