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