proxygen
HTTP1xCodecTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-present, Facebook, Inc.
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. An additional grant
7  * of patent rights can be found in the PATENTS file in the same directory.
8  *
9  */
17 
18 using namespace proxygen;
19 using namespace std;
20 using namespace testing;
21 
23  public:
25 
27  HTTPMessage* /*msg*/) override {}
29  HTTPCodec::StreamID /*assocStream*/,
30  HTTPMessage* /*msg*/) override {}
32  std::unique_ptr<HTTPMessage> msg) override {
33  headersComplete++;
35  msg_ = std::move(msg);
36  }
37  void onBody(HTTPCodec::StreamID /*stream*/,
38  std::unique_ptr<folly::IOBuf> chain,
39  uint16_t /*padding*/) override {
40  bodyLen += chain->computeChainDataLength();
41  }
43  size_t /*length*/) override {}
44  void onChunkComplete(HTTPCodec::StreamID /*stream*/) override {}
46  std::unique_ptr<HTTPHeaders> /*trailers*/) override {}
48  bool /*upgrade*/) override {
49  ++messageComplete;
50  }
51  void onError(HTTPCodec::StreamID /*stream*/,
52  const HTTPException& /*error*/,
53  bool /*newTxn*/) override {
54  ++errors;
55  LOG(ERROR) << "parse error";
56  }
57 
58  uint32_t headersComplete{0};
59  uint32_t messageComplete{0};
60  uint32_t errors{0};
61  uint32_t bodyLen{0};
63  std::unique_ptr<HTTPMessage> msg_;
64 };
65 
66 unique_ptr<folly::IOBuf> getSimpleRequestData() {
67  string req("GET /yeah HTTP/1.1\nHost: www.facebook.com\n\n");
68  return folly::IOBuf::copyBuffer(req);
69 }
70 
71 TEST(HTTP1xCodecTest, TestSimpleHeaders) {
73  HTTP1xCodecCallback callbacks;
74  codec.setCallback(&callbacks);
76  codec.onIngress(*buffer);
77  EXPECT_EQ(callbacks.headersComplete, 1);
78  EXPECT_EQ(buffer->length(), callbacks.headerSize.uncompressed);
79  EXPECT_EQ(callbacks.headerSize.compressed, 0);
80 }
81 
82 TEST(HTTP1xCodecTest, Test09Req) {
84  HTTP1xCodecCallback callbacks;
85  codec.setCallback(&callbacks);
86  auto buffer = folly::IOBuf::copyBuffer(string("GET /yeah\r\n"));
87  codec.onIngress(*buffer);
88  EXPECT_EQ(callbacks.headersComplete, 1);
89  EXPECT_EQ(callbacks.messageComplete, 1);
90  EXPECT_EQ(buffer->length(), callbacks.headerSize.uncompressed);
91  EXPECT_EQ(callbacks.headerSize.compressed, 0);
92  buffer = folly::IOBuf::copyBuffer(string("\r\n"));
93  codec.onIngress(*buffer);
94  EXPECT_EQ(callbacks.headersComplete, 1);
95  EXPECT_EQ(callbacks.messageComplete, 1);
96 }
97 
98 TEST(HTTP1xCodecTest, Test09ReqVers) {
100  HTTP1xCodecCallback callbacks;
101  codec.setCallback(&callbacks);
102  auto buffer = folly::IOBuf::copyBuffer(string("GET /yeah HTTP/0.9\r\n"));
103  codec.onIngress(*buffer);
104  EXPECT_EQ(callbacks.headersComplete, 1);
105  EXPECT_EQ(callbacks.messageComplete, 1);
106  EXPECT_EQ(buffer->length(), callbacks.headerSize.uncompressed);
107  EXPECT_EQ(callbacks.headerSize.compressed, 0);
108 }
109 
110 TEST(HTTP1xCodecTest, Test09Resp) {
112  HTTP1xCodecCallback callbacks;
113  HTTPMessage req;
114  auto id = codec.createStream();
115  req.setHTTPVersion(0, 9);
117  req.setURL("/");
118  codec.setCallback(&callbacks);
119  folly::IOBufQueue buf;
120  codec.generateHeader(buf, id, req, true);
122  string("iamtheverymodelofamodernmajorgeneral"));
123  codec.onIngress(*buffer);
124  EXPECT_EQ(callbacks.headersComplete, 1);
125  EXPECT_EQ(callbacks.bodyLen, buffer->computeChainDataLength());
126  EXPECT_EQ(callbacks.messageComplete, 0);
127  codec.onIngressEOF();
128  EXPECT_EQ(callbacks.messageComplete, 1);
129 }
130 
131 TEST(HTTP1xCodecTest, TestBadHeaders) {
133  MockHTTPCodecCallback callbacks;
134  codec.setCallback(&callbacks);
136  string("GET /yeah HTTP/1.1\nUser-Agent: Mozilla/5.0 Version/4.0 "
137  "\x10i\xC7n tho\xA1iSafari/534.30]"));
138  EXPECT_CALL(callbacks, onMessageBegin(1, _));
139  EXPECT_CALL(callbacks, onError(1, _, _))
140  .WillOnce(Invoke([&] (HTTPCodec::StreamID,
141  std::shared_ptr<HTTPException> error,
142  bool) {
143  EXPECT_EQ(error->getHttpStatusCode(), 400);
144  }));
145  codec.onIngress(*buffer);
146 }
147 
148 TEST(HTTP1xCodecTest, TestHeadRequestChunkedResponse) {
150  HTTP1xCodecCallback callbacks;
151  codec.setCallback(&callbacks);
152  auto txnID = codec.createStream();
153 
154  // Generate a HEAD request
155  auto reqBuf = folly::IOBuf::copyBuffer(
156  "HEAD /www.facebook.com HTTP/1.1\nHost: www.facebook.com\n\n");
157  codec.onIngress(*reqBuf);
158  EXPECT_EQ(callbacks.headersComplete, 1);
159 
160  // Generate chunked response with no body
161  HTTPMessage resp;
162  resp.setHTTPVersion(1, 1);
163  resp.setStatusCode(200);
164  resp.setIsChunked(true);
165  resp.getHeaders().set(HTTP_HEADER_TRANSFER_ENCODING, "chunked");
167  codec.generateHeader(respBuf, txnID, resp, true);
168  auto respStr = respBuf.move()->moveToFbString();
169  EXPECT_TRUE(respStr.find("0\r\n") == string::npos);
170 }
171 
172 TEST(HTTP1xCodecTest, TestGetRequestChunkedResponse) {
174  HTTP1xCodecCallback callbacks;
175  codec.setCallback(&callbacks);
176  auto txnID = codec.createStream();
177 
178  // Generate a GET request
179  auto reqBuf = folly::IOBuf::copyBuffer(
180  "GET /www.facebook.com HTTP/1.1\nHost: www.facebook.com\n\n");
181  codec.onIngress(*reqBuf);
182  EXPECT_EQ(callbacks.headersComplete, 1);
183 
184  // Generate chunked response with body
185  HTTPMessage resp;
186  resp.setHTTPVersion(1, 1);
187  resp.setStatusCode(200);
188  resp.setIsChunked(true);
189  resp.getHeaders().set(HTTP_HEADER_TRANSFER_ENCODING, "chunked");
191  codec.generateHeader(respBuf, txnID, resp, false);
192 
193  auto headerFromBuf = respBuf.split(respBuf.chainLength());
194 
195  string resp1("Hello");
196  auto body1 = folly::IOBuf::copyBuffer(resp1);
197 
198  string resp2("");
199  auto body2 = folly::IOBuf::copyBuffer(resp2);
200 
201  codec.generateBody(respBuf, txnID, std::move(body1), HTTPCodec::NoPadding,
202  false);
203 
204  auto bodyFromBuf = respBuf.split(respBuf.chainLength());
205  ASSERT_EQ("5\r\nHello\r\n", bodyFromBuf->moveToFbString());
206 
207  codec.generateBody(respBuf, txnID, std::move(body2), HTTPCodec::NoPadding,
208  true);
209 
210  bodyFromBuf = respBuf.split(respBuf.chainLength());
211  ASSERT_EQ("0\r\n\r\n", bodyFromBuf->moveToFbString());
212 }
213 
214 unique_ptr<folly::IOBuf> getChunkedRequest1st() {
215  string req("GET /aha HTTP/1.1\n");
216  return folly::IOBuf::copyBuffer(req);
217 }
218 
219 unique_ptr<folly::IOBuf> getChunkedRequest2nd() {
220  string req("Host: m.facebook.com\nAccept-Encoding: meflate\n\n");
221  return folly::IOBuf::copyBuffer(req);
222 }
223 
224 TEST(HTTP1xCodecTest, TestChunkedHeaders) {
226  HTTP1xCodecCallback callbacks;
227  codec.setCallback(&callbacks);
228  // test a sequence of requests to make sure we're resetting the size counter
229  for (int i = 0; i < 3; i++) {
230  callbacks.headersComplete = 0;
231  auto buffer1 = getChunkedRequest1st();
232  codec.onIngress(*buffer1);
233  EXPECT_EQ(callbacks.headersComplete, 0);
234 
235  auto buffer2 = getChunkedRequest2nd();
236  codec.onIngress(*buffer2);
237  EXPECT_EQ(callbacks.headersComplete, 1);
239  buffer1->length() + buffer2->length());
240  }
241 }
242 
243 TEST(HTTP1xCodecTest, TestChunkedUpstream) {
245 
246  auto txnID = codec.createStream();
247 
248  HTTPMessage msg;
249  msg.setHTTPVersion(1, 1);
250  msg.setURL("https://www.facebook.com/");
251  msg.getHeaders().set(HTTP_HEADER_HOST, "www.facebook.com");
252  msg.getHeaders().set(HTTP_HEADER_TRANSFER_ENCODING, "chunked");
253  msg.setIsChunked(true);
254 
256 
258  codec.generateHeader(buf, txnID, msg, false, &size);
259  auto headerFromBuf = buf.split(buf.chainLength());
260 
261  string req1("Hello");
262  auto body1 = folly::IOBuf::copyBuffer(req1);
263 
264  string req2("World");
265  auto body2 = folly::IOBuf::copyBuffer(req2);
266 
267  codec.generateBody(buf, txnID, std::move(body1), HTTPCodec::NoPadding,
268  false);
269 
270  auto bodyFromBuf = buf.split(buf.chainLength());
271  ASSERT_EQ("5\r\nHello\r\n", bodyFromBuf->moveToFbString());
272 
273  codec.generateBody(buf, txnID, std::move(body2), HTTPCodec::NoPadding,
274  true);
275  LOG(WARNING) << "len chain" << buf.chainLength();
276 
277  auto eomFromBuf = buf.split(buf.chainLength());
278  ASSERT_EQ("5\r\nWorld\r\n0\r\n\r\n", eomFromBuf->moveToFbString());
279 }
280 
281 TEST(HTTP1xCodecTest, TestBadPost100) {
283  MockHTTPCodecCallback callbacks;
284  codec.setCallback(&callbacks);
286 
287  InSequence enforceOrder;
288  EXPECT_CALL(callbacks, onMessageBegin(1, _));
289  EXPECT_CALL(callbacks, onHeadersComplete(1, _))
290  .WillOnce(InvokeWithoutArgs([&] {
291  HTTPMessage cont;
292  cont.setStatusCode(100);
293  cont.setStatusMessage("Continue");
294  codec.generateHeader(writeBuf, 1, cont);
295  }));
296 
297  EXPECT_CALL(callbacks, onBody(1, _, _));
298  EXPECT_CALL(callbacks, onMessageComplete(1, _));
299  EXPECT_CALL(callbacks, onMessageBegin(2, _))
300  .WillOnce(InvokeWithoutArgs([&] {
301  // simulate HTTPSession's aversion to pipelining
302  codec.setParserPaused(true);
303 
304  // Trigger the response to the POST
305  HTTPMessage resp;
306  resp.setStatusCode(200);
307  resp.setStatusMessage("OK");
308  codec.generateHeader(writeBuf, 1, resp);
309  codec.generateEOM(writeBuf, 1);
310  codec.setParserPaused(false);
311  }));
312  EXPECT_CALL(callbacks, onError(2, _, _))
313  .WillOnce(InvokeWithoutArgs([&] {
314  HTTPMessage resp;
315  resp.setStatusCode(400);
316  resp.setStatusMessage("Bad");
317  codec.generateHeader(writeBuf, 2, resp);
318  codec.generateEOM(writeBuf, 2);
319  }));
320  // Generate a POST request with a bad content-length
321  auto reqBuf = folly::IOBuf::copyBuffer(
322  "POST /www.facebook.com HTTP/1.1\r\nHost: www.facebook.com\r\n"
323  "Expect: 100-Continue\r\nContent-Length: 5\r\n\r\nabcdefghij");
324  codec.onIngress(*reqBuf);
325 }
326 
327 TEST(HTTP1xCodecTest, TestMultipleIdenticalContentLengthHeaders) {
329  FakeHTTPCodecCallback callbacks;
330  codec.setCallback(&callbacks);
332 
333  // Generate a POST request with two identical Content-Length headers
334  auto reqBuf = folly::IOBuf::copyBuffer(
335  "POST /www.facebook.com HTTP/1.1\r\nHost: www.facebook.com\r\n"
336  "Content-Length: 5\r\nContent-Length: 5\r\n\r\n");
337  codec.onIngress(*reqBuf);
338 
339  // Check that the request is accepted
340  EXPECT_EQ(callbacks.streamErrors, 0);
341  EXPECT_EQ(callbacks.messageBegin, 1);
342  EXPECT_EQ(callbacks.headersComplete, 1);
343 
344 }
345 
346 TEST(HTTP1xCodecTest, TestMultipleDistinctContentLengthHeaders) {
348  FakeHTTPCodecCallback callbacks;
349  codec.setCallback(&callbacks);
351 
352  // Generate a POST request with two distinct Content-Length headers
353  auto reqBuf = folly::IOBuf::copyBuffer(
354  "POST /www.facebook.com HTTP/1.1\r\nHost: www.facebook.com\r\n"
355  "Content-Length: 5\r\nContent-Length: 6\r\n\r\n");
356  codec.onIngress(*reqBuf);
357 
358  // Check that the request fails before the codec finishes parsing the headers
359  EXPECT_EQ(callbacks.streamErrors, 1);
360  EXPECT_EQ(callbacks.messageBegin, 1);
361  EXPECT_EQ(callbacks.headersComplete, 0);
362  EXPECT_EQ(callbacks.lastParseError->getHttpStatusCode(), 400);
363 }
364 
365 TEST(HTTP1xCodecTest, TestCorrectTransferEncodingHeader) {
367  FakeHTTPCodecCallback callbacks;
368  downstream.setCallback(&callbacks);
370 
371  // Generate a POST request with folded
372  auto reqBuf = folly::IOBuf::copyBuffer(
373  "POST /www.facebook.com HTTP/1.1\r\nHost: www.facebook.com\r\n"
374  "Transfer-Encoding: chunked\r\n\r\n");
375  downstream.onIngress(*reqBuf);
376 
377  // Check that the request fails before the codec finishes parsing the headers
378  EXPECT_EQ(callbacks.streamErrors, 0);
379  EXPECT_EQ(callbacks.messageBegin, 1);
380  EXPECT_EQ(callbacks.headersComplete, 1);
381 }
382 
383 TEST(HTTP1xCodecTest, TestFoldedTransferEncodingHeader) {
385  FakeHTTPCodecCallback callbacks;
386  downstream.setCallback(&callbacks);
388 
389  // Generate a POST request with folded
390  auto reqBuf = folly::IOBuf::copyBuffer(
391  "POST /www.facebook.com HTTP/1.1\r\nHost: www.facebook.com\r\n"
392  "Transfer-Encoding: \r\n chunked\r\nContent-Length: 8\r\n\r\n");
393  downstream.onIngress(*reqBuf);
394 
395  // Check that the request fails before the codec finishes parsing the headers
396  EXPECT_EQ(callbacks.streamErrors, 1);
397  EXPECT_EQ(callbacks.messageBegin, 1);
398  EXPECT_EQ(callbacks.headersComplete, 0);
399  EXPECT_EQ(callbacks.lastParseError->getHttpStatusCode(), 400);
400 }
401 
402 TEST(HTTP1xCodecTest, TestBadTransferEncodingHeader) {
404  FakeHTTPCodecCallback callbacks;
405  downstream.setCallback(&callbacks);
407 
408  auto reqBuf = folly::IOBuf::copyBuffer(
409  "POST /www.facebook.com HTTP/1.1\r\nHost: www.facebook.com\r\n"
410  "Transfer-Encoding: chunked, zorg\r\n\r\n");
411  downstream.onIngress(*reqBuf);
412 
413  // Check that the request fails before the codec finishes parsing the headers
414  EXPECT_EQ(callbacks.streamErrors, 1);
415  EXPECT_EQ(callbacks.messageBegin, 1);
416  EXPECT_EQ(callbacks.headersComplete, 0);
417  EXPECT_EQ(callbacks.lastParseError->getHttpStatusCode(), 400);
418 }
419 
420 TEST(HTTP1xCodecTest, Test1xxConnectionHeader) {
423  HTTP1xCodecCallback callbacks;
424  upstream.setCallback(&callbacks);
425  HTTPMessage resp;
426  resp.setStatusCode(100);
427  resp.setHTTPVersion(1, 1);
428  resp.getHeaders().add(HTTP_HEADER_CONNECTION, "Upgrade");
430  auto streamID = downstream.createStream();
431  downstream.generateHeader(writeBuf, streamID, resp);
432  upstream.onIngress(*writeBuf.front());
433  EXPECT_EQ(callbacks.headersComplete, 1);
434  EXPECT_EQ(
435  callbacks.msg_->getHeaders().getSingleOrEmpty(HTTP_HEADER_CONNECTION),
436  "Upgrade");
437  resp.setStatusCode(200);
440  writeBuf.move();
441  downstream.generateHeader(writeBuf, streamID, resp);
442  upstream.onIngress(*writeBuf.front());
443  EXPECT_EQ(callbacks.headersComplete, 2);
444  EXPECT_EQ(
445  callbacks.msg_->getHeaders().getSingleOrEmpty(HTTP_HEADER_CONNECTION),
446  "keep-alive");
447 }
448 
449 TEST(HTTP1xCodecTest, TestChainedBody) {
451  MockHTTPCodecCallback callbacks;
452  codec.setCallback(&callbacks);
453 
454  folly::IOBufQueue bodyQueue;
455  ON_CALL(callbacks, onBody(1, _, _))
456  .WillByDefault(Invoke(
457  [&bodyQueue](HTTPCodec::StreamID, std::shared_ptr<folly::IOBuf> buf,
458  uint16_t) { bodyQueue.append(buf->clone()); }));
459 
460  folly::IOBufQueue reqQueue;
462  "POST /test.php HTTP/1.1\r\nHost: www.test.com\r\n"
463  "Content-Length: 10\r\n\r\nabcde"));
464  reqQueue.append(folly::IOBuf::copyBuffer("fghij"));
465 
466  while (!reqQueue.empty()) {
467  auto processed = codec.onIngress(*reqQueue.front());
468  if (processed == 0) {
469  break;
470  }
471  reqQueue.trimStart(processed);
472  }
473 
474  EXPECT_TRUE(folly::IOBufEqualTo()(*bodyQueue.front(),
475  *folly::IOBuf::copyBuffer("abcdefghij")));
476 }
477 
478 TEST(HTTP1xCodecTest, TestIgnoreUpstreamUpgrade) {
480  FakeHTTPCodecCallback callbacks;
481  codec.setCallback(&callbacks);
483 
484  auto reqBuf = folly::IOBuf::copyBuffer(
485  "HTTP/1.1 200 OK\r\n"
486  "Connection: close\r\n"
487  "Upgrade: h2,h2c\r\n"
488  "\r\n"
489  "<!DOCTYPE html>");
490  codec.onIngress(*reqBuf);
491 
492  EXPECT_EQ(callbacks.streamErrors, 0);
493  EXPECT_EQ(callbacks.messageBegin, 1);
494  EXPECT_EQ(callbacks.headersComplete, 1);
495  EXPECT_EQ(callbacks.bodyLength, 15);
496 }
497 
498 TEST(HTTP1xCodecTest, WebsocketUpgrade) {
501  HTTP1xCodecCallback downStreamCallbacks;
502  HTTP1xCodecCallback upstreamCallbacks;
503  downstreamCodec.setCallback(&downStreamCallbacks);
504  upstreamCodec.setCallback(&upstreamCallbacks);
505 
506  HTTPMessage req;
507  req.setHTTPVersion(1, 1);
508  req.setURL("/websocket");
510  folly::IOBufQueue buf;
511  auto streamID = upstreamCodec.createStream();
512  upstreamCodec.generateHeader(buf, streamID, req);
513 
514  downstreamCodec.onIngress(*buf.front());
515  EXPECT_EQ(downStreamCallbacks.headersComplete, 1);
516  EXPECT_TRUE(downStreamCallbacks.msg_->isIngressWebsocketUpgrade());
517  auto& headers = downStreamCallbacks.msg_->getHeaders();
518  auto ws_key_header = headers.getSingleOrEmpty(HTTP_HEADER_SEC_WEBSOCKET_KEY);
519  EXPECT_NE(ws_key_header, empty_string);
520 
521  HTTPMessage resp;
522  resp.setHTTPVersion(1, 1);
523  resp.setStatusCode(101);
525  buf.clear();
526  downstreamCodec.generateHeader(buf, streamID, resp);
527  upstreamCodec.onIngress(*buf.front());
528  EXPECT_EQ(upstreamCallbacks.headersComplete, 1);
529  headers = upstreamCallbacks.msg_->getHeaders();
530  auto ws_accept_header =
531  headers.getSingleOrEmpty(HTTP_HEADER_SEC_WEBSOCKET_ACCEPT);
532  EXPECT_NE(ws_accept_header, empty_string);
533 }
534 
535 TEST(HTTP1xCodecTest, WebsocketUpgradeKeyError) {
537  HTTP1xCodecCallback callbacks;
538  codec.setCallback(&callbacks);
539 
540  HTTPMessage req;
541  req.setHTTPVersion(1, 1);
542  req.setURL("/websocket");
544  folly::IOBufQueue buf;
545  auto streamID = codec.createStream();
546  codec.generateHeader(buf, streamID, req);
547 
548  auto resp = folly::IOBuf::copyBuffer(
549  "HTTP/1.1 200 OK\r\n"
550  "Connection: upgrade\r\n"
551  "Upgrade: websocket\r\n"
552  "\r\n");
553  codec.onIngress(*resp);
554  EXPECT_EQ(callbacks.headersComplete, 0);
555  EXPECT_EQ(callbacks.errors, 1);
556 }
557 
558 TEST(HTTP1xCodecTest, WebsocketUpgradeHeaderSet) {
560  HTTPMessage req;
562  req.setURL("/websocket");
564  req.getHeaders().add(proxygen::HTTP_HEADER_UPGRADE, "Websocket");
565 
566  folly::IOBufQueue buf;
567  upstreamCodec.generateHeader(buf, upstreamCodec.createStream(), req);
568 
570  HTTP1xCodecCallback callbacks;
571  downstreamCodec.setCallback(&callbacks);
572  downstreamCodec.onIngress(*buf.front());
573  auto headers = callbacks.msg_->getHeaders();
574  EXPECT_EQ(headers.getSingleOrEmpty(HTTP_HEADER_SEC_WEBSOCKET_KEY),
575  empty_string);
576 }
577 
578 TEST(HTTP1xCodecTest, WebsocketConnectionHeader) {
580  HTTPMessage req;
582  req.setURL("/websocket");
584  req.getHeaders().add(proxygen::HTTP_HEADER_CONNECTION, "upgrade, keep-alive");
586  "key should change");
588  "this should not be found");
589 
590  folly::IOBufQueue buf;
591  upstreamCodec.generateHeader(buf, upstreamCodec.createStream(), req);
593  HTTP1xCodecCallback callbacks;
594  downstreamCodec.setCallback(&callbacks);
595  downstreamCodec.onIngress(*buf.front());
596  auto headers = callbacks.msg_->getHeaders();
597  auto ws_sec_key = headers.getSingleOrEmpty(HTTP_HEADER_SEC_WEBSOCKET_KEY);
598  EXPECT_NE(ws_sec_key, empty_string);
599  EXPECT_NE(ws_sec_key, "key should change");
600 
601  // We know the key is length 16
602  // https://tools.ietf.org/html/rfc6455#section-4.2.1.5
603  // for base64 % 3 leaves 1 byte so we expect padding of '=='
604  // hence this offset of 2 explicitly
605  EXPECT_NO_THROW(Base64::decode(ws_sec_key, 2));
606  EXPECT_EQ(16, Base64::decode(ws_sec_key, 2).length());
607 
608  EXPECT_EQ(headers.getSingleOrEmpty(HTTP_HEADER_SEC_WEBSOCKET_ACCEPT),
609  empty_string);
610  EXPECT_EQ(headers.getSingleOrEmpty(HTTP_HEADER_CONNECTION),
611  "upgrade, keep-alive");
612 }
613 
614 TEST(HTTP1xCodecTest, TrailersAndEomAreNotGeneratedWhenNonChunked) {
615  // Verify that generateTrailes and generateEom result in 0 bytes
616  // generated when message is not chunked.
617  // HTTP2 codec handles all BODY as regular non-chunked body, thus
618  // HTTPTransation SM transitions allow trailers after regular body. Which
619  // is not allowed in HTTP1.
621 
622  auto txnID = codec.createStream();
623 
624  HTTPMessage msg;
625  msg.setHTTPVersion(1, 1);
626  msg.setURL("https://www.facebook.com/");
627  msg.getHeaders().set(HTTP_HEADER_HOST, "www.facebook.com");
628  msg.setIsChunked(false);
629 
630  folly::IOBufQueue buf;
631  codec.generateHeader(buf, txnID, msg);
632 
633  HTTPHeaders trailers;
634  trailers.add("X-Test-Trailer", "test");
635  EXPECT_EQ(0, codec.generateTrailers(buf, txnID, trailers));
636  EXPECT_EQ(0, codec.generateEOM(buf, txnID));
637 }
638 
640  public TestWithParam<std::pair<std::list<string>, string>> {
641  public:
642  using ParamType = std::pair<std::list<string>, string>;
643 };
644 
645 TEST_P(ConnectionHeaderTest, TestConnectionHeaders) {
648  HTTP1xCodecCallback callbacks;
649  downstream.setCallback(&callbacks);
650  HTTPMessage req;
652  req.setURL("/");
653  auto val = GetParam();
654  for (auto header: val.first) {
655  req.getHeaders().add(HTTP_HEADER_CONNECTION, header);
656  }
658  upstream.generateHeader(writeBuf, upstream.createStream(), req);
659  downstream.onIngress(*writeBuf.front());
660  EXPECT_EQ(callbacks.headersComplete, 1);
661  auto& headers = callbacks.msg_->getHeaders();
662  EXPECT_EQ(headers.getSingleOrEmpty(HTTP_HEADER_CONNECTION),
663  val.second);
664 }
665 
666 
668  HTTP1xCodec,
670  ::testing::Values(
671  // Moves close to the end
673  { "foo", "bar", "close", "baz" }, "foo, bar, baz, close"),
674  // has to resize token vector
676  { "foo", "bar, close", "baz" }, "foo, bar, baz, close"),
677  // whitespace trimming
679  { " foo", "bar, close ", " baz " }, "foo, bar, baz, close"),
680  // No close token => keep-alive
682  { "foo", "bar, boo", "baz" }, "foo, bar, boo, baz, keep-alive"),
683  // close and keep-alive => close
685  { "foo", "keep-alive, boo", "close" }, "foo, boo, close"),
686  // upgrade gets no special treatment
688  { "foo", "upgrade, boo", "baz" }, "foo, upgrade, boo, baz, keep-alive")
689  ));
std::unique_ptr< folly::IOBuf > split(size_t n)
Definition: IOBufQueue.h:420
std::vector< uint8_t > buffer(kBufferSize+16)
size_t generateTrailers(folly::IOBufQueue &writeBuf, StreamID txn, const HTTPHeaders &trailers) override
const folly::IOBuf * front() const
Definition: IOBufQueue.h:476
static const folly::Optional< uint8_t > NoPadding
Definition: HTTPCodec.h:53
void append(std::unique_ptr< folly::IOBuf > &&buf, bool pack=false)
Definition: IOBufQueue.cpp:143
bool remove(folly::StringPiece name)
constexpr size_t headerSize()
Definition: RecordIO-inl.h:101
size_t chainLength() const
Definition: IOBufQueue.h:492
void onChunkHeader(HTTPCodec::StreamID, size_t) override
void onPushMessageBegin(HTTPCodec::StreamID, HTTPCodec::StreamID, HTTPMessage *) override
#define EXPECT_NO_THROW(statement)
Definition: gtest.h:1845
#define ASSERT_EQ(val1, val2)
Definition: gtest.h:1956
std::unique_ptr< HTTPException > lastParseError
Definition: TestUtils.h:405
void setStatusMessage(T &&msg)
Definition: HTTPMessage.h:242
void generateHeader(folly::IOBufQueue &writeBuf, StreamID txn, const HTTPMessage &msg, bool eom=false, HTTPHeaderSize *size=nullptr) override
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
bool empty() const
Definition: IOBufQueue.h:503
void onChunkComplete(HTTPCodec::StreamID) override
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
CodecFactory codec
STL namespace.
double val
Definition: String.cpp:273
std::unique_ptr< folly::IOBuf > move()
Definition: IOBufQueue.h:459
std::pair< std::list< string >, string > ParamType
const HTTPHeaderSize & getIngressHeaderSize() const
Definition: HTTPMessage.h:615
TEST(HTTP1xCodecTest, TestSimpleHeaders)
void onBody(HTTPCodec::StreamID, std::unique_ptr< folly::IOBuf > chain, uint16_t) override
requires And< SemiMovable< VN >... > &&SemiMovable< E > auto error(E e)
Definition: error.h:48
PolymorphicAction< internal::InvokeWithoutArgsAction< FunctionImpl > > InvokeWithoutArgs(FunctionImpl function_impl)
unique_ptr< folly::IOBuf > getChunkedRequest1st()
void onHeadersComplete(HTTPCodec::StreamID, std::unique_ptr< HTTPMessage > msg) override
void setIsChunked(bool chunked)
Definition: HTTPMessage.h:79
void set(folly::StringPiece name, const std::string &value)
Definition: HTTPHeaders.h:119
unique_ptr< folly::IOBuf > getChunkedRequest2nd()
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
void writeBuf(const Buf &buf, folly::io::Appender &out)
TEST_P(RFC1867CR, Test)
ParseURL setURL(T &&url)
Definition: HTTPMessage.h:183
static Options cacheChainLength()
Definition: IOBufQueue.h:83
PolymorphicAction< internal::InvokeAction< FunctionImpl > > Invoke(FunctionImpl function_impl)
void setCallback(Callback *callback) override
Definition: HTTP1xCodec.h:40
static std::string decode(const std::string &b64message, int padding)
Definition: Base64.cpp:51
unique_ptr< folly::IOBuf > getSimpleRequestData()
size_t onIngress(const folly::IOBuf &buf) override
void onTrailersComplete(HTTPCodec::StreamID, std::unique_ptr< HTTPHeaders >) override
HTTPHeaders & getHeaders()
Definition: HTTPMessage.h:273
void onMessageBegin(HTTPCodec::StreamID, HTTPMessage *) override
void setMethod(HTTPMethod method)
std::size_t computeChainDataLength() const
Definition: IOBuf.cpp:501
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
const std::string empty_string
Definition: HTTPHeaders.cpp:23
#define ON_CALL(obj, call)
HTTPHeaderSize headerSize
void onMessageComplete(HTTPCodec::StreamID, bool) override
#define EXPECT_NE(val1, val2)
Definition: gtest.h:1926
void setParserPaused(bool paused) override
void setHTTPVersion(uint8_t major, uint8_t minor)
void trimStart(size_t amount)
Definition: IOBufQueue.cpp:255
#define EXPECT_CALL(obj, call)
uint64_t StreamID
Definition: HTTPCodec.h:49
const internal::AnythingMatcher _
void add(folly::StringPiece name, folly::StringPiece value)
Definition: HTTPHeaders.cpp:52
void setEgressWebsocketUpgrade()
Definition: HTTPMessage.h:66
StreamID createStream() override
static std::unique_ptr< IOBuf > copyBuffer(const void *buf, std::size_t size, std::size_t headroom=0, std::size_t minTailroom=0)
Definition: IOBuf.h:1587
size_t generateEOM(folly::IOBufQueue &writeBuf, StreamID txn) override
uint32_t streamID
Definition: SPDYCodec.cpp:131
void onError(HTTPCodec::StreamID, const HTTPException &, bool) override
INSTANTIATE_TEST_CASE_P(ValueTest, RFC1867CR,::testing::Values(string("zyx\r\nwvu", 8), string("\rzyxwvut", 8), string("zyxwvut\r", 8), string("\nzyxwvut", 8), string("zyxwvut\n", 8), string("\r\n\r\n\r\n\r\n", 8), string("\r\r\r\r\r\r\r\r", 8)))
void onIngressEOF() override
std::unique_ptr< HTTPMessage > msg_
size_t generateBody(folly::IOBufQueue &writeBuf, StreamID txn, std::unique_ptr< folly::IOBuf > chain, folly::Optional< uint8_t > padding, bool eom) override
void setStatusCode(uint16_t status)