tizen 2.4 release
[external/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(9 == nva.size());
157   auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 8, 9, 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
494 void test_http2_path_join(void) {
495   {
496     const char base[] = "/";
497     const char rel[] = "/";
498     CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
499                                       sizeof(rel) - 1, nullptr, 0));
500   }
501   {
502     const char base[] = "/";
503     const char rel[] = "/alpha";
504     CU_ASSERT("/alpha" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
505                                            rel, sizeof(rel) - 1, nullptr, 0));
506   }
507   {
508     // rel ends with trailing '/'
509     const char base[] = "/";
510     const char rel[] = "/alpha/";
511     CU_ASSERT("/alpha/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
512                                             rel, sizeof(rel) - 1, nullptr, 0));
513   }
514   {
515     // rel contains multiple components
516     const char base[] = "/";
517     const char rel[] = "/alpha/bravo";
518     CU_ASSERT("/alpha/bravo" == http2::path_join(base, sizeof(base) - 1,
519                                                  nullptr, 0, rel,
520                                                  sizeof(rel) - 1, nullptr, 0));
521   }
522   {
523     // rel is relative
524     const char base[] = "/";
525     const char rel[] = "alpha/bravo";
526     CU_ASSERT("/alpha/bravo" == http2::path_join(base, sizeof(base) - 1,
527                                                  nullptr, 0, rel,
528                                                  sizeof(rel) - 1, nullptr, 0));
529   }
530   {
531     // rel is relative and base ends without /, which means it refers
532     // to file.
533     const char base[] = "/alpha";
534     const char rel[] = "bravo/charlie";
535     CU_ASSERT("/bravo/charlie" ==
536               http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
537                                sizeof(rel) - 1, nullptr, 0));
538   }
539   {
540     // rel contains repeated '/'s
541     const char base[] = "/";
542     const char rel[] = "/alpha/////bravo/////";
543     CU_ASSERT("/alpha/bravo/" == http2::path_join(base, sizeof(base) - 1,
544                                                   nullptr, 0, rel,
545                                                   sizeof(rel) - 1, nullptr, 0));
546   }
547   {
548     // base ends with '/', so '..' eats 'bravo'
549     const char base[] = "/alpha/bravo/";
550     const char rel[] = "../charlie/delta";
551     CU_ASSERT("/alpha/charlie/delta" ==
552               http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
553                                sizeof(rel) - 1, nullptr, 0));
554   }
555   {
556     // base does not end with '/', so '..' eats 'alpha/bravo'
557     const char base[] = "/alpha/bravo";
558     const char rel[] = "../charlie";
559     CU_ASSERT("/charlie" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
560                                              rel, sizeof(rel) - 1, nullptr, 0));
561   }
562   {
563     // 'charlie' is eaten by following '..'
564     const char base[] = "/alpha/bravo/";
565     const char rel[] = "../charlie/../delta";
566     CU_ASSERT("/alpha/delta" == http2::path_join(base, sizeof(base) - 1,
567                                                  nullptr, 0, rel,
568                                                  sizeof(rel) - 1, nullptr, 0));
569   }
570   {
571     // excessive '..' results in '/'
572     const char base[] = "/alpha/bravo/";
573     const char rel[] = "../../../";
574     CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
575                                       sizeof(rel) - 1, nullptr, 0));
576   }
577   {
578     // excessive '..'  and  path component
579     const char base[] = "/alpha/bravo/";
580     const char rel[] = "../../../charlie";
581     CU_ASSERT("/charlie" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
582                                              rel, sizeof(rel) - 1, nullptr, 0));
583   }
584   {
585     // rel ends with '..'
586     const char base[] = "/alpha/bravo/";
587     const char rel[] = "charlie/..";
588     CU_ASSERT("/alpha/bravo/" == http2::path_join(base, sizeof(base) - 1,
589                                                   nullptr, 0, rel,
590                                                   sizeof(rel) - 1, nullptr, 0));
591   }
592   {
593     // base empty and rel contains '..'
594     const char base[] = "";
595     const char rel[] = "charlie/..";
596     CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
597                                       sizeof(rel) - 1, nullptr, 0));
598   }
599   {
600     // '.' is ignored
601     const char base[] = "/";
602     const char rel[] = "charlie/././././delta";
603     CU_ASSERT("/charlie/delta" ==
604               http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
605                                sizeof(rel) - 1, nullptr, 0));
606   }
607   {
608     // trailing '.' is ignored
609     const char base[] = "/";
610     const char rel[] = "charlie/.";
611     CU_ASSERT("/charlie/" == http2::path_join(base, sizeof(base) - 1, nullptr,
612                                               0, rel, sizeof(rel) - 1, nullptr,
613                                               0));
614   }
615   {
616     // query
617     const char base[] = "/";
618     const char rel[] = "/";
619     const char relq[] = "q";
620     CU_ASSERT("/?q" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel,
621                                         sizeof(rel) - 1, relq,
622                                         sizeof(relq) - 1));
623   }
624   {
625     // empty rel and query
626     const char base[] = "/alpha";
627     const char rel[] = "";
628     const char relq[] = "q";
629     CU_ASSERT("/alpha?q" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
630                                              rel, sizeof(rel) - 1, relq,
631                                              sizeof(relq) - 1));
632   }
633   {
634     // both rel and query are empty
635     const char base[] = "/alpha";
636     const char baseq[] = "r";
637     const char rel[] = "";
638     const char relq[] = "";
639     CU_ASSERT("/alpha?r" ==
640               http2::path_join(base, sizeof(base) - 1, baseq, sizeof(baseq) - 1,
641                                rel, sizeof(rel) - 1, relq, sizeof(relq) - 1));
642   }
643   {
644     // empty base
645     const char base[] = "";
646     const char rel[] = "/alpha";
647     CU_ASSERT("/alpha" == http2::path_join(base, sizeof(base) - 1, nullptr, 0,
648                                            rel, sizeof(rel) - 1, nullptr, 0));
649   }
650   {
651     // everything is empty
652     CU_ASSERT("/" ==
653               http2::path_join(nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0));
654   }
655   {
656     // only baseq is not empty
657     const char base[] = "";
658     const char baseq[] = "r";
659     const char rel[] = "";
660     CU_ASSERT("/?r" == http2::path_join(base, sizeof(base) - 1, baseq,
661                                         sizeof(baseq) - 1, rel, sizeof(rel) - 1,
662                                         nullptr, 0));
663   }
664 }
665
666 } // namespace shrpx