Imported Upstream version 1.0.0
[platform/upstream/nghttp2.git] / src / http2_test.cc
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2013 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "http2_test.h"
26
27 #include <cassert>
28 #include <cstring>
29 #include <iostream>
30
31 #include <CUnit/CUnit.h>
32
33 #include "http-parser/http_parser.h"
34
35 #include "http2.h"
36 #include "util.h"
37
38 using namespace nghttp2;
39
40 #define MAKE_NV(K, V)                                                          \
41   {                                                                            \
42     (uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1,                 \
43         NGHTTP2_NV_FLAG_NONE                                                   \
44   }
45
46 namespace shrpx {
47
48 namespace {
49 void check_nv(const Header &a, const nghttp2_nv *b) {
50   CU_ASSERT(a.name.size() == b->namelen);
51   CU_ASSERT(a.value.size() == b->valuelen);
52   CU_ASSERT(memcmp(a.name.c_str(), b->name, b->namelen) == 0);
53   CU_ASSERT(memcmp(a.value.c_str(), b->value, b->valuelen) == 0);
54 }
55 } // namespace
56
57 void test_http2_add_header(void) {
58   auto nva = Headers();
59
60   http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"123", 3,
61                     false, -1);
62   CU_ASSERT(Headers::value_type("alpha", "123") == nva[0]);
63   CU_ASSERT(!nva[0].no_index);
64
65   nva.clear();
66
67   http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"", 0,
68                     true, -1);
69   CU_ASSERT(Headers::value_type("alpha", "") == nva[0]);
70   CU_ASSERT(nva[0].no_index);
71
72   nva.clear();
73
74   http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b", 2,
75                     false, -1);
76   CU_ASSERT(Headers::value_type("a", "b") == nva[0]);
77
78   nva.clear();
79
80   http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"b ", 2,
81                     false, -1);
82   CU_ASSERT(Headers::value_type("a", "b") == nva[0]);
83
84   nva.clear();
85
86   http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"  b  ", 5,
87                     false, -1);
88   CU_ASSERT(Headers::value_type("a", "b") == nva[0]);
89
90   nva.clear();
91
92   http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"  bravo  ",
93                     9, false, -1);
94   CU_ASSERT(Headers::value_type("a", "bravo") == nva[0]);
95
96   nva.clear();
97
98   http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"    ", 4,
99                     false, -1);
100   CU_ASSERT(Headers::value_type("a", "") == nva[0]);
101
102   nva.clear();
103
104   http2::add_header(nva, (const uint8_t *)"te", 2, (const uint8_t *)"trailers",
105                     8, false, http2::HD_TE);
106   CU_ASSERT(http2::HD_TE == nva[0].token);
107 }
108
109 void test_http2_get_header(void) {
110   auto nva = Headers{{"alpha", "1"},
111                      {"bravo", "2"},
112                      {"bravo", "3"},
113                      {"charlie", "4"},
114                      {"delta", "5"},
115                      {"echo", "6"},
116                      {"content-length", "7"}};
117   const Headers::value_type *rv;
118   rv = http2::get_header(nva, "delta");
119   CU_ASSERT(rv != nullptr);
120   CU_ASSERT("delta" == rv->name);
121
122   rv = http2::get_header(nva, "bravo");
123   CU_ASSERT(rv != nullptr);
124   CU_ASSERT("bravo" == rv->name);
125
126   rv = http2::get_header(nva, "foxtrot");
127   CU_ASSERT(rv == nullptr);
128
129   http2::HeaderIndex hdidx;
130   http2::init_hdidx(hdidx);
131   hdidx[http2::HD_CONTENT_LENGTH] = 6;
132   rv = http2::get_header(hdidx, http2::HD_CONTENT_LENGTH, nva);
133   CU_ASSERT("content-length" == rv->name);
134 }
135
136 namespace {
137 auto headers =
138     Headers{{"alpha", "0", true},
139             {"bravo", "1"},
140             {"connection", "2", false, http2::HD_CONNECTION},
141             {"connection", "3", false, http2::HD_CONNECTION},
142             {"delta", "4"},
143             {"expect", "5"},
144             {"foxtrot", "6"},
145             {"tango", "7"},
146             {"te", "8", false, http2::HD_TE},
147             {"te", "9", false, http2::HD_TE},
148             {"x-forwarded-proto", "10", false, http2::HD_X_FORWARDED_FOR},
149             {"x-forwarded-proto", "11", false, http2::HD_X_FORWARDED_FOR},
150             {"zulu", "12"}};
151 } // namespace
152
153 void test_http2_copy_headers_to_nva(void) {
154   std::vector<nghttp2_nv> nva;
155   http2::copy_headers_to_nva(nva, headers);
156   CU_ASSERT(7 == nva.size());
157   auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 12};
158   for (size_t i = 0; i < ans.size(); ++i) {
159     check_nv(headers[ans[i]], &nva[i]);
160
161     if (ans[i] == 0) {
162       CU_ASSERT(nva[i].flags & NGHTTP2_NV_FLAG_NO_INDEX);
163     } else {
164       CU_ASSERT(NGHTTP2_NV_FLAG_NONE == nva[i].flags);
165     }
166   }
167 }
168
169 void test_http2_build_http1_headers_from_headers(void) {
170   std::string hdrs;
171   http2::build_http1_headers_from_headers(hdrs, headers);
172   CU_ASSERT(hdrs == "Alpha: 0\r\n"
173                     "Bravo: 1\r\n"
174                     "Delta: 4\r\n"
175                     "Expect: 5\r\n"
176                     "Foxtrot: 6\r\n"
177                     "Tango: 7\r\n"
178                     "Te: 8\r\n"
179                     "Te: 9\r\n"
180                     "Zulu: 12\r\n");
181 }
182
183 void test_http2_lws(void) {
184   CU_ASSERT(!http2::lws("alpha"));
185   CU_ASSERT(http2::lws(" "));
186   CU_ASSERT(http2::lws(""));
187 }
188
189 namespace {
190 void check_rewrite_location_uri(const std::string &want, const std::string &uri,
191                                 const std::string &match_host,
192                                 const std::string &req_authority,
193                                 const std::string &upstream_scheme) {
194   http_parser_url u;
195   memset(&u, 0, sizeof(u));
196   CU_ASSERT(0 == http_parser_parse_url(uri.c_str(), uri.size(), 0, &u));
197   auto got = http2::rewrite_location_uri(uri, u, match_host, req_authority,
198                                          upstream_scheme);
199   CU_ASSERT(want == got);
200 }
201 } // namespace
202
203 void test_http2_rewrite_location_uri(void) {
204   check_rewrite_location_uri("https://localhost:3000/alpha?bravo#charlie",
205                              "http://localhost:3001/alpha?bravo#charlie",
206                              "localhost:3001", "localhost:3000", "https");
207   check_rewrite_location_uri("https://localhost/", "http://localhost:3001/",
208                              "localhost", "localhost", "https");
209   check_rewrite_location_uri("http://localhost/", "http://localhost:3001/",
210                              "localhost", "localhost", "http");
211   check_rewrite_location_uri("http://localhost:443/", "http://localhost:3001/",
212                              "localhost", "localhost:443", "http");
213   check_rewrite_location_uri("https://localhost:80/", "http://localhost:3001/",
214                              "localhost", "localhost:80", "https");
215   check_rewrite_location_uri("", "http://localhost:3001/", "127.0.0.1",
216                              "127.0.0.1", "https");
217   check_rewrite_location_uri("https://localhost:3000/",
218                              "http://localhost:3001/", "localhost",
219                              "localhost:3000", "https");
220   check_rewrite_location_uri("https://localhost:3000/", "http://localhost/",
221                              "localhost", "localhost:3000", "https");
222
223   // match_host != req_authority
224   check_rewrite_location_uri("https://example.org", "http://127.0.0.1:8080",
225                              "127.0.0.1", "example.org", "https");
226   check_rewrite_location_uri("", "http://example.org", "127.0.0.1",
227                              "example.org", "https");
228 }
229
230 void test_http2_parse_http_status_code(void) {
231   CU_ASSERT(200 == http2::parse_http_status_code("200"));
232   CU_ASSERT(102 == http2::parse_http_status_code("102"));
233   CU_ASSERT(-1 == http2::parse_http_status_code("099"));
234   CU_ASSERT(-1 == http2::parse_http_status_code("99"));
235   CU_ASSERT(-1 == http2::parse_http_status_code("-1"));
236   CU_ASSERT(-1 == http2::parse_http_status_code("20a"));
237   CU_ASSERT(-1 == http2::parse_http_status_code(""));
238 }
239
240 void test_http2_index_header(void) {
241   http2::HeaderIndex hdidx;
242   http2::init_hdidx(hdidx);
243
244   http2::index_header(hdidx, http2::HD__AUTHORITY, 0);
245   http2::index_header(hdidx, -1, 1);
246
247   CU_ASSERT(0 == hdidx[http2::HD__AUTHORITY]);
248 }
249
250 void test_http2_lookup_token(void) {
251   CU_ASSERT(http2::HD__AUTHORITY == http2::lookup_token(":authority"));
252   CU_ASSERT(-1 == http2::lookup_token(":authorit"));
253   CU_ASSERT(-1 == http2::lookup_token(":Authority"));
254   CU_ASSERT(http2::HD_EXPECT == http2::lookup_token("expect"));
255 }
256
257 void test_http2_check_http2_pseudo_header(void) {
258   http2::HeaderIndex hdidx;
259   http2::init_hdidx(hdidx);
260
261   CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD));
262   hdidx[http2::HD__PATH] = 0;
263   CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD));
264   hdidx[http2::HD__METHOD] = 1;
265   CU_ASSERT(
266       !http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD));
267   CU_ASSERT(!http2::check_http2_request_pseudo_header(hdidx, http2::HD_VIA));
268
269   http2::init_hdidx(hdidx);
270
271   CU_ASSERT(
272       http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS));
273   hdidx[http2::HD__STATUS] = 0;
274   CU_ASSERT(
275       !http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS));
276   CU_ASSERT(!http2::check_http2_response_pseudo_header(hdidx, http2::HD_VIA));
277 }
278
279 void test_http2_http2_header_allowed(void) {
280   CU_ASSERT(http2::http2_header_allowed(http2::HD__PATH));
281   CU_ASSERT(http2::http2_header_allowed(http2::HD_CONTENT_LENGTH));
282   CU_ASSERT(!http2::http2_header_allowed(http2::HD_CONNECTION));
283 }
284
285 void test_http2_mandatory_request_headers_presence(void) {
286   http2::HeaderIndex hdidx;
287   http2::init_hdidx(hdidx);
288
289   CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
290   hdidx[http2::HD__AUTHORITY] = 0;
291   CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
292   hdidx[http2::HD__METHOD] = 1;
293   CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
294   hdidx[http2::HD__PATH] = 2;
295   CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
296   hdidx[http2::HD__SCHEME] = 3;
297   CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx));
298
299   hdidx[http2::HD__AUTHORITY] = -1;
300   hdidx[http2::HD_HOST] = 0;
301   CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx));
302 }
303
304 void test_http2_parse_link_header(void) {
305   {
306     // only URI appears; we don't extract URI unless it bears rel=preload
307     const char s[] = "<url>";
308     auto res = http2::parse_link_header(s, sizeof(s) - 1);
309     CU_ASSERT(0 == res.size());
310   }
311   {
312     // URI url should be extracted
313     const char s[] = "<url>; rel=preload";
314     auto res = http2::parse_link_header(s, sizeof(s) - 1);
315     CU_ASSERT(1 == res.size());
316     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
317   }
318   {
319     // With extra link-param.  URI url should be extracted
320     const char s[] = "<url>; rel=preload; as=file";
321     auto res = http2::parse_link_header(s, sizeof(s) - 1);
322     CU_ASSERT(1 == res.size());
323     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
324   }
325   {
326     // With extra link-param.  URI url should be extracted
327     const char s[] = "<url>; as=file; rel=preload";
328     auto res = http2::parse_link_header(s, sizeof(s) - 1);
329     CU_ASSERT(1 == res.size());
330     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
331   }
332   {
333     // With extra link-param and quote-string.  URI url should be
334     // extracted
335     const char s[] = R"(<url>; rel=preload; title="foo,bar")";
336     auto res = http2::parse_link_header(s, sizeof(s) - 1);
337     CU_ASSERT(1 == res.size());
338     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
339   }
340   {
341     // With extra link-param and quote-string.  URI url should be
342     // extracted
343     const char s[] = R"(<url>; title="foo,bar"; rel=preload)";
344     auto res = http2::parse_link_header(s, sizeof(s) - 1);
345     CU_ASSERT(1 == res.size());
346     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
347   }
348   {
349     // ',' after quote-string
350     const char s[] = R"(<url>; title="foo,bar", <url>; rel=preload)";
351     auto res = http2::parse_link_header(s, sizeof(s) - 1);
352     CU_ASSERT(1 == res.size());
353     CU_ASSERT(std::make_pair(&s[25], &s[28]) == res[0].uri);
354   }
355   {
356     // Only first URI should be extracted.
357     const char s[] = "<url>; rel=preload, <url>";
358     auto res = http2::parse_link_header(s, sizeof(s) - 1);
359     CU_ASSERT(1 == res.size());
360     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
361   }
362   {
363     // Both have rel=preload, so both urls should be extracted
364     const char s[] = "<url>; rel=preload, <url>; rel=preload";
365     auto res = http2::parse_link_header(s, sizeof(s) - 1);
366     CU_ASSERT(2 == res.size());
367     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
368     CU_ASSERT(std::make_pair(&s[21], &s[24]) == res[1].uri);
369   }
370   {
371     // Second URI uri should be extracted.
372     const char s[] = "<url>, <url>;rel=preload";
373     auto res = http2::parse_link_header(s, sizeof(s) - 1);
374     CU_ASSERT(1 == res.size());
375     CU_ASSERT(std::make_pair(&s[8], &s[11]) == res[0].uri);
376   }
377   {
378     // Error if input ends with ';'
379     const char s[] = "<url>;rel=preload;";
380     auto res = http2::parse_link_header(s, sizeof(s) - 1);
381     CU_ASSERT(0 == res.size());
382   }
383   {
384     // OK if input ends with ','
385     const char s[] = "<url>;rel=preload,";
386     auto res = http2::parse_link_header(s, sizeof(s) - 1);
387     CU_ASSERT(1 == res.size());
388     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
389   }
390   {
391     // Multiple repeated ','s between fields is OK
392     const char s[] = "<url>,,,<url>;rel=preload";
393     auto res = http2::parse_link_header(s, sizeof(s) - 1);
394     CU_ASSERT(1 == res.size());
395     CU_ASSERT(std::make_pair(&s[9], &s[12]) == res[0].uri);
396   }
397   {
398     // Error if url is not enclosed by <>
399     const char s[] = "url>;rel=preload;";
400     auto res = http2::parse_link_header(s, sizeof(s) - 1);
401     CU_ASSERT(0 == res.size());
402   }
403   {
404     // Error if url is not enclosed by <>
405     const char s[] = "<url;rel=preload;";
406     auto res = http2::parse_link_header(s, sizeof(s) - 1);
407     CU_ASSERT(0 == res.size());
408   }
409   {
410     // Empty parameter value is not allowed
411     const char s[] = "<url>;rel=preload; as=";
412     auto res = http2::parse_link_header(s, sizeof(s) - 1);
413     CU_ASSERT(0 == res.size());
414   }
415   {
416     // Empty parameter value is not allowed
417     const char s[] = "<url>;as=;rel=preload";
418     auto res = http2::parse_link_header(s, sizeof(s) - 1);
419     CU_ASSERT(0 == res.size());
420   }
421   {
422     // Empty parameter value is not allowed
423     const char s[] = "<url>;as=, <url>;rel=preload";
424     auto res = http2::parse_link_header(s, sizeof(s) - 1);
425     CU_ASSERT(0 == res.size());
426   }
427   {
428     // Empty parameter name is not allowed
429     const char s[] = "<url>; =file; rel=preload";
430     auto res = http2::parse_link_header(s, sizeof(s) - 1);
431     CU_ASSERT(0 == res.size());
432   }
433   {
434     // Without whitespaces
435     const char s[] = "<url>;as=file;rel=preload,<url>;rel=preload";
436     auto res = http2::parse_link_header(s, sizeof(s) - 1);
437     CU_ASSERT(2 == res.size());
438     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
439     CU_ASSERT(std::make_pair(&s[27], &s[30]) == res[1].uri);
440   }
441   {
442     // link-extension may have no value
443     const char s[] = "<url>; as; rel=preload";
444     auto res = http2::parse_link_header(s, sizeof(s) - 1);
445     CU_ASSERT(1 == res.size());
446     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
447   }
448   {
449     // ext-name-star
450     const char s[] = "<url>; foo*=bar; rel=preload";
451     auto res = http2::parse_link_header(s, sizeof(s) - 1);
452     CU_ASSERT(1 == res.size());
453     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
454   }
455   {
456     // '*' is not allowed expect for trailing one
457     const char s[] = "<url>; *=bar; rel=preload";
458     auto res = http2::parse_link_header(s, sizeof(s) - 1);
459     CU_ASSERT(0 == res.size());
460   }
461   {
462     // '*' is not allowed expect for trailing one
463     const char s[] = "<url>; foo*bar=buzz; rel=preload";
464     auto res = http2::parse_link_header(s, sizeof(s) - 1);
465     CU_ASSERT(0 == res.size());
466   }
467   {
468     // ext-name-star must be followed by '='
469     const char s[] = "<url>; foo*; rel=preload";
470     auto res = http2::parse_link_header(s, sizeof(s) - 1);
471     CU_ASSERT(0 == res.size());
472   }
473   {
474     // '>' is not followed by ';'
475     const char s[] = "<url> rel=preload";
476     auto res = http2::parse_link_header(s, sizeof(s) - 1);
477     CU_ASSERT(0 == res.size());
478   }
479   {
480     // Starting with whitespace is no problem.
481     const char s[] = "  <url>; rel=preload";
482     auto res = http2::parse_link_header(s, sizeof(s) - 1);
483     CU_ASSERT(1 == res.size());
484     CU_ASSERT(std::make_pair(&s[3], &s[6]) == res[0].uri);
485   }
486   {
487     // preload is a prefix of bogus rel parameter value
488     const char s[] = "<url>; rel=preloadx";
489     auto res = http2::parse_link_header(s, sizeof(s) - 1);
490     CU_ASSERT(0 == res.size());
491   }
492   {
493     // preload in relation-types list
494     const char s[] = R"(<url>; rel="preload")";
495     auto res = http2::parse_link_header(s, sizeof(s) - 1);
496     CU_ASSERT(1 == res.size());
497     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
498   }
499   {
500     // preload in relation-types list followed by another parameter
501     const char s[] = R"(<url>; rel="preload foo")";
502     auto res = http2::parse_link_header(s, sizeof(s) - 1);
503     CU_ASSERT(1 == res.size());
504     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
505   }
506   {
507     // preload in relation-types list following another parameter
508     const char s[] = R"(<url>; rel="foo preload")";
509     auto res = http2::parse_link_header(s, sizeof(s) - 1);
510     CU_ASSERT(1 == res.size());
511     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
512   }
513   {
514     // preload in relation-types list between other parameters
515     const char s[] = R"(<url>; rel="foo preload bar")";
516     auto res = http2::parse_link_header(s, sizeof(s) - 1);
517     CU_ASSERT(1 == res.size());
518     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
519   }
520   {
521     // preload in relation-types list between other parameters
522     const char s[] = R"(<url>; rel="foo   preload   bar")";
523     auto res = http2::parse_link_header(s, sizeof(s) - 1);
524     CU_ASSERT(1 == res.size());
525     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
526   }
527   {
528     // no preload in relation-types list
529     const char s[] = R"(<url>; rel="foo")";
530     auto res = http2::parse_link_header(s, sizeof(s) - 1);
531     CU_ASSERT(0 == res.size());
532   }
533   {
534     // no preload in relation-types list, multiple unrelated elements.
535     const char s[] = R"(<url>; rel="foo bar")";
536     auto res = http2::parse_link_header(s, sizeof(s) - 1);
537     CU_ASSERT(0 == res.size());
538   }
539   {
540     // preload in relation-types list, followed by another link-value.
541     const char s[] = R"(<url>; rel="preload", <url>)";
542     auto res = http2::parse_link_header(s, sizeof(s) - 1);
543     CU_ASSERT(1 == res.size());
544     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
545   }
546   {
547     // preload in relation-types list, following another link-value.
548     const char s[] = R"(<url>, <url>; rel="preload")";
549     auto res = http2::parse_link_header(s, sizeof(s) - 1);
550     CU_ASSERT(1 == res.size());
551     CU_ASSERT(std::make_pair(&s[8], &s[11]) == res[0].uri);
552   }
553   {
554     // preload in relation-types list, followed by another link-param.
555     const char s[] = R"(<url>; rel="preload"; as="font")";
556     auto res = http2::parse_link_header(s, sizeof(s) - 1);
557     CU_ASSERT(1 == res.size());
558     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
559   }
560   {
561     // preload in relation-types list, followed by character other
562     // than ';' or ','
563     const char s[] = R"(<url>; rel="preload".)";
564     auto res = http2::parse_link_header(s, sizeof(s) - 1);
565     CU_ASSERT(0 == res.size());
566   }
567   {
568     // preload in relation-types list, followed by ';' but it
569     // terminates input
570     const char s[] = R"(<url>; rel="preload";)";
571     auto res = http2::parse_link_header(s, sizeof(s) - 1);
572     CU_ASSERT(0 == res.size());
573   }
574   {
575     // preload in relation-types list, followed by ',' but it
576     // terminates input
577     const char s[] = R"(<url>; rel="preload",)";
578     auto res = http2::parse_link_header(s, sizeof(s) - 1);
579     CU_ASSERT(1 == res.size());
580     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
581   }
582   {
583     // preload in relation-types list but there is preceding white
584     // space.
585     const char s[] = R"(<url>; rel=" preload")";
586     auto res = http2::parse_link_header(s, sizeof(s) - 1);
587     CU_ASSERT(0 == res.size());
588   }
589   {
590     // preload in relation-types list but there is trailing white
591     // space.
592     const char s[] = R"(<url>; rel="preload ")";
593     auto res = http2::parse_link_header(s, sizeof(s) - 1);
594     CU_ASSERT(0 == res.size());
595   }
596   {
597     // backslash escaped characters in quoted-string
598     const char s[] = R"(<url>; rel=preload; title="foo\"baz\"bar")";
599     auto res = http2::parse_link_header(s, sizeof(s) - 1);
600     CU_ASSERT(1 == res.size());
601     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
602   }
603   {
604     // anchor="" is acceptable
605     const char s[] = R"(<url>; rel=preload; anchor="")";
606     auto res = http2::parse_link_header(s, sizeof(s) - 1);
607     CU_ASSERT(1 == res.size());
608     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
609   }
610   {
611     // With anchor="#foo", url should be ignored
612     const char s[] = R"(<url>; rel=preload; anchor="#foo")";
613     auto res = http2::parse_link_header(s, sizeof(s) - 1);
614     CU_ASSERT(0 == res.size());
615   }
616   {
617     // With anchor=f, url should be ignored
618     const char s[] = "<url>; rel=preload; anchor=f";
619     auto res = http2::parse_link_header(s, sizeof(s) - 1);
620     CU_ASSERT(0 == res.size());
621   }
622   {
623     // First url is ignored With anchor="#foo", but url should be
624     // accepted.
625     const char s[] = R"(<url>; rel=preload; anchor="#foo", <url>; rel=preload)";
626     auto res = http2::parse_link_header(s, sizeof(s) - 1);
627     CU_ASSERT(1 == res.size());
628     CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri);
629   }
630   {
631     // With loadpolicy="next", url should be ignored
632     const char s[] = R"(<url>; rel=preload; loadpolicy="next")";
633     auto res = http2::parse_link_header(s, sizeof(s) - 1);
634     CU_ASSERT(0 == res.size());
635   }
636   {
637     // url should be picked up if empty loadpolicy is specified
638     const char s[] = R"(<url>; rel=preload; loadpolicy="")";
639     auto res = http2::parse_link_header(s, sizeof(s) - 1);
640     CU_ASSERT(1 == res.size());
641     CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
642   }
643   {
644     // case-insensitive match
645     const char s[] = R"(<url>; rel=preload; ANCHOR="#foo", <url>; )"
646                      R"(REL=PRELOAD, <url>; REL="foo PRELOAD bar")";
647     auto res = http2::parse_link_header(s, sizeof(s) - 1);
648     CU_ASSERT(2 == res.size());
649     CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri);
650     CU_ASSERT(std::make_pair(&s[42 + 14], &s[42 + 17]) == res[1].uri);
651   }
652 }
653
654 void test_http2_path_join(void) {
655   {
656     const char base[] = "/";
657     const char rel[] = "/";
658     CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
659                                       sizeof(rel) - 1, nullptr, 0));
660   }
661   {
662     const char base[] = "/";
663     const char rel[] = "/alpha";
664     CU_ASSERT("/alpha" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
665                                            rel, sizeof(rel) - 1, nullptr, 0));
666   }
667   {
668     // rel ends with trailing '/'
669     const char base[] = "/";
670     const char rel[] = "/alpha/";
671     CU_ASSERT("/alpha/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
672                                             rel, sizeof(rel) - 1, nullptr, 0));
673   }
674   {
675     // rel contains multiple components
676     const char base[] = "/";
677     const char rel[] = "/alpha/bravo";
678     CU_ASSERT("/alpha/bravo" == http2::path_join(base, sizeof(base) - 1,
679                                                  nullptr, 0, rel,
680                                                  sizeof(rel) - 1, nullptr, 0));
681   }
682   {
683     // rel is relative
684     const char base[] = "/";
685     const char rel[] = "alpha/bravo";
686     CU_ASSERT("/alpha/bravo" == http2::path_join(base, sizeof(base) - 1,
687                                                  nullptr, 0, rel,
688                                                  sizeof(rel) - 1, nullptr, 0));
689   }
690   {
691     // rel is relative and base ends without /, which means it refers
692     // to file.
693     const char base[] = "/alpha";
694     const char rel[] = "bravo/charlie";
695     CU_ASSERT("/bravo/charlie" ==
696               http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
697                                sizeof(rel) - 1, nullptr, 0));
698   }
699   {
700     // rel contains repeated '/'s
701     const char base[] = "/";
702     const char rel[] = "/alpha/////bravo/////";
703     CU_ASSERT("/alpha/bravo/" == http2::path_join(base, sizeof(base) - 1,
704                                                   nullptr, 0, rel,
705                                                   sizeof(rel) - 1, nullptr, 0));
706   }
707   {
708     // base ends with '/', so '..' eats 'bravo'
709     const char base[] = "/alpha/bravo/";
710     const char rel[] = "../charlie/delta";
711     CU_ASSERT("/alpha/charlie/delta" ==
712               http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
713                                sizeof(rel) - 1, nullptr, 0));
714   }
715   {
716     // base does not end with '/', so '..' eats 'alpha/bravo'
717     const char base[] = "/alpha/bravo";
718     const char rel[] = "../charlie";
719     CU_ASSERT("/charlie" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
720                                              rel, sizeof(rel) - 1, nullptr, 0));
721   }
722   {
723     // 'charlie' is eaten by following '..'
724     const char base[] = "/alpha/bravo/";
725     const char rel[] = "../charlie/../delta";
726     CU_ASSERT("/alpha/delta" == http2::path_join(base, sizeof(base) - 1,
727                                                  nullptr, 0, rel,
728                                                  sizeof(rel) - 1, nullptr, 0));
729   }
730   {
731     // excessive '..' results in '/'
732     const char base[] = "/alpha/bravo/";
733     const char rel[] = "../../../";
734     CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
735                                       sizeof(rel) - 1, nullptr, 0));
736   }
737   {
738     // excessive '..'  and  path component
739     const char base[] = "/alpha/bravo/";
740     const char rel[] = "../../../charlie";
741     CU_ASSERT("/charlie" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
742                                              rel, sizeof(rel) - 1, nullptr, 0));
743   }
744   {
745     // rel ends with '..'
746     const char base[] = "/alpha/bravo/";
747     const char rel[] = "charlie/..";
748     CU_ASSERT("/alpha/bravo/" == http2::path_join(base, sizeof(base) - 1,
749                                                   nullptr, 0, rel,
750                                                   sizeof(rel) - 1, nullptr, 0));
751   }
752   {
753     // base empty and rel contains '..'
754     const char base[] = "";
755     const char rel[] = "charlie/..";
756     CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
757                                       sizeof(rel) - 1, nullptr, 0));
758   }
759   {
760     // '.' is ignored
761     const char base[] = "/";
762     const char rel[] = "charlie/././././delta";
763     CU_ASSERT("/charlie/delta" ==
764               http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
765                                sizeof(rel) - 1, nullptr, 0));
766   }
767   {
768     // trailing '.' is ignored
769     const char base[] = "/";
770     const char rel[] = "charlie/.";
771     CU_ASSERT("/charlie/" == http2::path_join(base, sizeof(base) - 1, nullptr,
772                                               0, rel, sizeof(rel) - 1, nullptr,
773                                               0));
774   }
775   {
776     // query
777     const char base[] = "/";
778     const char rel[] = "/";
779     const char relq[] = "q";
780     CU_ASSERT("/?q" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
781                                         sizeof(rel) - 1, relq,
782                                         sizeof(relq) - 1));
783   }
784   {
785     // empty rel and query
786     const char base[] = "/alpha";
787     const char rel[] = "";
788     const char relq[] = "q";
789     CU_ASSERT("/alpha?q" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
790                                              rel, sizeof(rel) - 1, relq,
791                                              sizeof(relq) - 1));
792   }
793   {
794     // both rel and query are empty
795     const char base[] = "/alpha";
796     const char baseq[] = "r";
797     const char rel[] = "";
798     const char relq[] = "";
799     CU_ASSERT("/alpha?r" ==
800               http2::path_join(base, sizeof(base) - 1, baseq, sizeof(baseq) - 1,
801                                rel, sizeof(rel) - 1, relq, sizeof(relq) - 1));
802   }
803   {
804     // empty base
805     const char base[] = "";
806     const char rel[] = "/alpha";
807     CU_ASSERT("/alpha" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
808                                            rel, sizeof(rel) - 1, nullptr, 0));
809   }
810   {
811     // everything is empty
812     CU_ASSERT("/" ==
813               http2::path_join(nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0));
814   }
815   {
816     // only baseq is not empty
817     const char base[] = "";
818     const char baseq[] = "r";
819     const char rel[] = "";
820     CU_ASSERT("/?r" == http2::path_join(base, sizeof(base) - 1, baseq,
821                                         sizeof(baseq) - 1, rel, sizeof(rel) - 1,
822                                         nullptr, 0));
823   }
824 }
825
826 } // namespace shrpx