]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/apps/memcached/tests/test_ascii_parser.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / seastar / apps / memcached / tests / test_ascii_parser.cc
1 /*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18 /*
19 * Copyright (C) 2014 Cloudius Systems, Ltd.
20 */
21
22 #include <iostream>
23 #include <limits>
24 #include <seastar/testing/test_case.hh>
25 #include <seastar/core/shared_ptr.hh>
26 #include <seastar/net/packet-data-source.hh>
27 #include "ascii.hh"
28 #include <seastar/core/future-util.hh>
29
30 using namespace seastar;
31 using namespace net;
32 using namespace memcache;
33
34 using parser_type = memcache_ascii_parser;
35
36 static packet make_packet(std::vector<std::string> chunks, size_t buffer_size) {
37 packet p;
38 for (auto&& chunk : chunks) {
39 size_t size = chunk.size();
40 for (size_t pos = 0; pos < size; pos += buffer_size) {
41 auto now = std::min(pos + buffer_size, chunk.size()) - pos;
42 p.append(packet(chunk.data() + pos, now));
43 }
44 }
45 return p;
46 }
47
48 static auto make_input_stream(packet&& p) {
49 return input_stream<char>(data_source(
50 std::make_unique<packet_data_source>(std::move(p))));
51 }
52
53 static auto parse(packet&& p) {
54 auto is = make_lw_shared<input_stream<char>>(make_input_stream(std::move(p)));
55 auto parser = make_lw_shared<parser_type>();
56 parser->init();
57 return is->consume(*parser).then([is, parser] {
58 return make_ready_future<lw_shared_ptr<parser_type>>(parser);
59 });
60 }
61
62 auto for_each_fragment_size = [] (auto&& func) {
63 static std::vector<int> buffer_sizes = { 100000, 1000, 100, 10, 5, 2, 1 };
64 return do_for_each(buffer_sizes.begin(), buffer_sizes.end(), [func] (size_t buffer_size) {
65 return func([buffer_size] (std::vector<std::string> chunks) {
66 return make_packet(chunks, buffer_size);
67 });
68 });
69 };
70
71 SEASTAR_TEST_CASE(test_set_command_is_parsed) {
72 return for_each_fragment_size([] (auto make_packet) {
73 return parse(make_packet({"set key 1 2 3\r\nabc\r\n"})).then([] (auto p) {
74 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
75 BOOST_REQUIRE(p->_flags_str == "1");
76 BOOST_REQUIRE(p->_expiration == 2);
77 BOOST_REQUIRE(p->_size == 3);
78 BOOST_REQUIRE(p->_size_str == "3");
79 BOOST_REQUIRE(p->_key.key() == "key");
80 BOOST_REQUIRE(p->_blob == "abc");
81 });
82 });
83 }
84
85 SEASTAR_TEST_CASE(test_empty_data_is_parsed) {
86 return for_each_fragment_size([] (auto make_packet) {
87 return parse(make_packet({"set key 1 2 0\r\n\r\n"})).then([] (auto p) {
88 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
89 BOOST_REQUIRE(p->_flags_str == "1");
90 BOOST_REQUIRE(p->_expiration == 2);
91 BOOST_REQUIRE(p->_size == 0);
92 BOOST_REQUIRE(p->_size_str == "0");
93 BOOST_REQUIRE(p->_key.key() == "key");
94 BOOST_REQUIRE(p->_blob == "");
95 });
96 });
97 }
98
99 SEASTAR_TEST_CASE(test_superflous_data_is_an_error) {
100 return for_each_fragment_size([] (auto make_packet) {
101 return parse(make_packet({"set key 0 0 0\r\nasd\r\n"})).then([] (auto p) {
102 BOOST_REQUIRE(p->_state == parser_type::state::error);
103 });
104 });
105 }
106
107 SEASTAR_TEST_CASE(test_not_enough_data_is_an_error) {
108 return for_each_fragment_size([] (auto make_packet) {
109 return parse(make_packet({"set key 0 0 3\r\n"})).then([] (auto p) {
110 BOOST_REQUIRE(p->_state == parser_type::state::error);
111 });
112 });
113 }
114
115 SEASTAR_TEST_CASE(test_u32_parsing) {
116 return for_each_fragment_size([] (auto make_packet) {
117 return make_ready_future<>().then([make_packet] {
118 return parse(make_packet({"set key 0 0 0\r\n\r\n"})).then([] (auto p) {
119 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
120 BOOST_REQUIRE(p->_flags_str == "0");
121 });
122 }).then([make_packet] {
123 return parse(make_packet({"set key 12345 0 0\r\n\r\n"}))
124 .then([] (auto p) {
125 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
126 BOOST_REQUIRE(p->_flags_str == "12345");
127 });
128 }).then([make_packet] {
129 return parse(make_packet({"set key -1 0 0\r\n\r\n"}))
130 .then([] (auto p) {
131 BOOST_REQUIRE(p->_state == parser_type::state::error);
132 });
133 }).then([make_packet] {
134 return parse(make_packet({"set key 1-1 0 0\r\n\r\n"}))
135 .then([] (auto p) {
136 BOOST_REQUIRE(p->_state == parser_type::state::error);
137 });
138 }).then([make_packet] {
139 return parse(make_packet({"set key " + std::to_string(std::numeric_limits<uint32_t>::max()) + " 0 0\r\n\r\n"}))
140 .then([] (auto p) {
141 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
142 BOOST_REQUIRE(p->_flags_str == to_sstring(std::numeric_limits<uint32_t>::max()));
143 });
144 });
145 });
146 }
147
148 SEASTAR_TEST_CASE(test_parsing_of_split_data) {
149 return for_each_fragment_size([] (auto make_packet) {
150 return make_ready_future<>()
151 .then([make_packet] {
152 return parse(make_packet({"set key 11", "1 222 3\r\nasd\r\n"}))
153 .then([] (auto p) {
154 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
155 BOOST_REQUIRE(p->_key.key() == "key");
156 BOOST_REQUIRE(p->_flags_str == "111");
157 BOOST_REQUIRE(p->_expiration == 222);
158 BOOST_REQUIRE(p->_size == 3);
159 BOOST_REQUIRE(p->_size_str == "3");
160 BOOST_REQUIRE(p->_blob == "asd");
161 });
162 }).then([make_packet] {
163 return parse(make_packet({"set key 11", "1 22", "2 3", "\r\nasd\r\n"}))
164 .then([] (auto p) {
165 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
166 BOOST_REQUIRE(p->_key.key() == "key");
167 BOOST_REQUIRE(p->_flags_str == "111");
168 BOOST_REQUIRE(p->_expiration == 222);
169 BOOST_REQUIRE(p->_size == 3);
170 BOOST_REQUIRE(p->_size_str == "3");
171 BOOST_REQUIRE(p->_blob == "asd");
172 });
173 }).then([make_packet] {
174 return parse(make_packet({"set k", "ey 11", "1 2", "2", "2 3", "\r\nasd\r\n"}))
175 .then([] (auto p) {
176 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
177 BOOST_REQUIRE(p->_key.key() == "key");
178 BOOST_REQUIRE(p->_flags_str == "111");
179 BOOST_REQUIRE(p->_expiration == 222);
180 BOOST_REQUIRE(p->_size == 3);
181 BOOST_REQUIRE(p->_size_str == "3");
182 BOOST_REQUIRE(p->_blob == "asd");
183 });
184 }).then([make_packet] {
185 return parse(make_packet({"set key 111 222 3\r\n", "asd\r\n"}))
186 .then([] (auto p) {
187 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
188 BOOST_REQUIRE(p->_key.key() == "key");
189 BOOST_REQUIRE(p->_flags_str == "111");
190 BOOST_REQUIRE(p->_expiration == 222);
191 BOOST_REQUIRE(p->_size == 3);
192 BOOST_REQUIRE(p->_size_str == "3");
193 BOOST_REQUIRE(p->_blob == "asd");
194 });
195 }).then([make_packet] {
196 return parse(make_packet({"set key 111 222 3\r\na", "sd\r\n"}))
197 .then([] (auto p) {
198 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
199 BOOST_REQUIRE(p->_key.key() == "key");
200 BOOST_REQUIRE(p->_flags_str == "111");
201 BOOST_REQUIRE(p->_expiration == 222);
202 BOOST_REQUIRE(p->_size == 3);
203 BOOST_REQUIRE(p->_size_str == "3");
204 BOOST_REQUIRE(p->_blob == "asd");
205 });
206 }).then([make_packet] {
207 return parse(make_packet({"set key 111 222 3\r\nasd", "\r\n"}))
208 .then([] (auto p) {
209 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
210 BOOST_REQUIRE(p->_key.key() == "key");
211 BOOST_REQUIRE(p->_flags_str == "111");
212 BOOST_REQUIRE(p->_expiration == 222);
213 BOOST_REQUIRE(p->_size == 3);
214 BOOST_REQUIRE(p->_size_str == "3");
215 BOOST_REQUIRE(p->_blob == "asd");
216 });
217 }).then([make_packet] {
218 return parse(make_packet({"set key 111 222 3\r\nasd\r", "\n"}))
219 .then([] (auto p) {
220 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
221 BOOST_REQUIRE(p->_key.key() == "key");
222 BOOST_REQUIRE(p->_flags_str == "111");
223 BOOST_REQUIRE(p->_expiration == 222);
224 BOOST_REQUIRE(p->_size == 3);
225 BOOST_REQUIRE(p->_size_str == "3");
226 BOOST_REQUIRE(p->_blob == "asd");
227 });
228 });
229 });
230 }
231
232 static std::vector<sstring> as_strings(std::vector<item_key>& keys) {
233 std::vector<sstring> v;
234 for (auto&& key : keys) {
235 v.push_back(key.key());
236 }
237 return v;
238 }
239
240 SEASTAR_TEST_CASE(test_get_parsing) {
241 return for_each_fragment_size([] (auto make_packet) {
242 return make_ready_future<>()
243 .then([make_packet] {
244 return parse(make_packet({"get key1\r\n"}))
245 .then([] (auto p) {
246 BOOST_REQUIRE(p->_state == parser_type::state::cmd_get);
247 BOOST_REQUIRE_EQUAL(as_strings(p->_keys), std::vector<sstring>({"key1"}));
248 });
249 }).then([make_packet] {
250 return parse(make_packet({"get key1 key2\r\n"}))
251 .then([] (auto p) {
252 BOOST_REQUIRE(p->_state == parser_type::state::cmd_get);
253 BOOST_REQUIRE_EQUAL(as_strings(p->_keys), std::vector<sstring>({"key1", "key2"}));
254 });
255 }).then([make_packet] {
256 return parse(make_packet({"get key1 key2 key3\r\n"}))
257 .then([] (auto p) {
258 BOOST_REQUIRE(p->_state == parser_type::state::cmd_get);
259 BOOST_REQUIRE_EQUAL(as_strings(p->_keys), std::vector<sstring>({"key1", "key2", "key3"}));
260 });
261 });
262 });
263 }
264
265 SEASTAR_TEST_CASE(test_catches_errors_in_get) {
266 return for_each_fragment_size([] (auto make_packet) {
267 return make_ready_future<>()
268 .then([make_packet] {
269 return parse(make_packet({"get\r\n"}))
270 .then([] (auto p) {
271 BOOST_REQUIRE(p->_state == parser_type::state::error);
272 });
273 });
274 });
275 }
276
277 SEASTAR_TEST_CASE(test_parser_returns_eof_state_when_no_command_follows) {
278 return for_each_fragment_size([] (auto make_packet) {
279 auto p = make_shared<parser_type>();
280 auto is = make_shared<input_stream<char>>(make_input_stream(make_packet({"get key\r\n"})));
281 p->init();
282 return is->consume(*p).then([p] {
283 BOOST_REQUIRE(p->_state == parser_type::state::cmd_get);
284 }).then([is, p] {
285 p->init();
286 return is->consume(*p).then([p, is] {
287 BOOST_REQUIRE(p->_state == parser_type::state::eof);
288 });
289 });
290 });
291 }
292
293 SEASTAR_TEST_CASE(test_incomplete_command_is_an_error) {
294 return for_each_fragment_size([] (auto make_packet) {
295 auto p = make_shared<parser_type>();
296 auto is = make_shared<input_stream<char>>(make_input_stream(make_packet({"get"})));
297 p->init();
298 return is->consume(*p).then([p] {
299 BOOST_REQUIRE(p->_state == parser_type::state::error);
300 }).then([is, p] {
301 p->init();
302 return is->consume(*p).then([p, is] {
303 BOOST_REQUIRE(p->_state == parser_type::state::eof);
304 });
305 });
306 });
307 }
308
309 SEASTAR_TEST_CASE(test_multiple_requests_in_one_stream) {
310 return for_each_fragment_size([] (auto make_packet) {
311 auto p = make_shared<parser_type>();
312 auto is = make_shared<input_stream<char>>(make_input_stream(make_packet({"set key1 1 1 5\r\ndata1\r\nset key2 2 2 6\r\ndata2+\r\n"})));
313 p->init();
314 return is->consume(*p).then([p] {
315 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
316 BOOST_REQUIRE(p->_key.key() == "key1");
317 BOOST_REQUIRE(p->_flags_str == "1");
318 BOOST_REQUIRE(p->_expiration == 1);
319 BOOST_REQUIRE(p->_size == 5);
320 BOOST_REQUIRE(p->_size_str == "5");
321 BOOST_REQUIRE(p->_blob == "data1");
322 }).then([is, p] {
323 p->init();
324 return is->consume(*p).then([p, is] {
325 BOOST_REQUIRE(p->_state == parser_type::state::cmd_set);
326 BOOST_REQUIRE(p->_key.key() == "key2");
327 BOOST_REQUIRE(p->_flags_str == "2");
328 BOOST_REQUIRE(p->_expiration == 2);
329 BOOST_REQUIRE(p->_size == 6);
330 BOOST_REQUIRE(p->_size_str == "6");
331 BOOST_REQUIRE(p->_blob == "data2+");
332 });
333 });
334 });
335 }