| #include <iostream> |
| #include <sstream> |
| #include <vector> |
| |
| #include "crow.h" |
| |
| #include "gtest/gtest.h" |
| |
| using namespace std; |
| using namespace crow; |
| |
| bool failed__ = false; |
| void error_print() |
| { |
| cerr << endl; |
| } |
| |
| template <typename A, typename... Args> |
| void error_print(const A& a, Args... args) |
| { |
| cerr << a; |
| error_print(args...); |
| } |
| |
| template <typename... Args> void fail(Args... args) |
| { |
| error_print(args...); |
| failed__ = true; |
| } |
| |
| #define ASSERT_EQUAL(a, b) \ |
| if ((a) != (b)) \ |
| fail(__FILE__ ":", __LINE__, ": Assert fail: expected ", (a), " actual ", \ |
| (b), ", " #a " == " #b ", at " __FILE__ ":", __LINE__) |
| #define ASSERT_NOTEQUAL(a, b) \ |
| if ((a) == (b)) \ |
| fail(__FILE__ ":", __LINE__, ": Assert fail: not expected ", (a), \ |
| ", " #a " != " #b ", at " __FILE__ ":", __LINE__) |
| |
| #define DISABLE_TEST(x) \ |
| struct test##x \ |
| { \ |
| void test(); \ |
| } x##_; \ |
| void test##x::test() |
| |
| #define LOCALHOST_ADDRESS "127.0.0.1" |
| |
| TEST(Crow, Rule) |
| { |
| TaggedRule<> r("/http/"); |
| r.name("abc"); |
| |
| // empty handler - fail to validate |
| try |
| { |
| r.validate(); |
| fail("empty handler should fail to validate"); |
| } |
| catch (runtime_error& e) |
| { |
| } |
| |
| int x = 0; |
| |
| // registering handler |
| r([&x] { |
| x = 1; |
| return ""; |
| }); |
| |
| r.validate(); |
| |
| Response res; |
| |
| // executing handler |
| ASSERT_EQUAL(0, x); |
| boost::beast::http::request<boost::beast::http::string_body> req{}; |
| r.handle(Request(req), res, RoutingParams()); |
| ASSERT_EQUAL(1, x); |
| |
| // registering handler with Request argument |
| r([&x](const crow::Request&) { |
| x = 2; |
| return ""; |
| }); |
| |
| r.validate(); |
| |
| // executing handler |
| ASSERT_EQUAL(1, x); |
| r.handle(Request(req), res, RoutingParams()); |
| ASSERT_EQUAL(2, x); |
| } |
| |
| TEST(Crow, ParameterTagging) |
| { |
| static_assert(black_magic::isValid("<int><int><int>"), "valid url"); |
| static_assert(!black_magic::isValid("<int><int<<int>"), "invalid url"); |
| static_assert(!black_magic::isValid("nt>"), "invalid url"); |
| ASSERT_EQUAL(1, black_magic::get_parameter_tag("<int>")); |
| ASSERT_EQUAL(2, black_magic::get_parameter_tag("<uint>")); |
| ASSERT_EQUAL(3, black_magic::get_parameter_tag("<float>")); |
| ASSERT_EQUAL(3, black_magic::get_parameter_tag("<double>")); |
| ASSERT_EQUAL(4, black_magic::get_parameter_tag("<str>")); |
| ASSERT_EQUAL(4, black_magic::get_parameter_tag("<string>")); |
| ASSERT_EQUAL(5, black_magic::get_parameter_tag("<path>")); |
| ASSERT_EQUAL(6 * 6 + 6 + 1, |
| black_magic::get_parameter_tag("<int><int><int>")); |
| ASSERT_EQUAL(6 * 6 + 6 + 2, |
| black_magic::get_parameter_tag("<uint><int><int>")); |
| ASSERT_EQUAL(6 * 6 + 6 * 3 + 2, |
| black_magic::get_parameter_tag("<uint><double><int>")); |
| |
| // url definition parsed in compile time, build into *one number*, and given |
| // to template argument |
| static_assert( |
| std::is_same<black_magic::S<uint64_t, double, int64_t>, |
| black_magic::Arguments<6 * 6 + 6 * 3 + 2>::type>::value, |
| "tag to type container"); |
| } |
| |
| TEST(Crow, PathRouting) |
| { |
| SimpleApp app; |
| |
| BMCWEB_ROUTE(app, "/file") |
| ([] { return "file"; }); |
| |
| BMCWEB_ROUTE(app, "/path/") |
| ([] { return "path"; }); |
| |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/file"; |
| |
| app.handle(req, res); |
| |
| ASSERT_EQUAL(200, res.resultInt()); |
| } |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/file/"; |
| |
| app.handle(req, res); |
| ASSERT_EQUAL(404, res.resultInt()); |
| } |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/path"; |
| |
| app.handle(req, res); |
| ASSERT_NOTEQUAL(404, res.resultInt()); |
| } |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/path/"; |
| |
| app.handle(req, res); |
| ASSERT_EQUAL(200, res.resultInt()); |
| } |
| } |
| |
| TEST(Crow, RoutingTest) |
| { |
| SimpleApp app; |
| int A{}; |
| uint32_t b{}; |
| double C{}; |
| string D{}; |
| string E{}; |
| |
| BMCWEB_ROUTE(app, "/0/<uint>") |
| ([&](uint32_t b) { |
| b = b; |
| return "OK"; |
| }); |
| |
| BMCWEB_ROUTE(app, "/1/<int>/<uint>") |
| ([&](int a, uint32_t b) { |
| A = a; |
| b = b; |
| return "OK"; |
| }); |
| |
| BMCWEB_ROUTE(app, "/4/<int>/<uint>/<double>/<string>") |
| ([&](int a, uint32_t b, double c, string d) { |
| A = a; |
| b = b; |
| C = c; |
| D = d; |
| return "OK"; |
| }); |
| |
| BMCWEB_ROUTE(app, "/5/<int>/<uint>/<double>/<string>/<path>") |
| ([&](int a, uint32_t b, double c, string d, string e) { |
| A = a; |
| b = b; |
| C = c; |
| D = d; |
| E = e; |
| return "OK"; |
| }); |
| |
| app.validate(); |
| // app.debugPrint(); |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/-1"; |
| |
| app.handle(req, res); |
| |
| ASSERT_EQUAL(404, res.resultInt()); |
| } |
| |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/0/1001999"; |
| |
| app.handle(req, res); |
| |
| ASSERT_EQUAL(200, res.resultInt()); |
| |
| ASSERT_EQUAL(1001999, b); |
| } |
| |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/1/-100/1999"; |
| |
| app.handle(req, res); |
| |
| ASSERT_EQUAL(200, res.resultInt()); |
| |
| ASSERT_EQUAL(-100, A); |
| ASSERT_EQUAL(1999, b); |
| } |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/4/5000/3/-2.71828/hellhere"; |
| |
| app.handle(req, res); |
| |
| ASSERT_EQUAL(200, res.resultInt()); |
| |
| ASSERT_EQUAL(5000, A); |
| ASSERT_EQUAL(3, b); |
| ASSERT_EQUAL(-2.71828, C); |
| ASSERT_EQUAL("hellhere", D); |
| } |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/5/-5/999/3.141592/hello_there/a/b/c/d"; |
| |
| app.handle(req, res); |
| |
| ASSERT_EQUAL(200, res.resultInt()); |
| |
| ASSERT_EQUAL(-5, A); |
| ASSERT_EQUAL(999, b); |
| ASSERT_EQUAL(3.141592, C); |
| ASSERT_EQUAL("hello_there", D); |
| ASSERT_EQUAL("a/b/c/d", E); |
| } |
| } |
| |
| TEST(Crow, simple_response_RoutingParams) |
| { |
| ASSERT_EQUAL(100, |
| Response(boost::beast::http::status::continue_).resultInt()); |
| ASSERT_EQUAL(200, Response("Hello there").resultInt()); |
| ASSERT_EQUAL(500, |
| Response(boost::beast::http::status::internal_server_error, |
| "Internal Error?") |
| .resultInt()); |
| |
| RoutingParams rp; |
| rp.intParams.push_back(1); |
| rp.intParams.push_back(5); |
| rp.uintParams.push_back(2); |
| rp.doubleParams.push_back(3); |
| rp.stringParams.push_back("hello"); |
| ASSERT_EQUAL(1, rp.get<int64_t>(0)); |
| ASSERT_EQUAL(5, rp.get<int64_t>(1)); |
| ASSERT_EQUAL(2, rp.get<uint64_t>(0)); |
| ASSERT_EQUAL(3, rp.get<double>(0)); |
| ASSERT_EQUAL("hello", rp.get<string>(0)); |
| } |
| |
| TEST(Crow, handler_with_response) |
| { |
| SimpleApp app; |
| BMCWEB_ROUTE(app, "/")([](const crow::Request&, crow::Response&) {}); |
| } |
| |
| TEST(Crow, http_method) |
| { |
| SimpleApp app; |
| |
| BMCWEB_ROUTE(app, "/").methods("POST"_method, |
| "GET"_method)([](const Request& req) { |
| if (req.method() == "GET"_method) |
| return "2"; |
| else |
| return "1"; |
| }); |
| |
| BMCWEB_ROUTE(app, "/get_only") |
| .methods("GET"_method)([](const Request& /*req*/) { return "get"; }); |
| BMCWEB_ROUTE(app, "/post_only") |
| .methods("POST"_method)([](const Request& /*req*/) { return "post"; }); |
| |
| // cannot have multiple handlers for the same url |
| // BMCWEB_ROUTE(app, "/") |
| //.methods("GET"_method) |
| //([]{ return "2"; }); |
| |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/"; |
| app.handle(req, res); |
| |
| ASSERT_EQUAL("2", res.body()); |
| } |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/"; |
| r.method("POST"_method); |
| app.handle(req, res); |
| |
| ASSERT_EQUAL("1", res.body()); |
| } |
| |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/get_only"; |
| app.handle(req, res); |
| |
| ASSERT_EQUAL("get", res.body()); |
| } |
| |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| |
| req.url = "/get_only"; |
| r.method("POST"_method); |
| app.handle(req, res); |
| |
| ASSERT_NOTEQUAL("get", res.body()); |
| } |
| } |
| |
| TEST(Crow, server_handling_error_request) |
| { |
| static char buf[2048]; |
| SimpleApp app; |
| BMCWEB_ROUTE(app, "/")([] { return "A"; }); |
| Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451); |
| auto _ = async(launch::async, [&] { server.run(); }); |
| std::string sendmsg = "POX"; |
| asio::io_context is; |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| |
| c.send(asio::buffer(sendmsg)); |
| |
| try |
| { |
| c.receive(asio::buffer(buf, 2048)); |
| fail(); |
| } |
| catch (std::exception& e) |
| { |
| // std::cerr << e.what() << std::endl; |
| } |
| } |
| server.stop(); |
| } |
| |
| TEST(Crow, multi_server) |
| { |
| static char buf[2048]; |
| SimpleApp app1, app2; |
| BMCWEB_ROUTE(app1, "/").methods("GET"_method, |
| "POST"_method)([] { return "A"; }); |
| BMCWEB_ROUTE(app2, "/").methods("GET"_method, |
| "POST"_method)([] { return "B"; }); |
| |
| Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451); |
| Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452); |
| |
| auto _ = async(launch::async, [&] { server1.run(); }); |
| auto _2 = async(launch::async, [&] { server2.run(); }); |
| |
| std::string sendmsg = |
| "POST /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=b\r\n"; |
| asio::io_context is; |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| |
| c.send(asio::buffer(sendmsg)); |
| |
| size_t recved = c.receive(asio::buffer(buf, 2048)); |
| ASSERT_EQUAL('A', buf[recved - 1]); |
| } |
| |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45452)); |
| |
| for (auto ch : sendmsg) |
| { |
| char buf[1] = {ch}; |
| c.send(asio::buffer(buf)); |
| } |
| |
| size_t recved = c.receive(asio::buffer(buf, 2048)); |
| ASSERT_EQUAL('b', buf[recved - 1]); |
| } |
| |
| server1.stop(); |
| server2.stop(); |
| } |
| |
| TEST(Crow, black_magic) |
| { |
| using namespace black_magic; |
| static_assert( |
| std::is_same<void, LastElementType<int, char, void>::type>::value, |
| "LastElementType"); |
| static_assert( |
| std::is_same<char, PopBack<int, char, |
| void>::rebind<LastElementType>::type>::value, |
| "pop_back"); |
| static_assert( |
| std::is_same<int, PopBack<int, char, void>::rebind<PopBack>::rebind< |
| LastElementType>::type>::value, |
| "pop_back"); |
| } |
| |
| struct NullMiddleware |
| { |
| struct Context |
| { |
| }; |
| |
| template <typename AllContext> |
| void beforeHandle(Request&, Response&, Context&, AllContext&) |
| { |
| } |
| |
| template <typename AllContext> |
| void afterHandle(Request&, Response&, Context&, AllContext&) |
| { |
| } |
| }; |
| |
| struct NullSimpleMiddleware |
| { |
| struct Context |
| { |
| }; |
| |
| void beforeHandle(Request& /*req*/, Response& /*res*/, Context& /*ctx*/) |
| { |
| } |
| |
| void afterHandle(Request& /*req*/, Response& /*res*/, Context& /*ctx*/) |
| { |
| } |
| }; |
| |
| TEST(Crow, middleware_simple) |
| { |
| App<NullMiddleware, NullSimpleMiddleware> app; |
| decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); |
| BMCWEB_ROUTE(app, "/") |
| ([&](const crow::Request& req) { |
| app.getContext<NullMiddleware>(req); |
| app.getContext<NullSimpleMiddleware>(req); |
| return ""; |
| }); |
| } |
| |
| struct IntSettingMiddleware |
| { |
| struct Context |
| { |
| int val; |
| }; |
| |
| template <typename AllContext> |
| void beforeHandle(Request&, Response&, Context& ctx, AllContext&) |
| { |
| ctx.val = 1; |
| } |
| |
| template <typename AllContext> |
| void afterHandle(Request&, Response&, Context& ctx, AllContext&) |
| { |
| ctx.val = 2; |
| } |
| }; |
| |
| std::vector<std::string> test_middleware_context_vector; |
| |
| struct FirstMW |
| { |
| struct Context |
| { |
| std::vector<string> v; |
| }; |
| |
| void beforeHandle(Request& /*req*/, Response& /*res*/, Context& ctx) |
| { |
| ctx.v.push_back("1 before"); |
| } |
| |
| void afterHandle(Request& /*req*/, Response& /*res*/, Context& ctx) |
| { |
| ctx.v.push_back("1 after"); |
| test_middleware_context_vector = ctx.v; |
| } |
| }; |
| |
| struct SecondMW |
| { |
| struct Context |
| { |
| }; |
| template <typename AllContext> |
| void beforeHandle(Request& req, Response& res, Context&, |
| AllContext& all_ctx) |
| { |
| all_ctx.template get<FirstMW>().v.push_back("2 before"); |
| if (req.url == "/break") |
| res.end(); |
| } |
| |
| template <typename AllContext> |
| void afterHandle(Request&, Response&, Context&, AllContext& all_ctx) |
| { |
| all_ctx.template get<FirstMW>().v.push_back("2 after"); |
| } |
| }; |
| |
| struct ThirdMW |
| { |
| struct Context |
| { |
| }; |
| template <typename AllContext> |
| void beforeHandle(Request&, Response&, Context&, AllContext& all_ctx) |
| { |
| all_ctx.template get<FirstMW>().v.push_back("3 before"); |
| } |
| |
| template <typename AllContext> |
| void afterHandle(Request&, Response&, Context&, AllContext& all_ctx) |
| { |
| all_ctx.template get<FirstMW>().v.push_back("3 after"); |
| } |
| }; |
| |
| TEST(Crow, middlewareContext) |
| { |
| static char buf[2048]; |
| // SecondMW depends on FirstMW (it uses all_ctx.get<FirstMW>) |
| // so it leads to compile error if we remove FirstMW from definition |
| // App<IntSettingMiddleware, SecondMW> app; |
| // or change the order of FirstMW and SecondMW |
| // App<IntSettingMiddleware, SecondMW, FirstMW> app; |
| |
| App<IntSettingMiddleware, FirstMW, SecondMW, ThirdMW> app; |
| |
| int x{}; |
| BMCWEB_ROUTE(app, "/") |
| ([&](const Request& req) { |
| { |
| auto& ctx = app.getContext<IntSettingMiddleware>(req); |
| x = ctx.val; |
| } |
| { |
| auto& ctx = app.getContext<FirstMW>(req); |
| ctx.v.push_back("handle"); |
| } |
| |
| return ""; |
| }); |
| BMCWEB_ROUTE(app, "/break") |
| ([&](const Request& req) { |
| { |
| auto& ctx = app.getContext<FirstMW>(req); |
| ctx.v.push_back("handle"); |
| } |
| |
| return ""; |
| }); |
| |
| decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); |
| auto _ = async(launch::async, [&] { server.run(); }); |
| std::string sendmsg = "GET /\r\n\r\n"; |
| asio::io_context is; |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| |
| c.send(asio::buffer(sendmsg)); |
| |
| c.receive(asio::buffer(buf, 2048)); |
| c.close(); |
| } |
| { |
| auto& out = test_middleware_context_vector; |
| ASSERT_EQUAL(1, x); |
| ASSERT_EQUAL(7, out.size()); |
| ASSERT_EQUAL("1 before", out[0]); |
| ASSERT_EQUAL("2 before", out[1]); |
| ASSERT_EQUAL("3 before", out[2]); |
| ASSERT_EQUAL("handle", out[3]); |
| ASSERT_EQUAL("3 after", out[4]); |
| ASSERT_EQUAL("2 after", out[5]); |
| ASSERT_EQUAL("1 after", out[6]); |
| } |
| std::string sendmsg2 = "GET /break\r\n\r\n"; |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| |
| c.send(asio::buffer(sendmsg2)); |
| |
| c.receive(asio::buffer(buf, 2048)); |
| c.close(); |
| } |
| { |
| auto& out = test_middleware_context_vector; |
| ASSERT_EQUAL(4, out.size()); |
| ASSERT_EQUAL("1 before", out[0]); |
| ASSERT_EQUAL("2 before", out[1]); |
| ASSERT_EQUAL("2 after", out[2]); |
| ASSERT_EQUAL("1 after", out[3]); |
| } |
| server.stop(); |
| } |
| |
| TEST(Crow, bug_quick_repeated_request) |
| { |
| static char buf[2048]; |
| |
| SimpleApp app; |
| |
| BMCWEB_ROUTE(app, "/")([&] { return "hello"; }); |
| |
| decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); |
| auto _ = async(launch::async, [&] { server.run(); }); |
| std::string sendmsg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; |
| asio::io_context is; |
| { |
| std::vector<std::future<void>> v; |
| for (int i = 0; i < 5; i++) |
| { |
| v.push_back(async(launch::async, [&] { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| |
| for (int j = 0; j < 5; j++) |
| { |
| c.send(asio::buffer(sendmsg)); |
| |
| size_t received = c.receive(asio::buffer(buf, 2048)); |
| ASSERT_EQUAL("hello", std::string(buf + received - 5, |
| buf + received)); |
| } |
| c.close(); |
| })); |
| } |
| } |
| server.stop(); |
| } |
| |
| TEST(Crow, simple_url_params) |
| { |
| static char buf[2048]; |
| |
| SimpleApp app; |
| |
| QueryString lastUrlParams; |
| |
| BMCWEB_ROUTE(app, "/params") |
| ([&lastUrlParams](const crow::Request& req) { |
| lastUrlParams = std::move(req.urlParams); |
| return "OK"; |
| }); |
| |
| /// params?h=1&foo=bar&lol&count[]=1&count[]=4&pew=5.2 |
| |
| decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); |
| auto _ = async(launch::async, [&] { server.run(); }); |
| asio::io_context is; |
| std::string sendmsg; |
| |
| // check empty params |
| sendmsg = "GET /params\r\n\r\n"; |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| c.send(asio::buffer(sendmsg)); |
| c.receive(asio::buffer(buf, 2048)); |
| c.close(); |
| |
| stringstream ss; |
| ss << lastUrlParams; |
| |
| ASSERT_EQUAL("[ ]", ss.str()); |
| } |
| // check single presence |
| sendmsg = "GET /params?foobar\r\n\r\n"; |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| c.send(asio::buffer(sendmsg)); |
| c.receive(asio::buffer(buf, 2048)); |
| c.close(); |
| |
| ASSERT_TRUE(lastUrlParams.get("missing") == nullptr); |
| ASSERT_TRUE(lastUrlParams.get("foobar") != nullptr); |
| ASSERT_TRUE(lastUrlParams.getList("missing").empty()); |
| } |
| // check multiple presence |
| sendmsg = "GET /params?foo&bar&baz\r\n\r\n"; |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| c.send(asio::buffer(sendmsg)); |
| c.receive(asio::buffer(buf, 2048)); |
| c.close(); |
| |
| ASSERT_TRUE(lastUrlParams.get("missing") == nullptr); |
| ASSERT_TRUE(lastUrlParams.get("foo") != nullptr); |
| ASSERT_TRUE(lastUrlParams.get("bar") != nullptr); |
| ASSERT_TRUE(lastUrlParams.get("baz") != nullptr); |
| } |
| // check single value |
| sendmsg = "GET /params?hello=world\r\n\r\n"; |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| c.send(asio::buffer(sendmsg)); |
| c.receive(asio::buffer(buf, 2048)); |
| c.close(); |
| |
| ASSERT_EQUAL(string(lastUrlParams.get("hello")), "world"); |
| } |
| // check multiple value |
| sendmsg = "GET /params?hello=world&left=right&up=down\r\n\r\n"; |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| c.send(asio::buffer(sendmsg)); |
| c.receive(asio::buffer(buf, 2048)); |
| c.close(); |
| |
| ASSERT_EQUAL(string(lastUrlParams.get("hello")), "world"); |
| ASSERT_EQUAL(string(lastUrlParams.get("left")), "right"); |
| ASSERT_EQUAL(string(lastUrlParams.get("up")), "down"); |
| } |
| // check multiple value, multiple types |
| sendmsg = "GET /params?int=100&double=123.45&boolean=1\r\n\r\n"; |
| { |
| asio::ip::tcp::socket c(is); |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| c.send(asio::buffer(sendmsg)); |
| c.receive(asio::buffer(buf, 2048)); |
| c.close(); |
| |
| ASSERT_EQUAL(boost::lexical_cast<int>(lastUrlParams.get("int")), 100); |
| ASSERT_EQUAL(boost::lexical_cast<double>(lastUrlParams.get("double")), |
| 123.45); |
| ASSERT_EQUAL(boost::lexical_cast<bool>(lastUrlParams.get("boolean")), |
| true); |
| } |
| // check single array value |
| sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n"; |
| { |
| asio::ip::tcp::socket c(is); |
| |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| c.send(asio::buffer(sendmsg)); |
| c.receive(asio::buffer(buf, 2048)); |
| c.close(); |
| |
| ASSERT_TRUE(lastUrlParams.get("tmnt") == nullptr); |
| ASSERT_EQUAL(lastUrlParams.getList("tmnt").size(), 1); |
| ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[0]), "leonardo"); |
| } |
| // check multiple array value |
| sendmsg = |
| "GET /params?tmnt[]=leonardo&tmnt[]=donatello&tmnt[]=raphael\r\n\r\n"; |
| { |
| asio::ip::tcp::socket c(is); |
| |
| c.connect(asio::ip::tcp::endpoint( |
| asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); |
| c.send(asio::buffer(sendmsg)); |
| c.receive(asio::buffer(buf, 2048)); |
| c.close(); |
| |
| ASSERT_EQUAL(lastUrlParams.getList("tmnt").size(), 3); |
| ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[0]), "leonardo"); |
| ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[1]), "donatello"); |
| ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[2]), "raphael"); |
| } |
| server.stop(); |
| } |
| |
| TEST(Crow, routeDynamic) |
| { |
| SimpleApp app; |
| int x = 1; |
| app.routeDynamic("/")([&] { |
| x = 2; |
| return ""; |
| }); |
| |
| app.routeDynamic("/set4")([&](const Request&) { |
| x = 4; |
| return ""; |
| }); |
| app.routeDynamic("/set5")([&](const Request&, Response& res) { |
| x = 5; |
| res.end(); |
| }); |
| |
| app.routeDynamic("/set_int/<int>")([&](int y) { |
| x = y; |
| return ""; |
| }); |
| |
| try |
| { |
| app.routeDynamic("/invalid_test/<double>/<path>")([]() { return ""; }); |
| fail(); |
| } |
| catch (std::exception&) |
| { |
| } |
| |
| // app is in an invalid state when routeDynamic throws an exception. |
| try |
| { |
| app.validate(); |
| fail(); |
| } |
| catch (std::exception&) |
| { |
| } |
| |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| req.url = "/"; |
| app.handle(req, res); |
| ASSERT_EQUAL(x, 2); |
| } |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| req.url = "/set_int/42"; |
| app.handle(req, res); |
| ASSERT_EQUAL(x, 42); |
| } |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| req.url = "/set5"; |
| app.handle(req, res); |
| ASSERT_EQUAL(x, 5); |
| } |
| { |
| boost::beast::http::request<boost::beast::http::string_body> r{}; |
| Request req{r}; |
| Response res; |
| req.url = "/set4"; |
| app.handle(req, res); |
| ASSERT_EQUAL(x, 4); |
| } |
| } |