1 #include "quiche/binary_http/binary_http_message.h"
2 
3 #include <cstdint>
4 #include <memory>
5 #include <string>
6 #include <vector>
7 
8 #include "absl/container/flat_hash_map.h"
9 #include "quiche/common/platform/api/quiche_test.h"
10 
11 using ::testing::ContainerEq;
12 using ::testing::FieldsAre;
13 using ::testing::StrEq;
14 
15 namespace quiche {
16 namespace {
17 
WordToBytes(uint32_t word)18 std::string WordToBytes(uint32_t word) {
19   return std::string({static_cast<char>(word >> 24),
20                       static_cast<char>(word >> 16),
21                       static_cast<char>(word >> 8), static_cast<char>(word)});
22 }
23 
24 template <class T>
TestPrintTo(const T & resp)25 void TestPrintTo(const T& resp) {
26   std::ostringstream os;
27   PrintTo(resp, &os);
28   EXPECT_EQ(os.str(), resp.DebugString());
29 }
30 }  // namespace
31 // Test examples from
32 // https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html
33 
TEST(BinaryHttpRequest,EncodeGetNoBody)34 TEST(BinaryHttpRequest, EncodeGetNoBody) {
35   /*
36     GET /hello.txt HTTP/1.1
37     User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
38     Host: www.example.com
39     Accept-Language: en, mi
40   */
41   BinaryHttpRequest request({"GET", "https", "www.example.com", "/hello.txt"});
42   request
43       .AddHeaderField({"User-Agent",
44                        "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"})
45       ->AddHeaderField({"Host", "www.example.com"})
46       ->AddHeaderField({"Accept-Language", "en, mi"});
47   /*
48       00000000: 00034745 54056874 74707300 0a2f6865  ..GET.https../he
49       00000010: 6c6c6f2e 74787440 6c0a7573 65722d61  llo.txt@l.user-a
50       00000020: 67656e74 34637572 6c2f372e 31362e33  gent4curl/7.16.3
51       00000030: 206c6962 6375726c 2f372e31 362e3320   libcurl/7.16.3
52       00000040: 4f70656e 53534c2f 302e392e 376c207a  OpenSSL/0.9.7l z
53       00000050: 6c69622f 312e322e 3304686f 73740f77  lib/1.2.3.host.w
54       00000060: 77772e65 78616d70 6c652e63 6f6d0f61  ww.example.com.a
55       00000070: 63636570 742d6c61 6e677561 67650665  ccept-language.e
56       00000080: 6e2c206d 6900                        n, mi..
57   */
58   const uint32_t expected_words[] = {
59       0x00034745, 0x54056874, 0x74707300, 0x0a2f6865, 0x6c6c6f2e, 0x74787440,
60       0x6c0a7573, 0x65722d61, 0x67656e74, 0x34637572, 0x6c2f372e, 0x31362e33,
61       0x206c6962, 0x6375726c, 0x2f372e31, 0x362e3320, 0x4f70656e, 0x53534c2f,
62       0x302e392e, 0x376c207a, 0x6c69622f, 0x312e322e, 0x3304686f, 0x73740f77,
63       0x77772e65, 0x78616d70, 0x6c652e63, 0x6f6d0f61, 0x63636570, 0x742d6c61,
64       0x6e677561, 0x67650665, 0x6e2c206d, 0x69000000};
65   std::string expected;
66   for (const auto& word : expected_words) {
67     expected += WordToBytes(word);
68   }
69   // Remove padding.
70   expected.resize(expected.size() - 2);
71 
72   const auto result = request.Serialize();
73   ASSERT_TRUE(result.ok());
74   ASSERT_EQ(*result, expected);
75   EXPECT_THAT(
76       request.DebugString(),
77       StrEq("BinaryHttpRequest{BinaryHttpMessage{Headers{Field{user-agent=curl/"
78             "7.16.3 "
79             "libcurl/7.16.3 OpenSSL/0.9.7l "
80             "zlib/1.2.3};Field{host=www.example.com};Field{accept-language=en, "
81             "mi}}Body{}}}"));
82   TestPrintTo(request);
83 }
84 
TEST(BinaryHttpRequest,DecodeGetNoBody)85 TEST(BinaryHttpRequest, DecodeGetNoBody) {
86   const uint32_t words[] = {
87       0x00034745, 0x54056874, 0x74707300, 0x0a2f6865, 0x6c6c6f2e, 0x74787440,
88       0x6c0a7573, 0x65722d61, 0x67656e74, 0x34637572, 0x6c2f372e, 0x31362e33,
89       0x206c6962, 0x6375726c, 0x2f372e31, 0x362e3320, 0x4f70656e, 0x53534c2f,
90       0x302e392e, 0x376c207a, 0x6c69622f, 0x312e322e, 0x3304686f, 0x73740f77,
91       0x77772e65, 0x78616d70, 0x6c652e63, 0x6f6d0f61, 0x63636570, 0x742d6c61,
92       0x6e677561, 0x67650665, 0x6e2c206d, 0x69000000};
93   std::string data;
94   for (const auto& word : words) {
95     data += WordToBytes(word);
96   }
97   const auto request_so = BinaryHttpRequest::Create(data);
98   ASSERT_TRUE(request_so.ok());
99   const BinaryHttpRequest request = *request_so;
100   ASSERT_THAT(request.control_data(),
101               FieldsAre("GET", "https", "", "/hello.txt"));
102   std::vector<BinaryHttpMessage::Field> expected_fields = {
103       {"user-agent", "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"},
104       {"host", "www.example.com"},
105       {"accept-language", "en, mi"}};
106   for (const auto& field : expected_fields) {
107     TestPrintTo(field);
108   }
109   ASSERT_THAT(request.GetHeaderFields(), ContainerEq(expected_fields));
110   ASSERT_EQ(request.body(), "");
111   EXPECT_THAT(
112       request.DebugString(),
113       StrEq("BinaryHttpRequest{BinaryHttpMessage{Headers{Field{user-agent=curl/"
114             "7.16.3 "
115             "libcurl/7.16.3 OpenSSL/0.9.7l "
116             "zlib/1.2.3};Field{host=www.example.com};Field{accept-language=en, "
117             "mi}}Body{}}}"));
118   TestPrintTo(request);
119 }
120 
TEST(BinaryHttpRequest,EncodeGetWithAuthority)121 TEST(BinaryHttpRequest, EncodeGetWithAuthority) {
122   /*
123     GET https://www.example.com/hello.txt HTTP/1.1
124     User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
125     Accept-Language: en, mi
126   */
127   BinaryHttpRequest request({"GET", "https", "www.example.com", "/hello.txt"});
128   request
129       .AddHeaderField({"User-Agent",
130                        "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"})
131       ->AddHeaderField({"Accept-Language", "en, mi"});
132   /*
133     00000000: 00034745 54056874 7470730f 7777772e  ..GET.https.www.
134     00000010: 6578616d 706c652e 636f6d0a 2f68656c  example.com./hel
135     00000020: 6c6f2e74 78744057 0a757365 722d6167  lo.txt@W.user-ag
136     00000030: 656e7434 6375726c 2f372e31 362e3320  ent4curl/7.16.3
137     00000040: 6c696263 75726c2f 372e3136 2e33204f  libcurl/7.16.3 O
138     00000050: 70656e53 534c2f30 2e392e37 6c207a6c  penSSL/0.9.7l zl
139     00000060: 69622f31 2e322e33 0f616363 6570742d  ib/1.2.3.accept-
140     00000070: 6c616e67 75616765 06656e2c 206d6900  language.en, mi.
141   */
142 
143   const uint32_t expected_words[] = {
144       0x00034745, 0x54056874, 0x7470730f, 0x7777772e, 0x6578616d, 0x706c652e,
145       0x636f6d0a, 0x2f68656c, 0x6c6f2e74, 0x78744057, 0x0a757365, 0x722d6167,
146       0x656e7434, 0x6375726c, 0x2f372e31, 0x362e3320, 0x6c696263, 0x75726c2f,
147       0x372e3136, 0x2e33204f, 0x70656e53, 0x534c2f30, 0x2e392e37, 0x6c207a6c,
148       0x69622f31, 0x2e322e33, 0x0f616363, 0x6570742d, 0x6c616e67, 0x75616765,
149       0x06656e2c, 0x206d6900};
150   std::string expected;
151   for (const auto& word : expected_words) {
152     expected += WordToBytes(word);
153   }
154   const auto result = request.Serialize();
155   ASSERT_TRUE(result.ok());
156   ASSERT_EQ(*result, expected);
157   EXPECT_THAT(
158       request.DebugString(),
159       StrEq("BinaryHttpRequest{BinaryHttpMessage{Headers{Field{user-agent=curl/"
160             "7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l "
161             "zlib/1.2.3};Field{accept-language=en, mi}}Body{}}}"));
162 }
163 
TEST(BinaryHttpRequest,DecodeGetWithAuthority)164 TEST(BinaryHttpRequest, DecodeGetWithAuthority) {
165   const uint32_t words[] = {
166       0x00034745, 0x54056874, 0x7470730f, 0x7777772e, 0x6578616d, 0x706c652e,
167       0x636f6d0a, 0x2f68656c, 0x6c6f2e74, 0x78744057, 0x0a757365, 0x722d6167,
168       0x656e7434, 0x6375726c, 0x2f372e31, 0x362e3320, 0x6c696263, 0x75726c2f,
169       0x372e3136, 0x2e33204f, 0x70656e53, 0x534c2f30, 0x2e392e37, 0x6c207a6c,
170       0x69622f31, 0x2e322e33, 0x0f616363, 0x6570742d, 0x6c616e67, 0x75616765,
171       0x06656e2c, 0x206d6900, 0x00};
172   std::string data;
173   for (const auto& word : words) {
174     data += WordToBytes(word);
175   }
176   const auto request_so = BinaryHttpRequest::Create(data);
177   ASSERT_TRUE(request_so.ok());
178   const BinaryHttpRequest request = *request_so;
179   ASSERT_THAT(request.control_data(),
180               FieldsAre("GET", "https", "www.example.com", "/hello.txt"));
181   std::vector<BinaryHttpMessage::Field> expected_fields = {
182       {"user-agent", "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"},
183       {"accept-language", "en, mi"}};
184   ASSERT_THAT(request.GetHeaderFields(), ContainerEq(expected_fields));
185   ASSERT_EQ(request.body(), "");
186   EXPECT_THAT(
187       request.DebugString(),
188       StrEq("BinaryHttpRequest{BinaryHttpMessage{Headers{Field{user-agent=curl/"
189             "7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l "
190             "zlib/1.2.3};Field{accept-language=en, mi}}Body{}}}"));
191 }
192 
TEST(BinaryHttpRequest,EncodePostBody)193 TEST(BinaryHttpRequest, EncodePostBody) {
194   /*
195   POST /hello.txt HTTP/1.1
196   User-Agent: not/telling
197   Host: www.example.com
198   Accept-Language: en
199 
200   Some body that I used to post.
201   */
202   BinaryHttpRequest request({"POST", "https", "www.example.com", "/hello.txt"});
203   request.AddHeaderField({"User-Agent", "not/telling"})
204       ->AddHeaderField({"Host", "www.example.com"})
205       ->AddHeaderField({"Accept-Language", "en"})
206       ->set_body({"Some body that I used to post.\r\n"});
207   /*
208     00000000: 0004504f 53540568 74747073 000a2f68  ..POST.https../h
209     00000010: 656c6c6f 2e747874 3f0a7573 65722d61  ello.txt?.user-a
210     00000020: 67656e74 0b6e6f74 2f74656c 6c696e67  gent.not/telling
211     00000030: 04686f73 740f7777 772e6578 616d706c  .host.www.exampl
212     00000040: 652e636f 6d0f6163 63657074 2d6c616e  e.com.accept-lan
213     00000050: 67756167 6502656e 20536f6d 6520626f  guage.en Some bo
214     00000060: 64792074 68617420 49207573 65642074  dy that I used t
215     00000070: 6f20706f 73742e0d 0a                 o post....
216   */
217   const uint32_t expected_words[] = {
218       0x0004504f, 0x53540568, 0x74747073, 0x000a2f68, 0x656c6c6f, 0x2e747874,
219       0x3f0a7573, 0x65722d61, 0x67656e74, 0x0b6e6f74, 0x2f74656c, 0x6c696e67,
220       0x04686f73, 0x740f7777, 0x772e6578, 0x616d706c, 0x652e636f, 0x6d0f6163,
221       0x63657074, 0x2d6c616e, 0x67756167, 0x6502656e, 0x20536f6d, 0x6520626f,
222       0x64792074, 0x68617420, 0x49207573, 0x65642074, 0x6f20706f, 0x73742e0d,
223       0x0a000000};
224   std::string expected;
225   for (const auto& word : expected_words) {
226     expected += WordToBytes(word);
227   }
228   // Remove padding.
229   expected.resize(expected.size() - 3);
230   const auto result = request.Serialize();
231   ASSERT_TRUE(result.ok());
232   ASSERT_EQ(*result, expected);
233   EXPECT_THAT(
234       request.DebugString(),
235       StrEq("BinaryHttpRequest{BinaryHttpMessage{Headers{Field{user-agent=not/"
236             "telling};Field{host=www.example.com};Field{accept-language=en}}"
237             "Body{Some "
238             "body that I used to post.\r\n}}}"));
239 }
240 
TEST(BinaryHttpRequest,DecodePostBody)241 TEST(BinaryHttpRequest, DecodePostBody) {
242   const uint32_t words[] = {
243       0x0004504f, 0x53540568, 0x74747073, 0x000a2f68, 0x656c6c6f, 0x2e747874,
244       0x3f0a7573, 0x65722d61, 0x67656e74, 0x0b6e6f74, 0x2f74656c, 0x6c696e67,
245       0x04686f73, 0x740f7777, 0x772e6578, 0x616d706c, 0x652e636f, 0x6d0f6163,
246       0x63657074, 0x2d6c616e, 0x67756167, 0x6502656e, 0x20536f6d, 0x6520626f,
247       0x64792074, 0x68617420, 0x49207573, 0x65642074, 0x6f20706f, 0x73742e0d,
248       0x0a000000};
249   std::string data;
250   for (const auto& word : words) {
251     data += WordToBytes(word);
252   }
253   const auto request_so = BinaryHttpRequest::Create(data);
254   ASSERT_TRUE(request_so.ok());
255   BinaryHttpRequest request = *request_so;
256   ASSERT_THAT(request.control_data(),
257               FieldsAre("POST", "https", "", "/hello.txt"));
258   std::vector<BinaryHttpMessage::Field> expected_fields = {
259       {"user-agent", "not/telling"},
260       {"host", "www.example.com"},
261       {"accept-language", "en"}};
262   ASSERT_THAT(request.GetHeaderFields(), ContainerEq(expected_fields));
263   ASSERT_EQ(request.body(), "Some body that I used to post.\r\n");
264   EXPECT_THAT(
265       request.DebugString(),
266       StrEq("BinaryHttpRequest{BinaryHttpMessage{Headers{Field{user-agent=not/"
267             "telling};Field{host=www.example.com};Field{accept-language=en}}"
268             "Body{Some "
269             "body that I used to post.\r\n}}}"));
270 }
271 
TEST(BinaryHttpRequest,Equality)272 TEST(BinaryHttpRequest, Equality) {
273   BinaryHttpRequest request({"POST", "https", "www.example.com", "/hello.txt"});
274   request.AddHeaderField({"User-Agent", "not/telling"})
275       ->set_body({"hello, world!\r\n"});
276 
277   BinaryHttpRequest same({"POST", "https", "www.example.com", "/hello.txt"});
278   same.AddHeaderField({"User-Agent", "not/telling"})
279       ->set_body({"hello, world!\r\n"});
280   EXPECT_EQ(request, same);
281 }
282 
TEST(BinaryHttpRequest,Inequality)283 TEST(BinaryHttpRequest, Inequality) {
284   BinaryHttpRequest request({"POST", "https", "www.example.com", "/hello.txt"});
285   request.AddHeaderField({"User-Agent", "not/telling"})
286       ->set_body({"hello, world!\r\n"});
287 
288   BinaryHttpRequest different_control(
289       {"PUT", "https", "www.example.com", "/hello.txt"});
290   different_control.AddHeaderField({"User-Agent", "not/telling"})
291       ->set_body({"hello, world!\r\n"});
292   EXPECT_NE(request, different_control);
293 
294   BinaryHttpRequest different_header(
295       {"PUT", "https", "www.example.com", "/hello.txt"});
296   different_header.AddHeaderField({"User-Agent", "told/you"})
297       ->set_body({"hello, world!\r\n"});
298   EXPECT_NE(request, different_header);
299 
300   BinaryHttpRequest no_header(
301       {"PUT", "https", "www.example.com", "/hello.txt"});
302   no_header.set_body({"hello, world!\r\n"});
303   EXPECT_NE(request, no_header);
304 
305   BinaryHttpRequest different_body(
306       {"POST", "https", "www.example.com", "/hello.txt"});
307   different_body.AddHeaderField({"User-Agent", "not/telling"})
308       ->set_body({"goodbye, world!\r\n"});
309   EXPECT_NE(request, different_body);
310 
311   BinaryHttpRequest no_body({"POST", "https", "www.example.com", "/hello.txt"});
312   no_body.AddHeaderField({"User-Agent", "not/telling"});
313   EXPECT_NE(request, no_body);
314 }
315 
TEST(BinaryHttpResponse,EncodeNoBody)316 TEST(BinaryHttpResponse, EncodeNoBody) {
317   /*
318     HTTP/1.1 404 Not Found
319     Server: Apache
320   */
321   BinaryHttpResponse response(404);
322   response.AddHeaderField({"Server", "Apache"});
323   /*
324     0141940e 06736572 76657206 41706163  .A...server.Apac
325     686500                               he..
326   */
327   const uint32_t expected_words[] = {0x0141940e, 0x06736572, 0x76657206,
328                                      0x41706163, 0x68650000};
329   std::string expected;
330   for (const auto& word : expected_words) {
331     expected += WordToBytes(word);
332   }
333   // Remove padding.
334   expected.resize(expected.size() - 1);
335   const auto result = response.Serialize();
336   ASSERT_TRUE(result.ok());
337   ASSERT_EQ(*result, expected);
338   EXPECT_THAT(
339       response.DebugString(),
340       StrEq("BinaryHttpResponse(404){BinaryHttpMessage{Headers{Field{server="
341             "Apache}}Body{}}}"));
342 }
343 
TEST(BinaryHttpResponse,DecodeNoBody)344 TEST(BinaryHttpResponse, DecodeNoBody) {
345   /*
346     HTTP/1.1 404 Not Found
347     Server: Apache
348   */
349   const uint32_t words[] = {0x0141940e, 0x06736572, 0x76657206, 0x41706163,
350                             0x68650000};
351   std::string data;
352   for (const auto& word : words) {
353     data += WordToBytes(word);
354   }
355   const auto response_so = BinaryHttpResponse::Create(data);
356   ASSERT_TRUE(response_so.ok());
357   const BinaryHttpResponse response = *response_so;
358   ASSERT_EQ(response.status_code(), 404);
359   std::vector<BinaryHttpMessage::Field> expected_fields = {
360       {"server", "Apache"}};
361   ASSERT_THAT(response.GetHeaderFields(), ContainerEq(expected_fields));
362   ASSERT_EQ(response.body(), "");
363   ASSERT_TRUE(response.informational_responses().empty());
364   EXPECT_THAT(
365       response.DebugString(),
366       StrEq("BinaryHttpResponse(404){BinaryHttpMessage{Headers{Field{server="
367             "Apache}}Body{}}}"));
368 }
369 
TEST(BinaryHttpResponse,EncodeBody)370 TEST(BinaryHttpResponse, EncodeBody) {
371   /*
372     HTTP/1.1 200 OK
373     Server: Apache
374 
375     Hello, world!
376   */
377   BinaryHttpResponse response(200);
378   response.AddHeaderField({"Server", "Apache"});
379   response.set_body("Hello, world!\r\n");
380   /*
381     0140c80e 06736572 76657206 41706163  .@...server.Apac
382     68650f48 656c6c6f 2c20776f 726c6421  he.Hello, world!
383     0d0a                                 ....
384   */
385   const uint32_t expected_words[] = {0x0140c80e, 0x06736572, 0x76657206,
386                                      0x41706163, 0x68650f48, 0x656c6c6f,
387                                      0x2c20776f, 0x726c6421, 0x0d0a0000};
388   std::string expected;
389   for (const auto& word : expected_words) {
390     expected += WordToBytes(word);
391   }
392   // Remove padding.
393   expected.resize(expected.size() - 2);
394 
395   const auto result = response.Serialize();
396   ASSERT_TRUE(result.ok());
397   ASSERT_EQ(*result, expected);
398   EXPECT_THAT(
399       response.DebugString(),
400       StrEq("BinaryHttpResponse(200){BinaryHttpMessage{Headers{Field{server="
401             "Apache}}Body{Hello, world!\r\n}}}"));
402 }
403 
TEST(BinaryHttpResponse,DecodeBody)404 TEST(BinaryHttpResponse, DecodeBody) {
405   /*
406     HTTP/1.1 200 OK
407 
408     Hello, world!
409   */
410   const uint32_t words[] = {0x0140c80e, 0x06736572, 0x76657206,
411                             0x41706163, 0x68650f48, 0x656c6c6f,
412                             0x2c20776f, 0x726c6421, 0x0d0a0000};
413   std::string data;
414   for (const auto& word : words) {
415     data += WordToBytes(word);
416   }
417   const auto response_so = BinaryHttpResponse::Create(data);
418   ASSERT_TRUE(response_so.ok());
419   const BinaryHttpResponse response = *response_so;
420   ASSERT_EQ(response.status_code(), 200);
421   std::vector<BinaryHttpMessage::Field> expected_fields = {
422       {"server", "Apache"}};
423   ASSERT_THAT(response.GetHeaderFields(), ContainerEq(expected_fields));
424   ASSERT_EQ(response.body(), "Hello, world!\r\n");
425   ASSERT_TRUE(response.informational_responses().empty());
426   EXPECT_THAT(
427       response.DebugString(),
428       StrEq("BinaryHttpResponse(200){BinaryHttpMessage{Headers{Field{server="
429             "Apache}}Body{Hello, world!\r\n}}}"));
430 }
431 
TEST(BHttpResponse,AddBadInformationalResponseCode)432 TEST(BHttpResponse, AddBadInformationalResponseCode) {
433   BinaryHttpResponse response(200);
434   ASSERT_FALSE(response.AddInformationalResponse(50, {}).ok());
435   ASSERT_FALSE(response.AddInformationalResponse(300, {}).ok());
436 }
437 
TEST(BinaryHttpResponse,EncodeMultiInformationalWithBody)438 TEST(BinaryHttpResponse, EncodeMultiInformationalWithBody) {
439   /*
440     HTTP/1.1 102 Processing
441     Running: "sleep 15"
442 
443     HTTP/1.1 103 Early Hints
444     Link: </style.css>; rel=preload; as=style
445     Link: </script.js>; rel=preload; as=script
446 
447     HTTP/1.1 200 OK
448     Date: Mon, 27 Jul 2009 12:28:53 GMT
449     Server: Apache
450     Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
451     ETag: "34aa387-d-1568eb00"
452     Accept-Ranges: bytes
453     Content-Length: 51
454     Vary: Accept-Encoding
455     Content-Type: text/plain
456 
457     Hello World! My content includes a trailing CRLF.
458   */
459   BinaryHttpResponse response(200);
460   response.AddHeaderField({"Date", "Mon, 27 Jul 2009 12:28:53 GMT"})
461       ->AddHeaderField({"Server", "Apache"})
462       ->AddHeaderField({"Last-Modified", "Wed, 22 Jul 2009 19:15:56 GMT"})
463       ->AddHeaderField({"ETag", "\"34aa387-d-1568eb00\""})
464       ->AddHeaderField({"Accept-Ranges", "bytes"})
465       ->AddHeaderField({"Content-Length", "51"})
466       ->AddHeaderField({"Vary", "Accept-Encoding"})
467       ->AddHeaderField({"Content-Type", "text/plain"});
468   response.set_body("Hello World! My content includes a trailing CRLF.\r\n");
469   ASSERT_TRUE(
470       response.AddInformationalResponse(102, {{"Running", "\"sleep 15\""}})
471           .ok());
472   ASSERT_TRUE(response
473                   .AddInformationalResponse(
474                       103, {{"Link", "</style.css>; rel=preload; as=style"},
475                             {"Link", "</script.js>; rel=preload; as=script"}})
476                   .ok());
477 
478   /*
479       01406613 0772756e 6e696e67 0a22736c  .@f..running."sl
480       65657020 31352240 67405304 6c696e6b  eep 15"@g@S.link
481       233c2f73 74796c65 2e637373 3e3b2072  #</style.css>; r
482       656c3d70 72656c6f 61643b20 61733d73  el=preload; as=s
483       74796c65 046c696e 6b243c2f 73637269  tyle.link$</scri
484       70742e6a 733e3b20 72656c3d 7072656c  pt.js>; rel=prel
485       6f61643b 2061733d 73637269 707440c8  oad; as=script@.
486       40ca0464 6174651d 4d6f6e2c 20323720  @..date.Mon, 27
487       4a756c20 32303039 2031323a 32383a35  Jul 2009 12:28:5
488       3320474d 54067365 72766572 06417061  3 GMT.server.Apa
489       6368650d 6c617374 2d6d6f64 69666965  che.last-modifie
490       641d5765 642c2032 32204a75 6c203230  d.Wed, 22 Jul 20
491       30392031 393a3135 3a353620 474d5404  09 19:15:56 GMT.
492       65746167 14223334 61613338 372d642d  etag."34aa387-d-
493       31353638 65623030 220d6163 63657074  1568eb00".accept
494       2d72616e 67657305 62797465 730e636f  -ranges.bytes.co
495       6e74656e 742d6c65 6e677468 02353104  ntent-length.51.
496       76617279 0f416363 6570742d 456e636f  vary.Accept-Enco
497       64696e67 0c636f6e 74656e74 2d747970  ding.content-typ
498       650a7465 78742f70 6c61696e 3348656c  e.text/plain3Hel
499       6c6f2057 6f726c64 21204d79 20636f6e  lo World! My con
500       74656e74 20696e63 6c756465 73206120  tent includes a
501       74726169 6c696e67 2043524c 462e0d0a  trailing CRLF...
502   */
503   const uint32_t expected_words[] = {
504       0x01406613, 0x0772756e, 0x6e696e67, 0x0a22736c, 0x65657020, 0x31352240,
505       0x67405304, 0x6c696e6b, 0x233c2f73, 0x74796c65, 0x2e637373, 0x3e3b2072,
506       0x656c3d70, 0x72656c6f, 0x61643b20, 0x61733d73, 0x74796c65, 0x046c696e,
507       0x6b243c2f, 0x73637269, 0x70742e6a, 0x733e3b20, 0x72656c3d, 0x7072656c,
508       0x6f61643b, 0x2061733d, 0x73637269, 0x707440c8, 0x40ca0464, 0x6174651d,
509       0x4d6f6e2c, 0x20323720, 0x4a756c20, 0x32303039, 0x2031323a, 0x32383a35,
510       0x3320474d, 0x54067365, 0x72766572, 0x06417061, 0x6368650d, 0x6c617374,
511       0x2d6d6f64, 0x69666965, 0x641d5765, 0x642c2032, 0x32204a75, 0x6c203230,
512       0x30392031, 0x393a3135, 0x3a353620, 0x474d5404, 0x65746167, 0x14223334,
513       0x61613338, 0x372d642d, 0x31353638, 0x65623030, 0x220d6163, 0x63657074,
514       0x2d72616e, 0x67657305, 0x62797465, 0x730e636f, 0x6e74656e, 0x742d6c65,
515       0x6e677468, 0x02353104, 0x76617279, 0x0f416363, 0x6570742d, 0x456e636f,
516       0x64696e67, 0x0c636f6e, 0x74656e74, 0x2d747970, 0x650a7465, 0x78742f70,
517       0x6c61696e, 0x3348656c, 0x6c6f2057, 0x6f726c64, 0x21204d79, 0x20636f6e,
518       0x74656e74, 0x20696e63, 0x6c756465, 0x73206120, 0x74726169, 0x6c696e67,
519       0x2043524c, 0x462e0d0a};
520   std::string expected;
521   for (const auto& word : expected_words) {
522     expected += WordToBytes(word);
523   }
524   const auto result = response.Serialize();
525   ASSERT_TRUE(result.ok());
526   ASSERT_EQ(*result, expected);
527   EXPECT_THAT(
528       response.DebugString(),
529       StrEq(
530           "BinaryHttpResponse(200){BinaryHttpMessage{Headers{Field{date=Mon, "
531           "27 Jul 2009 12:28:53 "
532           "GMT};Field{server=Apache};Field{last-modified=Wed, 22 Jul 2009 "
533           "19:15:56 "
534           "GMT};Field{etag=\"34aa387-d-1568eb00\"};Field{accept-ranges=bytes};"
535           "Field{"
536           "content-length=51};Field{vary=Accept-Encoding};Field{content-type="
537           "text/plain}}Body{Hello World! My content includes a trailing "
538           "CRLF.\r\n}}InformationalResponse{Field{running=\"sleep "
539           "15\"}};InformationalResponse{Field{link=</style.css>; rel=preload; "
540           "as=style};Field{link=</script.js>; rel=preload; as=script}}}"));
541   TestPrintTo(response);
542 }
543 
TEST(BinaryHttpResponse,DecodeMultiInformationalWithBody)544 TEST(BinaryHttpResponse, DecodeMultiInformationalWithBody) {
545   /*
546     HTTP/1.1 102 Processing
547     Running: "sleep 15"
548 
549     HTTP/1.1 103 Early Hints
550     Link: </style.css>; rel=preload; as=style
551     Link: </script.js>; rel=preload; as=script
552 
553     HTTP/1.1 200 OK
554     Date: Mon, 27 Jul 2009 12:28:53 GMT
555     Server: Apache
556     Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
557     ETag: "34aa387-d-1568eb00"
558     Accept-Ranges: bytes
559     Content-Length: 51
560     Vary: Accept-Encoding
561     Content-Type: text/plain
562 
563     Hello World! My content includes a trailing CRLF.
564   */
565   const uint32_t words[] = {
566       0x01406613, 0x0772756e, 0x6e696e67, 0x0a22736c, 0x65657020, 0x31352240,
567       0x67405304, 0x6c696e6b, 0x233c2f73, 0x74796c65, 0x2e637373, 0x3e3b2072,
568       0x656c3d70, 0x72656c6f, 0x61643b20, 0x61733d73, 0x74796c65, 0x046c696e,
569       0x6b243c2f, 0x73637269, 0x70742e6a, 0x733e3b20, 0x72656c3d, 0x7072656c,
570       0x6f61643b, 0x2061733d, 0x73637269, 0x707440c8, 0x40ca0464, 0x6174651d,
571       0x4d6f6e2c, 0x20323720, 0x4a756c20, 0x32303039, 0x2031323a, 0x32383a35,
572       0x3320474d, 0x54067365, 0x72766572, 0x06417061, 0x6368650d, 0x6c617374,
573       0x2d6d6f64, 0x69666965, 0x641d5765, 0x642c2032, 0x32204a75, 0x6c203230,
574       0x30392031, 0x393a3135, 0x3a353620, 0x474d5404, 0x65746167, 0x14223334,
575       0x61613338, 0x372d642d, 0x31353638, 0x65623030, 0x220d6163, 0x63657074,
576       0x2d72616e, 0x67657305, 0x62797465, 0x730e636f, 0x6e74656e, 0x742d6c65,
577       0x6e677468, 0x02353104, 0x76617279, 0x0f416363, 0x6570742d, 0x456e636f,
578       0x64696e67, 0x0c636f6e, 0x74656e74, 0x2d747970, 0x650a7465, 0x78742f70,
579       0x6c61696e, 0x3348656c, 0x6c6f2057, 0x6f726c64, 0x21204d79, 0x20636f6e,
580       0x74656e74, 0x20696e63, 0x6c756465, 0x73206120, 0x74726169, 0x6c696e67,
581       0x2043524c, 0x462e0d0a, 0x00000000};
582   std::string data;
583   for (const auto& word : words) {
584     data += WordToBytes(word);
585   }
586   const auto response_so = BinaryHttpResponse::Create(data);
587   ASSERT_TRUE(response_so.ok());
588   const BinaryHttpResponse response = *response_so;
589   std::vector<BinaryHttpMessage::Field> expected_fields = {
590       {"date", "Mon, 27 Jul 2009 12:28:53 GMT"},
591       {"server", "Apache"},
592       {"last-modified", "Wed, 22 Jul 2009 19:15:56 GMT"},
593       {"etag", "\"34aa387-d-1568eb00\""},
594       {"accept-ranges", "bytes"},
595       {"content-length", "51"},
596       {"vary", "Accept-Encoding"},
597       {"content-type", "text/plain"}};
598 
599   ASSERT_THAT(response.GetHeaderFields(), ContainerEq(expected_fields));
600   ASSERT_EQ(response.body(),
601             "Hello World! My content includes a trailing CRLF.\r\n");
602   std::vector<BinaryHttpMessage::Field> header102 = {
603       {"running", "\"sleep 15\""}};
604   std::vector<BinaryHttpMessage::Field> header103 = {
605       {"link", "</style.css>; rel=preload; as=style"},
606       {"link", "</script.js>; rel=preload; as=script"}};
607   std::vector<BinaryHttpResponse::InformationalResponse> expected_control = {
608       {102, header102}, {103, header103}};
609   ASSERT_THAT(response.informational_responses(),
610               ContainerEq(expected_control));
611   EXPECT_THAT(
612       response.DebugString(),
613       StrEq(
614           "BinaryHttpResponse(200){BinaryHttpMessage{Headers{Field{date=Mon, "
615           "27 Jul 2009 12:28:53 "
616           "GMT};Field{server=Apache};Field{last-modified=Wed, 22 Jul 2009 "
617           "19:15:56 "
618           "GMT};Field{etag=\"34aa387-d-1568eb00\"};Field{accept-ranges=bytes};"
619           "Field{"
620           "content-length=51};Field{vary=Accept-Encoding};Field{content-type="
621           "text/plain}}Body{Hello World! My content includes a trailing "
622           "CRLF.\r\n}}InformationalResponse{Field{running=\"sleep "
623           "15\"}};InformationalResponse{Field{link=</style.css>; rel=preload; "
624           "as=style};Field{link=</script.js>; rel=preload; as=script}}}"));
625   TestPrintTo(response);
626 }
627 
TEST(BinaryHttpMessage,SwapBody)628 TEST(BinaryHttpMessage, SwapBody) {
629   BinaryHttpRequest request({});
630   request.set_body("hello, world!");
631   std::string other = "goodbye, world!";
632   request.swap_body(other);
633   EXPECT_EQ(request.body(), "goodbye, world!");
634   EXPECT_EQ(other, "hello, world!");
635 }
636 
TEST(BinaryHttpResponse,Equality)637 TEST(BinaryHttpResponse, Equality) {
638   BinaryHttpResponse response(200);
639   response.AddHeaderField({"Server", "Apache"})->set_body("Hello, world!\r\n");
640   ASSERT_TRUE(
641       response.AddInformationalResponse(102, {{"Running", "\"sleep 15\""}})
642           .ok());
643 
644   BinaryHttpResponse same(200);
645   same.AddHeaderField({"Server", "Apache"})->set_body("Hello, world!\r\n");
646   ASSERT_TRUE(
647       same.AddInformationalResponse(102, {{"Running", "\"sleep 15\""}}).ok());
648   ASSERT_EQ(response, same);
649 }
650 
TEST(BinaryHttpResponse,Inequality)651 TEST(BinaryHttpResponse, Inequality) {
652   BinaryHttpResponse response(200);
653   response.AddHeaderField({"Server", "Apache"})->set_body("Hello, world!\r\n");
654   ASSERT_TRUE(
655       response.AddInformationalResponse(102, {{"Running", "\"sleep 15\""}})
656           .ok());
657 
658   BinaryHttpResponse different_status(201);
659   different_status.AddHeaderField({"Server", "Apache"})
660       ->set_body("Hello, world!\r\n");
661   EXPECT_TRUE(different_status
662                   .AddInformationalResponse(102, {{"Running", "\"sleep 15\""}})
663                   .ok());
664   EXPECT_NE(response, different_status);
665 
666   BinaryHttpResponse different_header(200);
667   different_header.AddHeaderField({"Server", "python3"})
668       ->set_body("Hello, world!\r\n");
669   EXPECT_TRUE(different_header
670                   .AddInformationalResponse(102, {{"Running", "\"sleep 15\""}})
671                   .ok());
672   EXPECT_NE(response, different_header);
673 
674   BinaryHttpResponse no_header(200);
675   no_header.set_body("Hello, world!\r\n");
676   EXPECT_TRUE(
677       no_header.AddInformationalResponse(102, {{"Running", "\"sleep 15\""}})
678           .ok());
679   EXPECT_NE(response, no_header);
680 
681   BinaryHttpResponse different_body(200);
682   different_body.AddHeaderField({"Server", "Apache"})
683       ->set_body("Goodbye, world!\r\n");
684   EXPECT_TRUE(different_body
685                   .AddInformationalResponse(102, {{"Running", "\"sleep 15\""}})
686                   .ok());
687   EXPECT_NE(response, different_body);
688 
689   BinaryHttpResponse no_body(200);
690   no_body.AddHeaderField({"Server", "Apache"});
691   EXPECT_TRUE(
692       no_body.AddInformationalResponse(102, {{"Running", "\"sleep 15\""}})
693           .ok());
694   EXPECT_NE(response, no_body);
695 
696   BinaryHttpResponse different_informational(200);
697   different_informational.AddHeaderField({"Server", "Apache"})
698       ->set_body("Hello, world!\r\n");
699   EXPECT_TRUE(different_informational
700                   .AddInformationalResponse(198, {{"Running", "\"sleep 15\""}})
701                   .ok());
702   EXPECT_NE(response, different_informational);
703 
704   BinaryHttpResponse no_informational(200);
705   no_informational.AddHeaderField({"Server", "Apache"})
706       ->set_body("Hello, world!\r\n");
707   EXPECT_NE(response, no_informational);
708 }
709 
710 MATCHER_P(HasEqPayload, value, "Payloads of messages are equivalent.") {
711   return arg.IsPayloadEqual(value);
712 }
713 
714 template <typename T>
TestPadding(T & message)715 void TestPadding(T& message) {
716   const auto data_so = message.Serialize();
717   ASSERT_TRUE(data_so.ok());
718   auto data = *data_so;
719   ASSERT_EQ(data.size(), message.EncodedSize());
720 
721   message.set_num_padding_bytes(10);
722   const auto padded_data_so = message.Serialize();
723   ASSERT_TRUE(padded_data_so.ok());
724   const auto padded_data = *padded_data_so;
725   ASSERT_EQ(padded_data.size(), message.EncodedSize());
726 
727   // Check padding size output.
728   ASSERT_EQ(data.size() + 10, padded_data.size());
729   // Check for valid null byte padding output
730   data.resize(data.size() + 10);
731   ASSERT_EQ(data, padded_data);
732 
733   // Deserialize padded and not padded, and verify they are the same.
734   const auto deserialized_padded_message_so = T::Create(data);
735   ASSERT_TRUE(deserialized_padded_message_so.ok());
736   const auto deserialized_padded_message = *deserialized_padded_message_so;
737   ASSERT_EQ(deserialized_padded_message, message);
738   ASSERT_EQ(deserialized_padded_message.num_padding_bytes(), size_t(10));
739 
740   // Invalid padding
741   data[data.size() - 1] = 'a';
742   const auto bad_so = T::Create(data);
743   ASSERT_FALSE(bad_so.ok());
744 
745   // Check that padding does not impact equality.
746   data.resize(data.size() - 10);
747   const auto deserialized_message_so = T::Create(data);
748   ASSERT_TRUE(deserialized_message_so.ok());
749   const auto deserialized_message = *deserialized_message_so;
750   ASSERT_EQ(deserialized_message.num_padding_bytes(), size_t(0));
751   // Confirm that the message payloads are equal, but not fully equivalent due
752   // to padding.
753   ASSERT_THAT(deserialized_message, HasEqPayload(deserialized_padded_message));
754   ASSERT_NE(deserialized_message, deserialized_padded_message);
755 }
756 
TEST(BinaryHttpRequest,Padding)757 TEST(BinaryHttpRequest, Padding) {
758   /*
759     GET /hello.txt HTTP/1.1
760     User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
761     Host: www.example.com
762     Accept-Language: en, mi
763   */
764   BinaryHttpRequest request({"GET", "https", "", "/hello.txt"});
765   request
766       .AddHeaderField({"User-Agent",
767                        "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"})
768       ->AddHeaderField({"Host", "www.example.com"})
769       ->AddHeaderField({"Accept-Language", "en, mi"});
770   TestPadding(request);
771 }
772 
TEST(BinaryHttpResponse,Padding)773 TEST(BinaryHttpResponse, Padding) {
774   /*
775     HTTP/1.1 200 OK
776     Server: Apache
777 
778     Hello, world!
779   */
780   BinaryHttpResponse response(200);
781   response.AddHeaderField({"Server", "Apache"});
782   response.set_body("Hello, world!\r\n");
783   TestPadding(response);
784 }
785 
786 }  // namespace quiche
787