2 * Copyright 2015 Cloudius Systems
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>
22 using namespace seastar
;
23 using namespace httpd
;
25 class handl
: public httpd::handler_base
{
27 virtual future
<std::unique_ptr
<reply
> > handle(const sstring
& path
,
28 std::unique_ptr
<request
> req
, std::unique_ptr
<reply
> rep
) {
30 return make_ready_future
<std::unique_ptr
<reply
>>(std::move(rep
));
34 SEASTAR_TEST_CASE(test_reply
)
37 r
.set_content_type("txt");
38 BOOST_REQUIRE_EQUAL(r
._headers
["Content-Type"], sstring("text/plain"));
39 return make_ready_future
<>();
42 SEASTAR_TEST_CASE(test_str_matcher
)
45 str_matcher
m("/hello");
47 BOOST_REQUIRE_EQUAL(m
.match("/abc/hello", 4, param
), 10u);
48 return make_ready_future
<>();
51 SEASTAR_TEST_CASE(test_param_matcher
)
54 param_matcher
m("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
<>();
62 SEASTAR_TEST_CASE(test_match_rule
)
66 handl
* h
= new handl();
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
<>();
78 SEASTAR_TEST_CASE(test_formatter
)
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\"");
86 BOOST_REQUIRE_EQUAL(json::formatter::to_json(str
), "\"abc\"");
88 BOOST_REQUIRE_EQUAL(json::formatter::to_json(f
), "1");
90 BOOST_CHECK_THROW(json::formatter::to_json(f
), std::out_of_range
);
92 BOOST_CHECK_THROW(json::formatter::to_json(f
), std::out_of_range
);
94 BOOST_CHECK_THROW(json::formatter::to_json(f
), std::invalid_argument
);
96 BOOST_REQUIRE_EQUAL(json::formatter::to_json(d
), "-1");
98 BOOST_CHECK_THROW(json::formatter::to_json(d
), std::out_of_range
);
100 BOOST_CHECK_THROW(json::formatter::to_json(d
), std::out_of_range
);
102 BOOST_CHECK_THROW(json::formatter::to_json(d
), std::invalid_argument
);
103 return make_ready_future
<>();
106 SEASTAR_TEST_CASE(test_decode_url
) {
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
<>();
119 SEASTAR_TEST_CASE(test_routes
) {
120 handl
* h1
= new handl();
121 handl
* h2
= new handl();
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
>();
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
);
133 req
.reset(new request
);
134 rep
.reset(new reply
);
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
);
141 req
.reset(new request
);
142 rep
.reset(new reply
);
144 route
.handle("/api/abc", std::move(req
), std::move(rep
)).then(
145 [] (std::unique_ptr
<reply
> rep
) {
147 req
.reset(new request
);
148 rep
.reset(new reply
);
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
);
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();
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
}},{});
179 path1
.set(*route
, [res1
] (const_req req
) {
181 BOOST_REQUIRE_EQUAL(req
.param
["param1"], "value1");
185 path2
.set(*route
, [res2
] (const_req req
) {
187 BOOST_REQUIRE_EQUAL(req
.param
["param1"], "value2");
188 BOOST_REQUIRE_EQUAL(req
.param
["param2"], "text1");
192 path3
.set(*route
, [res3
] (const_req req
) {
194 BOOST_REQUIRE_EQUAL(req
.param
["param1"], "value3");
195 BOOST_REQUIRE_EQUAL(req
.param
["param2"], "text2/text3");
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);
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);
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);
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();
220 * \brief a helper data sink that stores everything it gets in a stringstream
222 class memory_data_sink_impl
: public data_sink_impl
{
223 std::stringstream
& _ss
;
225 memory_data_sink_impl(std::stringstream
& ss
) : _ss(ss
) {
227 virtual future
<> put(net::packet data
) override
{
229 return make_ready_future
<>();
231 virtual future
<> put(temporary_buffer
<char> buf
) override
{
232 _ss
.write(buf
.get(), buf
.size());
233 return make_ready_future
<>();
235 virtual future
<> flush() override
{
236 return make_ready_future
<>();
239 virtual future
<> close() override
{
240 return make_ready_future
<>();
244 class memory_data_sink
: public data_sink
{
246 memory_data_sink(std::stringstream
& ss
)
247 : data_sink(std::make_unique
<memory_data_sink_impl
>(ss
)) {}
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
>();
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
)), [] (output_stream
<char>& os
, std::vector
<sstring
>& parts
) {
256 return do_for_each(parts
, [&os
](auto& p
) {
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 [] (output_stream
<char>& os
) {
268 return os
.write(sstring("hello-{{Protocol}}-xyz-{{Host}}")).then([&os
] {
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
] {
278 BOOST_REQUIRE_EQUAL(ss
.str(), "hello-{{Prothttpocol}}localhost");
286 struct http_consumer
{
287 std::map
<sstring
, std::string
> _headers
;
289 uint32_t _remain
= 0;
290 std::string _current
;
295 enum class status_type
{
300 READING_BODY_BY_SIZE
,
303 status_type status
= status_type::READING_HEADERS
;
305 bool read(const temporary_buffer
<char>& 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);
313 status
= status_type::DONE
;
316 status
= status_type::READING_BODY_BY_SIZE
;
318 status
= status_type::CHUNK_SIZE
;
320 } else if (status
== status_type::CHUNK_END
) {
321 status
= status_type::DONE
;
326 case status_type::READING_HEADERS
: add_header(_current
);
328 case status_type::CHUNK_SIZE
: set_chunk(_current
);
338 if (status
== status_type::CHUNK_BODY
|| status
== status_type::READING_BODY_BY_SIZE
) {
340 _body
= _body
+ last
;
344 if (_remain
<= 1 && status
== status_type::READING_BODY_BY_SIZE
) {
349 status
= status_type::DONE
;
353 _current
= _current
+ last
;
360 return status
== status_type::DONE
;
363 void set_chunk(const std::string
& s
) {
364 _remain
= stoi(s
, nullptr, 16);
366 status
= status_type::CHUNK_END
;
368 status
= status_type::CHUNK_BODY
;
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];
381 class test_client_server
{
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();
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());
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(c_socket
.input());
398 output_stream
<char> output(c_socket
.output());
405 write_request(output
).get();
406 repeat([&input
, &htp
] {
407 return input
.read().then([&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
);
412 std::cout
<< htp
._body
<< std::endl
;
413 more
= reader(count
, htp
);
421 auto writer
= seastar::async([&server
, &write_func
] {
422 class test_handler
: public handler_base
{
424 http_server
& _server
;
425 std::function
<future
<>(output_stream
<char> &&)> _write_func
;
426 promise
<> _all_message_sent
;
428 test_handler(http_server
& server
, std::function
<future
<>(output_stream
<char> &&)>&& write_func
) : _server(server
), _write_func(write_func
) {
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
));
434 _all_message_sent
.set_value();
435 return make_ready_future
<std::unique_ptr
<reply
>>(std::move(rep
));
437 future
<> wait_for_message() {
438 return _all_message_sent
.get_future();
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();
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();
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());
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(c_socket
.input());
461 output_stream
<char> output(c_socket
.output());
466 write_request(output
).get();
467 repeat([&input
, &htp
] {
468 return input
.read().then([&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
);
473 if (std::get
<bool>(tests
[count
])) {
474 BOOST_REQUIRE_EQUAL(htp
._body
.length(), std::get
<size_t>(tests
[count
]));
476 BOOST_REQUIRE_EQUAL(input
.eof(), true);
480 if (count
== tests
.size()) {
489 auto writer
= seastar::async([&server
, tests
] {
490 class test_handler
: public handler_base
{
492 http_server
& _server
;
493 std::vector
<std::tuple
<bool, size_t>> _tests
;
494 promise
<> _all_message_sent
;
496 test_handler(http_server
& server
, const std::vector
<std::tuple
<bool, size_t>>& tests
) : _server(server
), _tests(tests
) {
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
])));
502 if (count
== _tests
.size()) {
503 _all_message_sent
.set_value();
505 return make_ready_future
<std::unique_ptr
<reply
>>(std::move(rep
));
507 future
<> wait_for_message() {
508 return _all_message_sent
.get_future();
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();
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();
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
) {
530 throw std::runtime_error("Throwing exception before writing");
533 return repeat([&str
, &remain
] () mutable {
534 return str
.write("1234567890").then([&remain
]() mutable {
536 return (remain
== 0)? make_ready_future
<stop_iteration
>(stop_iteration::yes
) : make_ready_future
<stop_iteration
>(stop_iteration::no
);
538 }).then([&str
, success
] {
542 return make_ready_future
<>();
543 }).then([&str
, success
] {
547 throw std::runtime_error("Throwing exception after writing");
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
);
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
);
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
);
583 * return string in the given size
584 * The string size takes the quotes into consideration.
586 std::string
get_value(int size
) {
587 std::stringstream res
;
588 for (auto i
= 0; i
< size
- 2; i
++) {
595 * A helper object that map to a big json string
597 * {"valu": "aaa....aa", "valu": "aaa....aa", "valu": "aaa....aa"...}
599 * The object can have an arbitrary size in multiplication of 10000 bytes
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
);
614 virtual ~extra_big_object() {
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
);
628 SEASTAR_TEST_CASE(json_stream
) {
629 std::vector
<extra_big_object
> vec
;
630 size_t num_objects
= 1000;
631 size_t total_size
= num_objects
* 1000001 + 1;
632 for (size_t i
= 0; i
< num_objects
; i
++) {
633 vec
.emplace_back(1000000);
635 return test_client_server::run_test(json::stream_object(vec
), [total_size
](size_t s
, http_consumer
& h
) {
636 BOOST_REQUIRE_EQUAL(h
._size
, total_size
);
641 SEASTAR_TEST_CASE(content_length_limit
) {
642 return seastar::async([] {
643 loopback_connection_factory lcf
;
644 http_server
server("test");
645 server
.set_content_length_limit(11);
646 loopback_socket_impl
lsi(lcf
);
647 httpd::http_server_tester::listeners(server
).emplace_back(lcf
.get_server_socket());
649 future
<> client
= seastar::async([&lsi
] {
650 connected_socket c_socket
= std::get
<connected_socket
>(lsi
.connect(socket_address(ipv4_addr()), socket_address(ipv4_addr())).get());
651 input_stream
<char> input(c_socket
.input());
652 output_stream
<char> output(c_socket
.output());
654 output
.write(sstring("GET /test HTTP/1.1\r\nHost: test\r\n\r\n")).get();
655 output
.flush().get();
656 auto resp
= input
.read().get0();
657 BOOST_REQUIRE_NE(std::string(resp
.get(), resp
.size()).find("200 OK"), std::string::npos
);
659 output
.write(sstring("GET /test HTTP/1.1\r\nHost: test\r\nContent-Length: 11\r\n\r\nxxxxxxxxxxx")).get();
660 output
.flush().get();
661 resp
= input
.read().get0();
662 BOOST_REQUIRE_NE(std::string(resp
.get(), resp
.size()).find("200 OK"), std::string::npos
);
664 output
.write(sstring("GET /test HTTP/1.1\r\nHost: test\r\nContent-Length: 17\r\n\r\nxxxxxxxxxxxxxxxx")).get();
665 output
.flush().get();
666 resp
= input
.read().get0();
667 BOOST_REQUIRE_EQUAL(std::string(resp
.get(), resp
.size()).find("200 OK"), std::string::npos
);
668 BOOST_REQUIRE_NE(std::string(resp
.get(), resp
.size()).find("413 Payload Too Large"), std::string::npos
);
671 output
.close().get();
674 future
<> writer
= seastar::async([&server
] {
675 class test_handler
: public handler_base
{
677 http_server
& _server
;
678 std::function
<future
<>(output_stream
<char> &&)> _write_func
;
680 test_handler(http_server
& server
, std::function
<future
<>(output_stream
<char> &&)>&& write_func
) : _server(server
), _write_func(write_func
) {
682 future
<std::unique_ptr
<reply
>> handle(const sstring
& path
,
683 std::unique_ptr
<request
> req
, std::unique_ptr
<reply
> rep
) override
{
684 rep
->write_body("json", _write_func
);
685 return make_ready_future
<std::unique_ptr
<reply
>>(std::move(rep
));
688 auto handler
= new test_handler(server
, json::stream_object("hello"));
689 server
._routes
.put(GET
, "/test", handler
);
690 server
.do_accepts(0).get();