]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/tests/unit/httpd_test.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / seastar / tests / unit / httpd_test.cc
1 /*
2 * Copyright 2015 Cloudius Systems
3 */
4
5 #include <seastar/http/httpd.hh>
6 #include <seastar/http/handlers.hh>
7 #include <seastar/http/matcher.hh>
8 #include <seastar/http/matchrules.hh>
9 #include <seastar/json/formatter.hh>
10 #include <seastar/http/routes.hh>
11 #include <seastar/http/exception.hh>
12 #include <seastar/http/transformers.hh>
13 #include <seastar/core/future-util.hh>
14 #include <seastar/testing/test_case.hh>
15 #include "loopback_socket.hh"
16 #include <boost/algorithm/string.hpp>
17 #include <seastar/core/thread.hh>
18 #include <seastar/util/noncopyable_function.hh>
19 #include <seastar/http/json_path.hh>
20 #include <sstream>
21
22 using namespace seastar;
23 using namespace httpd;
24
25 class handl : public httpd::handler_base {
26 public:
27 virtual future<std::unique_ptr<reply> > handle(const sstring& path,
28 std::unique_ptr<request> req, std::unique_ptr<reply> rep) {
29 rep->done("html");
30 return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
31 }
32 };
33
34 SEASTAR_TEST_CASE(test_reply)
35 {
36 reply r;
37 r.set_content_type("txt");
38 BOOST_REQUIRE_EQUAL(r._headers["Content-Type"], sstring("text/plain"));
39 return make_ready_future<>();
40 }
41
42 SEASTAR_TEST_CASE(test_str_matcher)
43 {
44
45 str_matcher m("/hello");
46 parameters param;
47 BOOST_REQUIRE_EQUAL(m.match("/abc/hello", 4, param), 10u);
48 return make_ready_future<>();
49 }
50
51 SEASTAR_TEST_CASE(test_param_matcher)
52 {
53
54 param_matcher m("param");
55 parameters param;
56 BOOST_REQUIRE_EQUAL(m.match("/abc/hello", 4, param), 10u);
57 BOOST_REQUIRE_EQUAL(param.path("param"), "/hello");
58 BOOST_REQUIRE_EQUAL(param["param"], "hello");
59 return make_ready_future<>();
60 }
61
62 SEASTAR_TEST_CASE(test_match_rule)
63 {
64
65 parameters param;
66 handl* h = new handl();
67 match_rule mr(h);
68 mr.add_str("/hello").add_param("param");
69 httpd::handler_base* res = mr.get("/hello/val1", param);
70 BOOST_REQUIRE_EQUAL(res, h);
71 BOOST_REQUIRE_EQUAL(param["param"], "val1");
72 res = mr.get("/hell/val1", param);
73 httpd::handler_base* nl = nullptr;
74 BOOST_REQUIRE_EQUAL(res, nl);
75 return make_ready_future<>();
76 }
77
78 SEASTAR_TEST_CASE(test_formatter)
79 {
80 BOOST_REQUIRE_EQUAL(json::formatter::to_json(true), "true");
81 BOOST_REQUIRE_EQUAL(json::formatter::to_json(false), "false");
82 BOOST_REQUIRE_EQUAL(json::formatter::to_json(1), "1");
83 const char* txt = "efg";
84 BOOST_REQUIRE_EQUAL(json::formatter::to_json(txt), "\"efg\"");
85 sstring str = "abc";
86 BOOST_REQUIRE_EQUAL(json::formatter::to_json(str), "\"abc\"");
87 float f = 1;
88 BOOST_REQUIRE_EQUAL(json::formatter::to_json(f), "1");
89 f = 1.0/0.0;
90 BOOST_CHECK_THROW(json::formatter::to_json(f), std::out_of_range);
91 f = -1.0/0.0;
92 BOOST_CHECK_THROW(json::formatter::to_json(f), std::out_of_range);
93 f = 0.0/0.0;
94 BOOST_CHECK_THROW(json::formatter::to_json(f), std::invalid_argument);
95 double d = -1;
96 BOOST_REQUIRE_EQUAL(json::formatter::to_json(d), "-1");
97 d = 1.0/0.0;
98 BOOST_CHECK_THROW(json::formatter::to_json(d), std::out_of_range);
99 d = -1.0/0.0;
100 BOOST_CHECK_THROW(json::formatter::to_json(d), std::out_of_range);
101 d = 0.0/0.0;
102 BOOST_CHECK_THROW(json::formatter::to_json(d), std::invalid_argument);
103 return make_ready_future<>();
104 }
105
106 SEASTAR_TEST_CASE(test_decode_url) {
107 request req;
108 req._url = "/a?q=%23%24%23";
109 sstring url = http_server::connection::set_query_param(req);
110 BOOST_REQUIRE_EQUAL(url, "/a");
111 BOOST_REQUIRE_EQUAL(req.get_query_param("q"), "#$#");
112 req._url = "/a?a=%23%24%23&b=%22%26%22";
113 http_server::connection::set_query_param(req);
114 BOOST_REQUIRE_EQUAL(req.get_query_param("a"), "#$#");
115 BOOST_REQUIRE_EQUAL(req.get_query_param("b"), "\"&\"");
116 return make_ready_future<>();
117 }
118
119 SEASTAR_TEST_CASE(test_routes) {
120 handl* h1 = new handl();
121 handl* h2 = new handl();
122 routes route;
123 route.add(operation_type::GET, url("/api").remainder("path"), h1);
124 route.add(operation_type::GET, url("/"), h2);
125 std::unique_ptr<request> req = std::make_unique<request>();
126 std::unique_ptr<reply> rep = std::make_unique<reply>();
127
128 auto f1 =
129 route.handle("/api", std::move(req), std::move(rep)).then(
130 [] (std::unique_ptr<reply> rep) {
131 BOOST_REQUIRE_EQUAL((int )rep->_status, (int )reply::status_type::ok);
132 });
133 req.reset(new request);
134 rep.reset(new reply);
135
136 auto f2 =
137 route.handle("/", std::move(req), std::move(rep)).then(
138 [] (std::unique_ptr<reply> rep) {
139 BOOST_REQUIRE_EQUAL((int )rep->_status, (int )reply::status_type::ok);
140 });
141 req.reset(new request);
142 rep.reset(new reply);
143 auto f3 =
144 route.handle("/api/abc", std::move(req), std::move(rep)).then(
145 [] (std::unique_ptr<reply> rep) {
146 });
147 req.reset(new request);
148 rep.reset(new reply);
149 auto f4 =
150 route.handle("/ap", std::move(req), std::move(rep)).then(
151 [] (std::unique_ptr<reply> rep) {
152 BOOST_REQUIRE_EQUAL((int )rep->_status,
153 (int )reply::status_type::not_found);
154 });
155 return when_all(std::move(f1), std::move(f2), std::move(f3), std::move(f4))
156 .then([] (std::tuple<future<>, future<>, future<>, future<>> fs) {
157 std::get<0>(fs).get();
158 std::get<1>(fs).get();
159 std::get<2>(fs).get();
160 std::get<3>(fs).get();
161 });
162 }
163
164 SEASTAR_TEST_CASE(test_json_path) {
165 shared_ptr<bool> res1 = make_shared<bool>(false);
166 shared_ptr<bool> res2 = make_shared<bool>(false);
167 shared_ptr<bool> res3 = make_shared<bool>(false);
168 shared_ptr<routes> route = make_shared<routes>();
169 path_description path1("/my/path",GET,"path1",
170 {{"param1", path_description::url_component_type::PARAM}
171 ,{"/text", path_description::url_component_type::FIXED_STRING}},{});
172 path_description path2("/my/path",GET,"path2",
173 {{"param1", path_description::url_component_type::PARAM}
174 ,{"param2", path_description::url_component_type::PARAM}},{});
175 path_description path3("/my/path",GET,"path3",
176 {{"param1", path_description::url_component_type::PARAM}
177 ,{"param2", path_description::url_component_type::PARAM_UNTIL_END_OF_PATH}},{});
178
179 path1.set(*route, [res1] (const_req req) {
180 (*res1) = true;
181 BOOST_REQUIRE_EQUAL(req.param["param1"], "value1");
182 return "";
183 });
184
185 path2.set(*route, [res2] (const_req req) {
186 (*res2) = true;
187 BOOST_REQUIRE_EQUAL(req.param["param1"], "value2");
188 BOOST_REQUIRE_EQUAL(req.param["param2"], "text1");
189 return "";
190 });
191
192 path3.set(*route, [res3] (const_req req) {
193 (*res3) = true;
194 BOOST_REQUIRE_EQUAL(req.param["param1"], "value3");
195 BOOST_REQUIRE_EQUAL(req.param["param2"], "text2/text3");
196 return "";
197 });
198
199 auto f1 = route->handle("/my/path/value1/text", std::make_unique<request>(), std::make_unique<reply>()).then([res1, route] (auto f) {
200 BOOST_REQUIRE_EQUAL(*res1, true);
201 });
202
203 auto f2 = route->handle("/my/path/value2/text1", std::make_unique<request>(), std::make_unique<reply>()).then([res2, route] (auto f) {
204 BOOST_REQUIRE_EQUAL(*res2, true);
205 });
206
207 auto f3 = route->handle("/my/path/value3/text2/text3", std::make_unique<request>(), std::make_unique<reply>()).then([res3, route] (auto f) {
208 BOOST_REQUIRE_EQUAL(*res3, true);
209 });
210
211 return when_all(std::move(f1), std::move(f2), std::move(f3))
212 .then([] (std::tuple<future<>, future<>, future<>> fs) {
213 std::get<0>(fs).get();
214 std::get<1>(fs).get();
215 std::get<2>(fs).get();
216 });
217 }
218
219 /*!
220 * \brief a helper data sink that stores everything it gets in a stringstream
221 */
222 class memory_data_sink_impl : public data_sink_impl {
223 std::stringstream& _ss;
224 public:
225 memory_data_sink_impl(std::stringstream& ss) : _ss(ss) {
226 }
227 virtual future<> put(net::packet data) override {
228 abort();
229 return make_ready_future<>();
230 }
231 virtual future<> put(temporary_buffer<char> buf) override {
232 _ss.write(buf.get(), buf.size());
233 return make_ready_future<>();
234 }
235 virtual future<> flush() override {
236 return make_ready_future<>();
237 }
238
239 virtual future<> close() override {
240 return make_ready_future<>();
241 }
242 };
243
244 class memory_data_sink : public data_sink {
245 public:
246 memory_data_sink(std::stringstream& ss)
247 : data_sink(std::make_unique<memory_data_sink_impl>(ss)) {}
248 };
249
250 future<> test_transformer_stream(std::stringstream& ss, content_replace& cr, std::vector<sstring>&& buffer_parts) {
251 std::unique_ptr<seastar::httpd::request> req = std::make_unique<seastar::httpd::request>();
252 ss.str("");
253 req->_headers["Host"] = "localhost";
254 return do_with(output_stream<char>(cr.transform(std::move(req), "json", output_stream<char>(memory_data_sink(ss), 32000, true))),
255 std::vector<sstring>(std::move(buffer_parts)), [&ss, &cr] (output_stream<char>& os, std::vector<sstring>& parts) {
256 return do_for_each(parts, [&os](auto& p) {
257 return os.write(p);
258 }).then([&os, &ss] {
259 return os.close();
260 });
261 });
262 }
263
264 SEASTAR_TEST_CASE(test_transformer) {
265 return do_with(std::stringstream(), content_replace("json"), [] (std::stringstream& ss, content_replace& cr) {
266 return do_with(output_stream<char>(cr.transform(std::make_unique<seastar::httpd::request>(), "html", output_stream<char>(memory_data_sink(ss), 32000, true))),
267 [&ss] (output_stream<char>& os) {
268 return os.write(sstring("hello-{{Protocol}}-xyz-{{Host}}")).then([&os] {
269 return os.close();
270 });
271 }).then([&ss, &cr] () {
272 BOOST_REQUIRE_EQUAL(ss.str(), "hello-{{Protocol}}-xyz-{{Host}}");
273 return test_transformer_stream(ss, cr, {"hell", "o-{", "{Pro", "tocol}}-xyz-{{Ho", "st}}{{Pr"}).then([&ss, &cr] {
274 BOOST_REQUIRE_EQUAL(ss.str(), "hello-http-xyz-localhost{{Pr");
275 return test_transformer_stream(ss, cr, {"hell", "o-{{", "Pro", "tocol}}{{Protocol}}-{{Protoxyz-{{Ho", "st}}{{Pr"}).then([&ss, &cr] {
276 BOOST_REQUIRE_EQUAL(ss.str(), "hello-httphttp-{{Protoxyz-localhost{{Pr");
277 return test_transformer_stream(ss, cr, {"hell", "o-{{Pro", "t{{Protocol}}ocol}}", "{{Host}}"}).then([&ss, &cr] {
278 BOOST_REQUIRE_EQUAL(ss.str(), "hello-{{Prothttpocol}}localhost");
279 });
280 });
281 });
282 });
283 });
284 }
285
286 struct http_consumer {
287 std::map<sstring, std::string> _headers;
288 std::string _body;
289 uint32_t _remain = 0;
290 std::string _current;
291 char last = '\0';
292 uint32_t _size = 0;
293 bool _concat = true;
294
295 enum class status_type {
296 READING_HEADERS,
297 CHUNK_SIZE,
298 CHUNK_BODY,
299 CHUNK_END,
300 READING_BODY_BY_SIZE,
301 DONE
302 };
303 status_type status = status_type::READING_HEADERS;
304
305 bool read(const temporary_buffer<char>& b) {
306 for (auto c : b) {
307 if (last =='\r' && c == '\n') {
308 if (_current == "") {
309 if (status == status_type::READING_HEADERS || (status == status_type::CHUNK_BODY && _remain == 0)) {
310 if (status == status_type::READING_HEADERS && _headers.find("Content-Length") != _headers.end()) {
311 _remain = stoi(_headers["Content-Length"], nullptr, 16);
312 if (_remain == 0) {
313 status = status_type::DONE;
314 break;
315 }
316 status = status_type::READING_BODY_BY_SIZE;
317 } else {
318 status = status_type::CHUNK_SIZE;
319 }
320 } else if (status == status_type::CHUNK_END) {
321 status = status_type::DONE;
322 break;
323 }
324 } else {
325 switch (status) {
326 case status_type::READING_HEADERS: add_header(_current);
327 break;
328 case status_type::CHUNK_SIZE: set_chunk(_current);
329 break;
330 default:
331 break;
332 }
333 _current = "";
334 }
335 last = '\0';
336 } else {
337 if (last != '\0') {
338 if (status == status_type::CHUNK_BODY || status == status_type::READING_BODY_BY_SIZE) {
339 if (_concat) {
340 _body = _body + last;
341 }
342 _size++;
343 _remain--;
344 if (_remain <= 1 && status == status_type::READING_BODY_BY_SIZE) {
345 if (_concat) {
346 _body = _body + c;
347 }
348 _size++;
349 status = status_type::DONE;
350 break;
351 }
352 } else {
353 _current = _current + last;
354 }
355
356 }
357 last = c;
358 }
359 }
360 return status == status_type::DONE;
361 }
362
363 void set_chunk(const std::string& s) {
364 _remain = stoi(s, nullptr, 16);
365 if (_remain == 0) {
366 status = status_type::CHUNK_END;
367 } else {
368 status = status_type::CHUNK_BODY;
369 }
370 }
371
372 void add_header(const std::string& s) {
373 std::vector<std::string> strs;
374 boost::split(strs, s, boost::is_any_of(":"));
375 if (strs.size() > 1) {
376 _headers[strs[0]] = strs[1];
377 }
378 }
379 };
380
381 class test_client_server {
382 public:
383 static future<> write_request(output_stream<char>& output) {
384 return output.write(sstring("GET /test HTTP/1.1\r\nHost: myhost.org\r\n\r\n")).then([&output]{
385 return output.flush();
386 });
387 }
388
389 static future<> run_test(std::function<future<>(output_stream<char> &&)>&& write_func, std::function<bool(size_t, http_consumer&)> reader) {
390 return do_with(loopback_connection_factory(), foreign_ptr<shared_ptr<http_server>>(make_shared<http_server>("test")),
391 [reader, &write_func] (loopback_connection_factory& lcf, auto& server) {
392 return do_with(loopback_socket_impl(lcf), [&server, &lcf, reader, &write_func](loopback_socket_impl& lsi) {
393 httpd::http_server_tester::listeners(*server).emplace_back(lcf.get_server_socket());
394
395 auto client = seastar::async([&lsi, reader] {
396 connected_socket c_socket = std::get<connected_socket>(lsi.connect(socket_address(ipv4_addr()), socket_address(ipv4_addr())).get());
397 input_stream<char> input(std::move(c_socket.input()));
398 output_stream<char> output(std::move(c_socket.output()));
399 bool more = true;
400 size_t count = 0;
401 while (more) {
402 http_consumer htp;
403 htp._concat = false;
404
405 write_request(output).get();
406 repeat([&c_socket, &input, &htp] {
407 return input.read().then([&c_socket, &input, &htp](const temporary_buffer<char>& b) mutable {
408 return (b.size() == 0 || htp.read(b)) ? make_ready_future<stop_iteration>(stop_iteration::yes) :
409 make_ready_future<stop_iteration>(stop_iteration::no);
410 });
411 }).get();
412 std::cout << htp._body << std::endl;
413 more = reader(count, htp);
414 count++;
415 }
416 if (input.eof()) {
417 input.close().get();
418 }
419 });
420
421 auto writer = seastar::async([&server, &write_func] {
422 class test_handler : public handler_base {
423 size_t count = 0;
424 http_server& _server;
425 std::function<future<>(output_stream<char> &&)> _write_func;
426 promise<> _all_message_sent;
427 public:
428 test_handler(http_server& server, std::function<future<>(output_stream<char> &&)>&& write_func) : _server(server), _write_func(write_func) {
429 }
430 future<std::unique_ptr<reply>> handle(const sstring& path,
431 std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
432 rep->write_body("json", std::move(_write_func));
433 count++;
434 _all_message_sent.set_value();
435 return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
436 }
437 future<> wait_for_message() {
438 return _all_message_sent.get_future();
439 }
440 };
441 auto handler = new test_handler(*server, std::move(write_func));
442 server->_routes.put(GET, "/test", handler);
443 when_all(server->do_accepts(0), handler->wait_for_message()).get();
444 });
445 return when_all(std::move(client), std::move(writer));
446 }).discard_result().then_wrapped([&server] (auto f) {
447 f.ignore_ready_future();
448 return server->stop();
449 });
450 });
451 }
452 static future<> run(std::vector<std::tuple<bool, size_t>> tests) {
453 return do_with(loopback_connection_factory(), foreign_ptr<shared_ptr<http_server>>(make_shared<http_server>("test")),
454 [tests] (loopback_connection_factory& lcf, auto& server) {
455 return do_with(loopback_socket_impl(lcf), [&server, &lcf, tests](loopback_socket_impl& lsi) {
456 httpd::http_server_tester::listeners(*server).emplace_back(lcf.get_server_socket());
457
458 auto client = seastar::async([&lsi, tests] {
459 connected_socket c_socket = std::get<connected_socket>(lsi.connect(socket_address(ipv4_addr()), socket_address(ipv4_addr())).get());
460 input_stream<char> input(std::move(c_socket.input()));
461 output_stream<char> output(std::move(c_socket.output()));
462 bool more = true;
463 size_t count = 0;
464 while (more) {
465 http_consumer htp;
466 write_request(output).get();
467 repeat([&c_socket, &input, &htp] {
468 return input.read().then([&c_socket, &input, &htp](const temporary_buffer<char>& b) mutable {
469 return (b.size() == 0 || htp.read(b)) ? make_ready_future<stop_iteration>(stop_iteration::yes) :
470 make_ready_future<stop_iteration>(stop_iteration::no);
471 });
472 }).get();
473 if (std::get<bool>(tests[count])) {
474 BOOST_REQUIRE_EQUAL(htp._body.length(), std::get<size_t>(tests[count]));
475 } else {
476 BOOST_REQUIRE_EQUAL(input.eof(), true);
477 more = false;
478 }
479 count++;
480 if (count == tests.size()) {
481 more = false;
482 }
483 }
484 if (input.eof()) {
485 input.close().get();
486 }
487 });
488
489 auto writer = seastar::async([&server, tests] {
490 class test_handler : public handler_base {
491 size_t count = 0;
492 http_server& _server;
493 std::vector<std::tuple<bool, size_t>> _tests;
494 promise<> _all_message_sent;
495 public:
496 test_handler(http_server& server, const std::vector<std::tuple<bool, size_t>>& tests) : _server(server), _tests(tests) {
497 }
498 future<std::unique_ptr<reply>> handle(const sstring& path,
499 std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
500 rep->write_body("txt", make_writer(std::get<size_t>(_tests[count]), std::get<bool>(_tests[count])));
501 count++;
502 if (count == _tests.size()) {
503 _all_message_sent.set_value();
504 }
505 return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
506 }
507 future<> wait_for_message() {
508 return _all_message_sent.get_future();
509 }
510 };
511 auto handler = new test_handler(*server, tests);
512 server->_routes.put(GET, "/test", handler);
513 when_all(server->do_accepts(0), handler->wait_for_message()).get();
514 });
515 return when_all(std::move(client), std::move(writer));
516 }).discard_result().then_wrapped([&server] (auto f) {
517 f.ignore_ready_future();
518 return server->stop();
519 });
520 });
521 }
522
523 static noncopyable_function<future<>(output_stream<char>&& o_stream)> make_writer(size_t len, bool success) {
524 return [len, success] (output_stream<char>&& o_stream) mutable {
525 return do_with(output_stream<char>(std::move(o_stream)), uint32_t(len/10), [success](output_stream<char>& str, uint32_t& remain) {
526 if (remain == 0) {
527 if (success) {
528 return str.close();
529 } else {
530 throw std::runtime_error("Throwing exception before writing");
531 }
532 }
533 return repeat([&str, &remain, success] () mutable {
534 return str.write("1234567890").then([&remain]() mutable {
535 remain--;
536 return (remain == 0)? make_ready_future<stop_iteration>(stop_iteration::yes) : make_ready_future<stop_iteration>(stop_iteration::no);
537 });
538 }).then([&str, success] {
539 if (!success) {
540 return str.flush();
541 }
542 return make_ready_future<>();
543 }).then([&str, success] {
544 if (success) {
545 return str.close();
546 } else {
547 throw std::runtime_error("Throwing exception after writing");
548 }
549 });
550 });
551 };
552 }
553 };
554
555 SEASTAR_TEST_CASE(test_message_with_error_non_empty_body) {
556 std::vector<std::tuple<bool, size_t>> tests = {
557 std::make_tuple(true, 100),
558 std::make_tuple(false, 10000)};
559 return test_client_server::run(tests);
560 }
561
562 SEASTAR_TEST_CASE(test_simple_chunked) {
563 std::vector<std::tuple<bool, size_t>> tests = {
564 std::make_tuple(true, 100000),
565 std::make_tuple(true, 100)};
566 return test_client_server::run(tests);
567 }
568
569 SEASTAR_TEST_CASE(test_http_client_server_full) {
570 std::vector<std::tuple<bool, size_t>> tests = {
571 std::make_tuple(true, 100),
572 std::make_tuple(true, 10000),
573 std::make_tuple(true, 100),
574 std::make_tuple(true, 0),
575 std::make_tuple(true, 5000),
576 std::make_tuple(true, 10000),
577 std::make_tuple(true, 9000),
578 std::make_tuple(true, 10000)};
579 return test_client_server::run(tests);
580 }
581
582 /*
583 * return string in the given size
584 * The string size takes the quotes into consideration.
585 */
586 std::string get_value(int size) {
587 std::stringstream res;
588 for (auto i = 0; i < size - 2; i++) {
589 res << "a";
590 }
591 return res.str();
592 }
593
594 /*
595 * A helper object that map to a big json string
596 * in the format of:
597 * {"valu": "aaa....aa", "valu": "aaa....aa", "valu": "aaa....aa"...}
598 *
599 * The object can have an arbitrary size in multiplication of 10000 bytes
600 * */
601 struct extra_big_object : public json::json_base {
602 json::json_element<sstring>* value;
603 extra_big_object(size_t size) {
604 value = new json::json_element<sstring>;
605 // size = brackets + (name + ": " + get_value) * n + ", " * (n-1)
606 // size = 2 + (name + 6 + get_value) * n - 2
607 value->_name = "valu";
608 *value = get_value(9990);
609 for (size_t i = 0; i < size/10000; i++) {
610 _elements.emplace_back(value);
611 }
612 }
613
614 virtual ~extra_big_object() {
615 delete value;
616 }
617
618 extra_big_object(const extra_big_object& o) {
619 value = new json::json_element<sstring>;
620 value->_name = o.value->_name;
621 *value = (*o.value)();
622 for (size_t i = 0; i < o._elements.size(); i++) {
623 _elements.emplace_back(value);
624 }
625 }
626
627 extra_big_object(extra_big_object&&) = default;
628 };
629
630 SEASTAR_TEST_CASE(json_stream) {
631 std::vector<extra_big_object> vec;
632 size_t num_objects = 1000;
633 size_t total_size = num_objects * 1000001 + 1;
634 for (size_t i = 0; i < num_objects; i++) {
635 vec.emplace_back(extra_big_object(1000000));
636 }
637 return test_client_server::run_test(json::stream_object(vec), [total_size](size_t s, http_consumer& h) {
638 BOOST_REQUIRE_EQUAL(h._size, total_size);
639 return false;
640 });
641 }