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