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 "shrpx_downstream_test.h"
26
27 #include <iostream>
28
29 #include "munitxx.h"
30
31 #include "shrpx_downstream.h"
32
33 using namespace std::literals;
34
35 namespace shrpx {
36
37 namespace {
38 const MunitTest tests[]{
39 munit_void_test(test_downstream_field_store_append_last_header),
40 munit_void_test(test_downstream_field_store_header),
41 munit_void_test(test_downstream_crumble_request_cookie),
42 munit_void_test(test_downstream_assemble_request_cookie),
43 munit_void_test(test_downstream_rewrite_location_response_header),
44 munit_void_test(test_downstream_supports_non_final_response),
45 munit_void_test(test_downstream_find_affinity_cookie),
46 munit_test_end(),
47 };
48 } // namespace
49
50 const MunitSuite downstream_suite{
51 "/downstream", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE,
52 };
53
test_downstream_field_store_append_last_header(void)54 void test_downstream_field_store_append_last_header(void) {
55 BlockAllocator balloc(16, 16);
56 FieldStore fs(balloc, 0);
57 fs.alloc_add_header_name("alpha"_sr);
58 auto bravo = "BRAVO"_sr;
59 fs.append_last_header_key(bravo.data(), bravo.size());
60 // Add more characters so that relloc occurs
61 auto golf = "golF0123456789"_sr;
62 fs.append_last_header_key(golf.data(), golf.size());
63
64 auto charlie = "Charlie"_sr;
65 fs.append_last_header_value(charlie.data(), charlie.size());
66 auto delta = "deltA"_sr;
67 fs.append_last_header_value(delta.data(), delta.size());
68 // Add more characters so that relloc occurs
69 auto echo = "echo0123456789"_sr;
70 fs.append_last_header_value(echo.data(), echo.size());
71
72 fs.add_header_token("echo"_sr, "foxtrot"_sr, false, -1);
73
74 auto ans = HeaderRefs{
75 {"alphabravogolf0123456789"_sr, "CharliedeltAecho0123456789"_sr},
76 {"echo"_sr, "foxtrot"_sr}};
77 assert_true(ans == fs.headers());
78 }
79
test_downstream_field_store_header(void)80 void test_downstream_field_store_header(void) {
81 BlockAllocator balloc(16, 16);
82 FieldStore fs(balloc, 0);
83 fs.add_header_token("alpha"_sr, "0"_sr, false, -1);
84 fs.add_header_token(":authority"_sr, "1"_sr, false, http2::HD__AUTHORITY);
85 fs.add_header_token("content-length"_sr, "2"_sr, false,
86 http2::HD_CONTENT_LENGTH);
87
88 // By token
89 assert_true(HeaderRef(StringRef{":authority"}, StringRef{"1"}) ==
90 *fs.header(http2::HD__AUTHORITY));
91 assert_null(fs.header(http2::HD__METHOD));
92
93 // By name
94 assert_true(HeaderRef(StringRef{"alpha"}, StringRef{"0"}) ==
95 *fs.header("alpha"_sr));
96 assert_null(fs.header("bravo"_sr));
97 }
98
test_downstream_crumble_request_cookie(void)99 void test_downstream_crumble_request_cookie(void) {
100 Downstream d(nullptr, nullptr, 0);
101 auto &req = d.request();
102 req.fs.add_header_token(":method"_sr, "get"_sr, false, -1);
103 req.fs.add_header_token(":path"_sr, "/"_sr, false, -1);
104 req.fs.add_header_token("cookie"_sr, "alpha; bravo; ; ;; charlie;;"_sr, true,
105 http2::HD_COOKIE);
106 req.fs.add_header_token("cookie"_sr, ";delta"_sr, false, http2::HD_COOKIE);
107 req.fs.add_header_token("cookie"_sr, "echo"_sr, false, http2::HD_COOKIE);
108
109 std::vector<nghttp2_nv> nva;
110 d.crumble_request_cookie(nva);
111
112 auto num_cookies = d.count_crumble_request_cookie();
113
114 assert_size(5, ==, nva.size());
115 assert_size(5, ==, num_cookies);
116
117 HeaderRefs cookies;
118 std::transform(std::begin(nva), std::end(nva), std::back_inserter(cookies),
119 [](const nghttp2_nv &nv) {
120 return HeaderRef(StringRef{nv.name, nv.namelen},
121 StringRef{nv.value, nv.valuelen},
122 nv.flags & NGHTTP2_NV_FLAG_NO_INDEX);
123 });
124
125 HeaderRefs ans = {{"cookie"_sr, "alpha"_sr},
126 {"cookie"_sr, "bravo"_sr},
127 {"cookie"_sr, "charlie"_sr},
128 {"cookie"_sr, "delta"_sr},
129 {"cookie"_sr, "echo"_sr}};
130
131 assert_true(ans == cookies);
132 assert_true(cookies[0].no_index);
133 assert_true(cookies[1].no_index);
134 assert_true(cookies[2].no_index);
135 }
136
test_downstream_assemble_request_cookie(void)137 void test_downstream_assemble_request_cookie(void) {
138 Downstream d(nullptr, nullptr, 0);
139 auto &req = d.request();
140
141 req.fs.add_header_token(":method"_sr, "get"_sr, false, -1);
142 req.fs.add_header_token(":path"_sr, "/"_sr, false, -1);
143 req.fs.add_header_token("cookie"_sr, "alpha"_sr, false, http2::HD_COOKIE);
144 req.fs.add_header_token("cookie"_sr, "bravo;"_sr, false, http2::HD_COOKIE);
145 req.fs.add_header_token("cookie"_sr, "charlie; "_sr, false, http2::HD_COOKIE);
146 req.fs.add_header_token("cookie"_sr, "delta;;"_sr, false, http2::HD_COOKIE);
147 assert_stdsv_equal("alpha; bravo; charlie; delta"sv,
148 d.assemble_request_cookie());
149 }
150
test_downstream_rewrite_location_response_header(void)151 void test_downstream_rewrite_location_response_header(void) {
152 Downstream d(nullptr, nullptr, 0);
153 auto &req = d.request();
154 auto &resp = d.response();
155 d.set_request_downstream_host("localhost2"_sr);
156 req.authority = "localhost:8443"_sr;
157 resp.fs.add_header_token("location"_sr, "http://localhost2:3000/"_sr, false,
158 http2::HD_LOCATION);
159 d.rewrite_location_response_header("https"_sr);
160 auto location = resp.fs.header(http2::HD_LOCATION);
161 assert_stdsv_equal("https://localhost:8443/"sv, (*location).value);
162 }
163
test_downstream_supports_non_final_response(void)164 void test_downstream_supports_non_final_response(void) {
165 Downstream d(nullptr, nullptr, 0);
166 auto &req = d.request();
167
168 req.http_major = 3;
169 req.http_minor = 0;
170
171 assert_true(d.supports_non_final_response());
172
173 req.http_major = 2;
174 req.http_minor = 0;
175
176 assert_true(d.supports_non_final_response());
177
178 req.http_major = 1;
179 req.http_minor = 1;
180
181 assert_true(d.supports_non_final_response());
182
183 req.http_major = 1;
184 req.http_minor = 0;
185
186 assert_false(d.supports_non_final_response());
187
188 req.http_major = 0;
189 req.http_minor = 9;
190
191 assert_false(d.supports_non_final_response());
192 }
193
test_downstream_find_affinity_cookie(void)194 void test_downstream_find_affinity_cookie(void) {
195 Downstream d(nullptr, nullptr, 0);
196
197 auto &req = d.request();
198 req.fs.add_header_token("cookie"_sr, StringRef{}, false, http2::HD_COOKIE);
199 req.fs.add_header_token("cookie"_sr, "a=b;;c=d"_sr, false, http2::HD_COOKIE);
200 req.fs.add_header_token("content-length"_sr, "599"_sr, false,
201 http2::HD_CONTENT_LENGTH);
202 req.fs.add_header_token("cookie"_sr, "lb=deadbeef;LB=f1f2f3f4"_sr, false,
203 http2::HD_COOKIE);
204 req.fs.add_header_token("cookie"_sr, "short=e1e2e3e"_sr, false,
205 http2::HD_COOKIE);
206
207 uint32_t aff;
208
209 aff = d.find_affinity_cookie("lb"_sr);
210
211 assert_uint32(0xdeadbeef, ==, aff);
212
213 aff = d.find_affinity_cookie("LB"_sr);
214
215 assert_uint32(0xf1f2f3f4, ==, aff);
216
217 aff = d.find_affinity_cookie("short"_sr);
218
219 assert_uint32(0, ==, aff);
220 }
221
222 } // namespace shrpx
223