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.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
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
19 * Copyright (C) 2014 Cloudius Systems, Ltd.
24 #include <seastar/testing/test_case.hh>
25 #include <seastar/core/shared_ptr.hh>
26 #include <seastar/net/packet-data-source.hh>
28 #include <seastar/core/future-util.hh>
30 using namespace seastar
;
32 using namespace memcache
;
34 using parser_type
= memcache_ascii_parser
;
36 static packet
make_packet(std::vector
<std::string
> chunks
, size_t buffer_size
) {
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
));
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
))));
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
>();
57 return is
->consume(*parser
).then([is
, parser
] {
58 return make_ready_future
<lw_shared_ptr
<parser_type
>>(parser
);
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
);
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");
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
== "");
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
);
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
);
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");
122 }).then([make_packet
] {
123 return parse(make_packet({"set key 12345 0 0\r\n\r\n"}))
125 BOOST_REQUIRE(p
->_state
== parser_type::state::cmd_set
);
126 BOOST_REQUIRE(p
->_flags_str
== "12345");
128 }).then([make_packet
] {
129 return parse(make_packet({"set key -1 0 0\r\n\r\n"}))
131 BOOST_REQUIRE(p
->_state
== parser_type::state::error
);
133 }).then([make_packet
] {
134 return parse(make_packet({"set key 1-1 0 0\r\n\r\n"}))
136 BOOST_REQUIRE(p
->_state
== parser_type::state::error
);
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"}))
141 BOOST_REQUIRE(p
->_state
== parser_type::state::cmd_set
);
142 BOOST_REQUIRE(p
->_flags_str
== to_sstring(std::numeric_limits
<uint32_t>::max()));
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"}))
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");
162 }).then([make_packet
] {
163 return parse(make_packet({"set key 11", "1 22", "2 3", "\r\nasd\r\n"}))
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");
173 }).then([make_packet
] {
174 return parse(make_packet({"set k", "ey 11", "1 2", "2", "2 3", "\r\nasd\r\n"}))
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");
184 }).then([make_packet
] {
185 return parse(make_packet({"set key 111 222 3\r\n", "asd\r\n"}))
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");
195 }).then([make_packet
] {
196 return parse(make_packet({"set key 111 222 3\r\na", "sd\r\n"}))
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");
206 }).then([make_packet
] {
207 return parse(make_packet({"set key 111 222 3\r\nasd", "\r\n"}))
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");
217 }).then([make_packet
] {
218 return parse(make_packet({"set key 111 222 3\r\nasd\r", "\n"}))
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");
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());
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"}))
246 BOOST_REQUIRE(p
->_state
== parser_type::state::cmd_get
);
247 BOOST_REQUIRE_EQUAL(as_strings(p
->_keys
), std::vector
<sstring
>({"key1"}));
249 }).then([make_packet
] {
250 return parse(make_packet({"get key1 key2\r\n"}))
252 BOOST_REQUIRE(p
->_state
== parser_type::state::cmd_get
);
253 BOOST_REQUIRE_EQUAL(as_strings(p
->_keys
), std::vector
<sstring
>({"key1", "key2"}));
255 }).then([make_packet
] {
256 return parse(make_packet({"get key1 key2 key3\r\n"}))
258 BOOST_REQUIRE(p
->_state
== parser_type::state::cmd_get
);
259 BOOST_REQUIRE_EQUAL(as_strings(p
->_keys
), std::vector
<sstring
>({"key1", "key2", "key3"}));
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"}))
271 BOOST_REQUIRE(p
->_state
== parser_type::state::error
);
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"})));
282 return is
->consume(*p
).then([p
] {
283 BOOST_REQUIRE(p
->_state
== parser_type::state::cmd_get
);
286 return is
->consume(*p
).then([p
, is
] {
287 BOOST_REQUIRE(p
->_state
== parser_type::state::eof
);
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"})));
298 return is
->consume(*p
).then([p
] {
299 BOOST_REQUIRE(p
->_state
== parser_type::state::error
);
302 return is
->consume(*p
).then([p
, is
] {
303 BOOST_REQUIRE(p
->_state
== parser_type::state::eof
);
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"})));
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");
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+");