blob: 20a946dab696ba4c51c9c0eedcc5284099818edf [file] [log] [blame]
Ed Tanouse0d918b2018-03-27 17:41:04 -07001#include "crow.h"
Ed Tanous8041f312017-04-03 09:47:01 -07002#include <iostream>
3#include <sstream>
4#include <vector>
Ed Tanous8041f312017-04-03 09:47:01 -07005#include "gtest/gtest.h"
6#undef CROW_LOG_LEVEL
7#define CROW_LOG_LEVEL 0
8
9using namespace std;
10using namespace crow;
11
12bool failed__ = false;
Ed Tanous1ff48782017-04-18 12:45:08 -070013void error_print() { cerr << endl; }
14
15template <typename A, typename... Args>
16void error_print(const A& a, Args... args) {
17 cerr << a;
18 error_print(args...);
Ed Tanous8041f312017-04-03 09:47:01 -070019}
20
Ed Tanous1ff48782017-04-18 12:45:08 -070021template <typename... Args>
22void fail(Args... args) {
23 error_print(args...);
24 failed__ = true;
Ed Tanous8041f312017-04-03 09:47:01 -070025}
26
Ed Tanous1ff48782017-04-18 12:45:08 -070027#define ASSERT_EQUAL(a, b) \
28 if ((a) != (b)) \
29 fail(__FILE__ ":", __LINE__, ": Assert fail: expected ", (a), " actual ", \
30 (b), ", " #a " == " #b ", at " __FILE__ ":", __LINE__)
31#define ASSERT_NOTEQUAL(a, b) \
32 if ((a) == (b)) \
33 fail(__FILE__ ":", __LINE__, ": Assert fail: not expected ", (a), \
34 ", " #a " != " #b ", at " __FILE__ ":", __LINE__)
Ed Tanous8041f312017-04-03 09:47:01 -070035
Ed Tanous1ff48782017-04-18 12:45:08 -070036#define DISABLE_TEST(x) \
37 struct test##x { \
38 void test(); \
39 } x##_; \
40 void test##x::test()
Ed Tanous8041f312017-04-03 09:47:01 -070041
42#define LOCALHOST_ADDRESS "127.0.0.1"
43
Ed Tanous1ff48782017-04-18 12:45:08 -070044TEST(Crow, Rule) {
45 TaggedRule<> r("/http/");
46 r.name("abc");
Ed Tanous8041f312017-04-03 09:47:01 -070047
Ed Tanous1ff48782017-04-18 12:45:08 -070048 // empty handler - fail to validate
49 try {
Ed Tanous8041f312017-04-03 09:47:01 -070050 r.validate();
Ed Tanous1ff48782017-04-18 12:45:08 -070051 fail("empty handler should fail to validate");
52 } catch (runtime_error& e) {
53 }
Ed Tanous8041f312017-04-03 09:47:01 -070054
Ed Tanous1ff48782017-04-18 12:45:08 -070055 int x = 0;
56
57 // registering handler
58 r([&x] {
59 x = 1;
60 return "";
61 });
62
63 r.validate();
64
65 response res;
66
67 // executing handler
68 ASSERT_EQUAL(0, x);
Ed Tanouse0d918b2018-03-27 17:41:04 -070069 boost::beast::http::request<boost::beast::http::string_body> req{};
70 r.handle(request(req), res, routing_params());
Ed Tanous1ff48782017-04-18 12:45:08 -070071 ASSERT_EQUAL(1, x);
72
73 // registering handler with request argument
74 r([&x](const crow::request&) {
75 x = 2;
76 return "";
77 });
78
79 r.validate();
80
81 // executing handler
82 ASSERT_EQUAL(1, x);
Ed Tanouse0d918b2018-03-27 17:41:04 -070083 r.handle(request(req), res, routing_params());
Ed Tanous1ff48782017-04-18 12:45:08 -070084 ASSERT_EQUAL(2, x);
85}
86
87TEST(Crow, ParameterTagging) {
88 static_assert(black_magic::is_valid("<int><int><int>"), "valid url");
89 static_assert(!black_magic::is_valid("<int><int<<int>"), "invalid url");
90 static_assert(!black_magic::is_valid("nt>"), "invalid url");
91 ASSERT_EQUAL(1, black_magic::get_parameter_tag("<int>"));
92 ASSERT_EQUAL(2, black_magic::get_parameter_tag("<uint>"));
93 ASSERT_EQUAL(3, black_magic::get_parameter_tag("<float>"));
94 ASSERT_EQUAL(3, black_magic::get_parameter_tag("<double>"));
95 ASSERT_EQUAL(4, black_magic::get_parameter_tag("<str>"));
96 ASSERT_EQUAL(4, black_magic::get_parameter_tag("<string>"));
97 ASSERT_EQUAL(5, black_magic::get_parameter_tag("<path>"));
98 ASSERT_EQUAL(6 * 6 + 6 + 1,
99 black_magic::get_parameter_tag("<int><int><int>"));
100 ASSERT_EQUAL(6 * 6 + 6 + 2,
101 black_magic::get_parameter_tag("<uint><int><int>"));
102 ASSERT_EQUAL(6 * 6 + 6 * 3 + 2,
103 black_magic::get_parameter_tag("<uint><double><int>"));
104
105 // url definition parsed in compile time, build into *one number*, and given
106 // to template argument
107 static_assert(
108 std::is_same<black_magic::S<uint64_t, double, int64_t>,
109 black_magic::arguments<6 * 6 + 6 * 3 + 2>::type>::value,
110 "tag to type container");
111}
112
113TEST(Crow, PathRouting) {
114 SimpleApp app;
115
116 CROW_ROUTE(app, "/file")
117 ([] { return "file"; });
118
119 CROW_ROUTE(app, "/path/")
120 ([] { return "path"; });
121
122 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700123 boost::beast::http::request<boost::beast::http::string_body> r{};
124 request req{r};
Ed Tanous8041f312017-04-03 09:47:01 -0700125 response res;
126
Ed Tanous1ff48782017-04-18 12:45:08 -0700127 req.url = "/file";
Ed Tanous8041f312017-04-03 09:47:01 -0700128
Ed Tanous1ff48782017-04-18 12:45:08 -0700129 app.handle(req, res);
Ed Tanous8041f312017-04-03 09:47:01 -0700130
Ed Tanouse0d918b2018-03-27 17:41:04 -0700131 ASSERT_EQUAL(200, res.result_int());
Ed Tanous1ff48782017-04-18 12:45:08 -0700132 }
133 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700134 boost::beast::http::request<boost::beast::http::string_body> r{};
135 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700136 response res;
Ed Tanous8041f312017-04-03 09:47:01 -0700137
Ed Tanous1ff48782017-04-18 12:45:08 -0700138 req.url = "/file/";
139
140 app.handle(req, res);
Ed Tanouse0d918b2018-03-27 17:41:04 -0700141 ASSERT_EQUAL(404, res.result_int());
Ed Tanous1ff48782017-04-18 12:45:08 -0700142 }
143 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700144 boost::beast::http::request<boost::beast::http::string_body> r{};
145 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700146 response res;
147
148 req.url = "/path";
149
150 app.handle(req, res);
Ed Tanouse0d918b2018-03-27 17:41:04 -0700151 ASSERT_NOTEQUAL(404, res.result_int());
Ed Tanous1ff48782017-04-18 12:45:08 -0700152 }
153 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700154 boost::beast::http::request<boost::beast::http::string_body> r{};
155 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700156 response res;
157
158 req.url = "/path/";
159
160 app.handle(req, res);
Ed Tanouse0d918b2018-03-27 17:41:04 -0700161 ASSERT_EQUAL(200, res.result_int());
Ed Tanous1ff48782017-04-18 12:45:08 -0700162 }
Ed Tanous8041f312017-04-03 09:47:01 -0700163}
164
Ed Tanous1ff48782017-04-18 12:45:08 -0700165TEST(Crow, RoutingTest) {
166 SimpleApp app;
167 int A{};
168 uint32_t B{};
169 double C{};
170 string D{};
171 string E{};
Ed Tanous8041f312017-04-03 09:47:01 -0700172
Ed Tanous1ff48782017-04-18 12:45:08 -0700173 CROW_ROUTE(app, "/0/<uint>")
174 ([&](uint32_t b) {
175 B = b;
176 return "OK";
177 });
178
179 CROW_ROUTE(app, "/1/<int>/<uint>")
180 ([&](int a, uint32_t b) {
181 A = a;
182 B = b;
183 return "OK";
184 });
185
186 CROW_ROUTE(app, "/4/<int>/<uint>/<double>/<string>")
187 ([&](int a, uint32_t b, double c, string d) {
188 A = a;
189 B = b;
190 C = c;
191 D = d;
192 return "OK";
193 });
194
195 CROW_ROUTE(app, "/5/<int>/<uint>/<double>/<string>/<path>")
196 ([&](int a, uint32_t b, double c, string d, string e) {
197 A = a;
198 B = b;
199 C = c;
200 D = d;
201 E = e;
202 return "OK";
203 });
204
205 app.validate();
206 // app.debug_print();
207 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700208 boost::beast::http::request<boost::beast::http::string_body> r{};
209 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700210 response res;
211
212 req.url = "/-1";
213
214 app.handle(req, res);
215
Ed Tanouse0d918b2018-03-27 17:41:04 -0700216 ASSERT_EQUAL(404, res.result_int());
Ed Tanous1ff48782017-04-18 12:45:08 -0700217 }
218
219 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700220 boost::beast::http::request<boost::beast::http::string_body> r{};
221 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700222 response res;
223
224 req.url = "/0/1001999";
225
226 app.handle(req, res);
227
Ed Tanouse0d918b2018-03-27 17:41:04 -0700228 ASSERT_EQUAL(200, res.result_int());
Ed Tanous1ff48782017-04-18 12:45:08 -0700229
230 ASSERT_EQUAL(1001999, B);
231 }
232
233 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700234 boost::beast::http::request<boost::beast::http::string_body> r{};
235 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700236 response res;
237
238 req.url = "/1/-100/1999";
239
240 app.handle(req, res);
241
Ed Tanouse0d918b2018-03-27 17:41:04 -0700242 ASSERT_EQUAL(200, res.result_int());
Ed Tanous1ff48782017-04-18 12:45:08 -0700243
244 ASSERT_EQUAL(-100, A);
245 ASSERT_EQUAL(1999, B);
246 }
247 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700248 boost::beast::http::request<boost::beast::http::string_body> r{};
249 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700250 response res;
251
252 req.url = "/4/5000/3/-2.71828/hellhere";
Ed Tanous1ff48782017-04-18 12:45:08 -0700253
254 app.handle(req, res);
255
Ed Tanouse0d918b2018-03-27 17:41:04 -0700256 ASSERT_EQUAL(200, res.result_int());
Ed Tanous1ff48782017-04-18 12:45:08 -0700257
258 ASSERT_EQUAL(5000, A);
259 ASSERT_EQUAL(3, B);
260 ASSERT_EQUAL(-2.71828, C);
261 ASSERT_EQUAL("hellhere", D);
262 }
263 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700264 boost::beast::http::request<boost::beast::http::string_body> r{};
265 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700266 response res;
267
268 req.url = "/5/-5/999/3.141592/hello_there/a/b/c/d";
Ed Tanous1ff48782017-04-18 12:45:08 -0700269
270 app.handle(req, res);
271
Ed Tanouse0d918b2018-03-27 17:41:04 -0700272 ASSERT_EQUAL(200, res.result_int());
Ed Tanous1ff48782017-04-18 12:45:08 -0700273
274 ASSERT_EQUAL(-5, A);
275 ASSERT_EQUAL(999, B);
276 ASSERT_EQUAL(3.141592, C);
277 ASSERT_EQUAL("hello_there", D);
278 ASSERT_EQUAL("a/b/c/d", E);
279 }
Ed Tanous8041f312017-04-03 09:47:01 -0700280}
281
Ed Tanous1ff48782017-04-18 12:45:08 -0700282TEST(Crow, simple_response_routing_params) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700283 ASSERT_EQUAL(100,
284 response(boost::beast::http::status::continue_).result_int());
285 ASSERT_EQUAL(200, response("Hello there").result_int());
286 ASSERT_EQUAL(500, response(boost::beast::http::status::internal_server_error,
287 "Internal Error?")
288 .result_int());
Ed Tanous8041f312017-04-03 09:47:01 -0700289
Ed Tanous1ff48782017-04-18 12:45:08 -0700290 routing_params rp;
291 rp.int_params.push_back(1);
292 rp.int_params.push_back(5);
293 rp.uint_params.push_back(2);
294 rp.double_params.push_back(3);
295 rp.string_params.push_back("hello");
296 ASSERT_EQUAL(1, rp.get<int64_t>(0));
297 ASSERT_EQUAL(5, rp.get<int64_t>(1));
298 ASSERT_EQUAL(2, rp.get<uint64_t>(0));
299 ASSERT_EQUAL(3, rp.get<double>(0));
300 ASSERT_EQUAL("hello", rp.get<string>(0));
Ed Tanous8041f312017-04-03 09:47:01 -0700301}
302
Ed Tanous1ff48782017-04-18 12:45:08 -0700303TEST(Crow, handler_with_response) {
304 SimpleApp app;
305 CROW_ROUTE(app, "/")([](const crow::request&, crow::response&) {});
Ed Tanous8041f312017-04-03 09:47:01 -0700306}
307
Ed Tanous1ff48782017-04-18 12:45:08 -0700308TEST(Crow, http_method) {
309 SimpleApp app;
Ed Tanous8041f312017-04-03 09:47:01 -0700310
Ed Tanous1ff48782017-04-18 12:45:08 -0700311 CROW_ROUTE(app, "/").methods("POST"_method,
312 "GET"_method)([](const request& req) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700313 if (req.method() == "GET"_method)
Ed Tanous1ff48782017-04-18 12:45:08 -0700314 return "2";
315 else
316 return "1";
317 });
318
319 CROW_ROUTE(app, "/get_only")
320 .methods("GET"_method)([](const request& /*req*/) { return "get"; });
321 CROW_ROUTE(app, "/post_only")
322 .methods("POST"_method)([](const request& /*req*/) { return "post"; });
323
324 // cannot have multiple handlers for the same url
325 // CROW_ROUTE(app, "/")
326 //.methods("GET"_method)
327 //([]{ return "2"; });
328
329 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700330 boost::beast::http::request<boost::beast::http::string_body> r{};
331 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700332 response res;
333
334 req.url = "/";
335 app.handle(req, res);
336
Ed Tanouse0d918b2018-03-27 17:41:04 -0700337 ASSERT_EQUAL("2", res.body());
Ed Tanous1ff48782017-04-18 12:45:08 -0700338 }
339 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700340 boost::beast::http::request<boost::beast::http::string_body> r{};
341 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700342 response res;
343
344 req.url = "/";
Ed Tanouse0d918b2018-03-27 17:41:04 -0700345 r.method("POST"_method);
Ed Tanous1ff48782017-04-18 12:45:08 -0700346 app.handle(req, res);
347
Ed Tanouse0d918b2018-03-27 17:41:04 -0700348 ASSERT_EQUAL("1", res.body());
Ed Tanous1ff48782017-04-18 12:45:08 -0700349 }
350
351 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700352 boost::beast::http::request<boost::beast::http::string_body> r{};
353 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700354 response res;
355
356 req.url = "/get_only";
357 app.handle(req, res);
358
Ed Tanouse0d918b2018-03-27 17:41:04 -0700359 ASSERT_EQUAL("get", res.body());
Ed Tanous1ff48782017-04-18 12:45:08 -0700360 }
361
362 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700363 boost::beast::http::request<boost::beast::http::string_body> r{};
364 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700365 response res;
366
367 req.url = "/get_only";
Ed Tanouse0d918b2018-03-27 17:41:04 -0700368 r.method("POST"_method);
Ed Tanous1ff48782017-04-18 12:45:08 -0700369 app.handle(req, res);
370
Ed Tanouse0d918b2018-03-27 17:41:04 -0700371 ASSERT_NOTEQUAL("get", res.body());
Ed Tanous1ff48782017-04-18 12:45:08 -0700372 }
Ed Tanous8041f312017-04-03 09:47:01 -0700373}
374
Ed Tanous1ff48782017-04-18 12:45:08 -0700375TEST(Crow, server_handling_error_request) {
376 static char buf[2048];
377 SimpleApp app;
378 CROW_ROUTE(app, "/")([] { return "A"; });
379 Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451);
380 auto _ = async(launch::async, [&] { server.run(); });
381 std::string sendmsg = "POX";
382 asio::io_service is;
383 {
384 asio::ip::tcp::socket c(is);
385 c.connect(asio::ip::tcp::endpoint(
386 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
Ed Tanous8041f312017-04-03 09:47:01 -0700387
Ed Tanous1ff48782017-04-18 12:45:08 -0700388 c.send(asio::buffer(sendmsg));
Ed Tanous8041f312017-04-03 09:47:01 -0700389
Ed Tanous1ff48782017-04-18 12:45:08 -0700390 try {
391 c.receive(asio::buffer(buf, 2048));
392 fail();
393 } catch (std::exception& e) {
394 // std::cerr << e.what() << std::endl;
Ed Tanous8041f312017-04-03 09:47:01 -0700395 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700396 }
397 server.stop();
398}
Ed Tanous8041f312017-04-03 09:47:01 -0700399
Ed Tanous1ff48782017-04-18 12:45:08 -0700400TEST(Crow, multi_server) {
401 static char buf[2048];
402 SimpleApp app1, app2;
403 CROW_ROUTE(app1, "/").methods("GET"_method,
404 "POST"_method)([] { return "A"; });
405 CROW_ROUTE(app2, "/").methods("GET"_method,
406 "POST"_method)([] { return "B"; });
Ed Tanous8041f312017-04-03 09:47:01 -0700407
Ed Tanous1ff48782017-04-18 12:45:08 -0700408 Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451);
409 Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452);
410
411 auto _ = async(launch::async, [&] { server1.run(); });
412 auto _2 = async(launch::async, [&] { server2.run(); });
413
414 std::string sendmsg =
415 "POST /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=B\r\n";
416 asio::io_service is;
417 {
418 asio::ip::tcp::socket c(is);
419 c.connect(asio::ip::tcp::endpoint(
420 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
421
422 c.send(asio::buffer(sendmsg));
423
424 size_t recved = c.receive(asio::buffer(buf, 2048));
425 ASSERT_EQUAL('A', buf[recved - 1]);
426 }
427
428 {
429 asio::ip::tcp::socket c(is);
430 c.connect(asio::ip::tcp::endpoint(
431 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45452));
432
433 for (auto ch : sendmsg) {
434 char buf[1] = {ch};
435 c.send(asio::buffer(buf));
Ed Tanous8041f312017-04-03 09:47:01 -0700436 }
437
Ed Tanous1ff48782017-04-18 12:45:08 -0700438 size_t recved = c.receive(asio::buffer(buf, 2048));
439 ASSERT_EQUAL('B', buf[recved - 1]);
440 }
Ed Tanous8041f312017-04-03 09:47:01 -0700441
Ed Tanous1ff48782017-04-18 12:45:08 -0700442 server1.stop();
443 server2.stop();
444}
Ed Tanous8041f312017-04-03 09:47:01 -0700445
Ed Tanous1ff48782017-04-18 12:45:08 -0700446TEST(Crow, black_magic) {
447 using namespace black_magic;
448 static_assert(
449 std::is_same<void, last_element_type<int, char, void>::type>::value,
450 "last_element_type");
451 static_assert(std::is_same<char, pop_back<int, char, void>::rebind<
452 last_element_type>::type>::value,
453 "pop_back");
454 static_assert(
455 std::is_same<int, pop_back<int, char, void>::rebind<pop_back>::rebind<
456 last_element_type>::type>::value,
457 "pop_back");
Ed Tanous8041f312017-04-03 09:47:01 -0700458}
459
Ed Tanous1ff48782017-04-18 12:45:08 -0700460struct NullMiddleware {
461 struct context {};
Ed Tanous8041f312017-04-03 09:47:01 -0700462
Ed Tanous1ff48782017-04-18 12:45:08 -0700463 template <typename AllContext>
464 void before_handle(request&, response&, context&, AllContext&) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700465
Ed Tanous1ff48782017-04-18 12:45:08 -0700466 template <typename AllContext>
467 void after_handle(request&, response&, context&, AllContext&) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700468};
469
Ed Tanous1ff48782017-04-18 12:45:08 -0700470struct NullSimpleMiddleware {
471 struct context {};
Ed Tanous8041f312017-04-03 09:47:01 -0700472
Ed Tanous1ff48782017-04-18 12:45:08 -0700473 void before_handle(request& /*req*/, response& /*res*/, context& /*ctx*/) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700474
Ed Tanous1ff48782017-04-18 12:45:08 -0700475 void after_handle(request& /*req*/, response& /*res*/, context& /*ctx*/) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700476};
477
Ed Tanous1ff48782017-04-18 12:45:08 -0700478TEST(Crow, middleware_simple) {
479 App<NullMiddleware, NullSimpleMiddleware> app;
480 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
481 CROW_ROUTE(app, "/")
482 ([&](const crow::request& req) {
483 app.get_context<NullMiddleware>(req);
484 app.get_context<NullSimpleMiddleware>(req);
485 return "";
486 });
Ed Tanous8041f312017-04-03 09:47:01 -0700487}
488
Ed Tanous1ff48782017-04-18 12:45:08 -0700489struct IntSettingMiddleware {
490 struct context {
491 int val;
492 };
Ed Tanous8041f312017-04-03 09:47:01 -0700493
Ed Tanous1ff48782017-04-18 12:45:08 -0700494 template <typename AllContext>
495 void before_handle(request&, response&, context& ctx, AllContext&) {
496 ctx.val = 1;
497 }
Ed Tanous8041f312017-04-03 09:47:01 -0700498
Ed Tanous1ff48782017-04-18 12:45:08 -0700499 template <typename AllContext>
500 void after_handle(request&, response&, context& ctx, AllContext&) {
501 ctx.val = 2;
502 }
Ed Tanous8041f312017-04-03 09:47:01 -0700503};
504
505std::vector<std::string> test_middleware_context_vector;
506
Ed Tanous1ff48782017-04-18 12:45:08 -0700507struct FirstMW {
508 struct context {
509 std::vector<string> v;
510 };
Ed Tanous8041f312017-04-03 09:47:01 -0700511
Ed Tanous1ff48782017-04-18 12:45:08 -0700512 void before_handle(request& /*req*/, response& /*res*/, context& ctx) {
513 ctx.v.push_back("1 before");
514 }
Ed Tanous8041f312017-04-03 09:47:01 -0700515
Ed Tanous1ff48782017-04-18 12:45:08 -0700516 void after_handle(request& /*req*/, response& /*res*/, context& ctx) {
517 ctx.v.push_back("1 after");
518 test_middleware_context_vector = ctx.v;
519 }
Ed Tanous8041f312017-04-03 09:47:01 -0700520};
521
Ed Tanous1ff48782017-04-18 12:45:08 -0700522struct SecondMW {
523 struct context {};
524 template <typename AllContext>
525 void before_handle(request& req, response& res, context&,
526 AllContext& all_ctx) {
527 all_ctx.template get<FirstMW>().v.push_back("2 before");
528 if (req.url == "/break") res.end();
529 }
Ed Tanous8041f312017-04-03 09:47:01 -0700530
Ed Tanous1ff48782017-04-18 12:45:08 -0700531 template <typename AllContext>
532 void after_handle(request&, response&, context&, AllContext& all_ctx) {
533 all_ctx.template get<FirstMW>().v.push_back("2 after");
534 }
Ed Tanous8041f312017-04-03 09:47:01 -0700535};
536
Ed Tanous1ff48782017-04-18 12:45:08 -0700537struct ThirdMW {
538 struct context {};
539 template <typename AllContext>
540 void before_handle(request&, response&, context&, AllContext& all_ctx) {
541 all_ctx.template get<FirstMW>().v.push_back("3 before");
542 }
Ed Tanous8041f312017-04-03 09:47:01 -0700543
Ed Tanous1ff48782017-04-18 12:45:08 -0700544 template <typename AllContext>
545 void after_handle(request&, response&, context&, AllContext& all_ctx) {
546 all_ctx.template get<FirstMW>().v.push_back("3 after");
547 }
Ed Tanous8041f312017-04-03 09:47:01 -0700548};
549
Ed Tanous1ff48782017-04-18 12:45:08 -0700550TEST(Crow, middleware_context) {
551 static char buf[2048];
552 // SecondMW depends on FirstMW (it uses all_ctx.get<FirstMW>)
553 // so it leads to compile error if we remove FirstMW from definition
554 // App<IntSettingMiddleware, SecondMW> app;
555 // or change the order of FirstMW and SecondMW
556 // App<IntSettingMiddleware, SecondMW, FirstMW> app;
Ed Tanous8041f312017-04-03 09:47:01 -0700557
Ed Tanous1ff48782017-04-18 12:45:08 -0700558 App<IntSettingMiddleware, FirstMW, SecondMW, ThirdMW> app;
Ed Tanous8041f312017-04-03 09:47:01 -0700559
Ed Tanous1ff48782017-04-18 12:45:08 -0700560 int x{};
561 CROW_ROUTE(app, "/")
562 ([&](const request& req) {
Ed Tanous8041f312017-04-03 09:47:01 -0700563 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700564 auto& ctx = app.get_context<IntSettingMiddleware>(req);
565 x = ctx.val;
Ed Tanous8041f312017-04-03 09:47:01 -0700566 }
567 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700568 auto& ctx = app.get_context<FirstMW>(req);
569 ctx.v.push_back("handle");
Ed Tanous8041f312017-04-03 09:47:01 -0700570 }
Ed Tanous8041f312017-04-03 09:47:01 -0700571
Ed Tanous1ff48782017-04-18 12:45:08 -0700572 return "";
573 });
574 CROW_ROUTE(app, "/break")
575 ([&](const request& req) {
Ed Tanous8041f312017-04-03 09:47:01 -0700576 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700577 auto& ctx = app.get_context<FirstMW>(req);
578 ctx.v.push_back("handle");
Ed Tanous8041f312017-04-03 09:47:01 -0700579 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700580
581 return "";
582 });
583
584 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
585 auto _ = async(launch::async, [&] { server.run(); });
586 std::string sendmsg = "GET /\r\n\r\n";
587 asio::io_service is;
588 {
589 asio::ip::tcp::socket c(is);
590 c.connect(asio::ip::tcp::endpoint(
591 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
592
593 c.send(asio::buffer(sendmsg));
594
595 c.receive(asio::buffer(buf, 2048));
596 c.close();
597 }
598 {
599 auto& out = test_middleware_context_vector;
600 ASSERT_EQUAL(1, x);
601 ASSERT_EQUAL(7, out.size());
602 ASSERT_EQUAL("1 before", out[0]);
603 ASSERT_EQUAL("2 before", out[1]);
604 ASSERT_EQUAL("3 before", out[2]);
605 ASSERT_EQUAL("handle", out[3]);
606 ASSERT_EQUAL("3 after", out[4]);
607 ASSERT_EQUAL("2 after", out[5]);
608 ASSERT_EQUAL("1 after", out[6]);
609 }
610 std::string sendmsg2 = "GET /break\r\n\r\n";
611 {
612 asio::ip::tcp::socket c(is);
613 c.connect(asio::ip::tcp::endpoint(
614 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
615
616 c.send(asio::buffer(sendmsg2));
617
618 c.receive(asio::buffer(buf, 2048));
619 c.close();
620 }
621 {
622 auto& out = test_middleware_context_vector;
623 ASSERT_EQUAL(4, out.size());
624 ASSERT_EQUAL("1 before", out[0]);
625 ASSERT_EQUAL("2 before", out[1]);
626 ASSERT_EQUAL("2 after", out[2]);
627 ASSERT_EQUAL("1 after", out[3]);
628 }
629 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700630}
631
Ed Tanous1ff48782017-04-18 12:45:08 -0700632TEST(Crow, bug_quick_repeated_request) {
633 static char buf[2048];
634
635 SimpleApp app;
636
637 CROW_ROUTE(app, "/")([&] { return "hello"; });
638
639 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
640 auto _ = async(launch::async, [&] { server.run(); });
641 std::string sendmsg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
642 asio::io_service is;
643 {
644 std::vector<std::future<void>> v;
645 for (int i = 0; i < 5; i++) {
646 v.push_back(async(launch::async, [&] {
647 asio::ip::tcp::socket c(is);
648 c.connect(asio::ip::tcp::endpoint(
649 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
650
651 for (int j = 0; j < 5; j++) {
652 c.send(asio::buffer(sendmsg));
653
654 size_t received = c.receive(asio::buffer(buf, 2048));
655 ASSERT_EQUAL("hello",
656 std::string(buf + received - 5, buf + received));
Ed Tanous8041f312017-04-03 09:47:01 -0700657 }
Ed Tanous8041f312017-04-03 09:47:01 -0700658 c.close();
Ed Tanous1ff48782017-04-18 12:45:08 -0700659 }));
Ed Tanous8041f312017-04-03 09:47:01 -0700660 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700661 }
662 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700663}
664
Ed Tanous1ff48782017-04-18 12:45:08 -0700665TEST(Crow, simple_url_params) {
666 static char buf[2048];
Ed Tanous8041f312017-04-03 09:47:01 -0700667
Ed Tanous1ff48782017-04-18 12:45:08 -0700668 SimpleApp app;
Ed Tanous8041f312017-04-03 09:47:01 -0700669
Ed Tanous1ff48782017-04-18 12:45:08 -0700670 query_string last_url_params;
Ed Tanous8041f312017-04-03 09:47:01 -0700671
Ed Tanous1ff48782017-04-18 12:45:08 -0700672 CROW_ROUTE(app, "/params")
673 ([&last_url_params](const crow::request& req) {
674 last_url_params = std::move(req.url_params);
675 return "OK";
676 });
Ed Tanous8041f312017-04-03 09:47:01 -0700677
Ed Tanous1ff48782017-04-18 12:45:08 -0700678 /// params?h=1&foo=bar&lol&count[]=1&count[]=4&pew=5.2
Ed Tanous8041f312017-04-03 09:47:01 -0700679
Ed Tanous1ff48782017-04-18 12:45:08 -0700680 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
681 auto _ = async(launch::async, [&] { server.run(); });
682 asio::io_service is;
683 std::string sendmsg;
684
685 // check empty params
686 sendmsg = "GET /params\r\n\r\n";
687 {
688 asio::ip::tcp::socket c(is);
689 c.connect(asio::ip::tcp::endpoint(
690 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
691 c.send(asio::buffer(sendmsg));
692 c.receive(asio::buffer(buf, 2048));
693 c.close();
694
695 stringstream ss;
696 ss << last_url_params;
697
698 ASSERT_EQUAL("[ ]", ss.str());
699 }
700 // check single presence
701 sendmsg = "GET /params?foobar\r\n\r\n";
702 {
703 asio::ip::tcp::socket c(is);
704 c.connect(asio::ip::tcp::endpoint(
705 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
706 c.send(asio::buffer(sendmsg));
707 c.receive(asio::buffer(buf, 2048));
708 c.close();
709
710 ASSERT_TRUE(last_url_params.get("missing") == nullptr);
711 ASSERT_TRUE(last_url_params.get("foobar") != nullptr);
712 ASSERT_TRUE(last_url_params.get_list("missing").empty());
713 }
714 // check multiple presence
715 sendmsg = "GET /params?foo&bar&baz\r\n\r\n";
716 {
717 asio::ip::tcp::socket c(is);
718 c.connect(asio::ip::tcp::endpoint(
719 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
720 c.send(asio::buffer(sendmsg));
721 c.receive(asio::buffer(buf, 2048));
722 c.close();
723
724 ASSERT_TRUE(last_url_params.get("missing") == nullptr);
725 ASSERT_TRUE(last_url_params.get("foo") != nullptr);
726 ASSERT_TRUE(last_url_params.get("bar") != nullptr);
727 ASSERT_TRUE(last_url_params.get("baz") != nullptr);
728 }
729 // check single value
730 sendmsg = "GET /params?hello=world\r\n\r\n";
731 {
732 asio::ip::tcp::socket c(is);
733 c.connect(asio::ip::tcp::endpoint(
734 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
735 c.send(asio::buffer(sendmsg));
736 c.receive(asio::buffer(buf, 2048));
737 c.close();
738
739 ASSERT_EQUAL(string(last_url_params.get("hello")), "world");
740 }
741 // check multiple value
742 sendmsg = "GET /params?hello=world&left=right&up=down\r\n\r\n";
743 {
744 asio::ip::tcp::socket c(is);
745 c.connect(asio::ip::tcp::endpoint(
746 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
747 c.send(asio::buffer(sendmsg));
748 c.receive(asio::buffer(buf, 2048));
749 c.close();
750
751 ASSERT_EQUAL(string(last_url_params.get("hello")), "world");
752 ASSERT_EQUAL(string(last_url_params.get("left")), "right");
753 ASSERT_EQUAL(string(last_url_params.get("up")), "down");
754 }
755 // check multiple value, multiple types
756 sendmsg = "GET /params?int=100&double=123.45&boolean=1\r\n\r\n";
757 {
758 asio::ip::tcp::socket c(is);
759 c.connect(asio::ip::tcp::endpoint(
760 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
761 c.send(asio::buffer(sendmsg));
762 c.receive(asio::buffer(buf, 2048));
763 c.close();
764
765 ASSERT_EQUAL(boost::lexical_cast<int>(last_url_params.get("int")), 100);
766 ASSERT_EQUAL(boost::lexical_cast<double>(last_url_params.get("double")),
767 123.45);
768 ASSERT_EQUAL(boost::lexical_cast<bool>(last_url_params.get("boolean")),
769 true);
770 }
771 // check single array value
772 sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n";
773 {
774 asio::ip::tcp::socket c(is);
775
776 c.connect(asio::ip::tcp::endpoint(
777 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
778 c.send(asio::buffer(sendmsg));
779 c.receive(asio::buffer(buf, 2048));
780 c.close();
781
782 ASSERT_TRUE(last_url_params.get("tmnt") == nullptr);
783 ASSERT_EQUAL(last_url_params.get_list("tmnt").size(), 1);
784 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[0]), "leonardo");
785 }
786 // check multiple array value
787 sendmsg =
788 "GET /params?tmnt[]=leonardo&tmnt[]=donatello&tmnt[]=raphael\r\n\r\n";
789 {
790 asio::ip::tcp::socket c(is);
791
792 c.connect(asio::ip::tcp::endpoint(
793 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
794 c.send(asio::buffer(sendmsg));
795 c.receive(asio::buffer(buf, 2048));
796 c.close();
797
798 ASSERT_EQUAL(last_url_params.get_list("tmnt").size(), 3);
799 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[0]), "leonardo");
800 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[1]), "donatello");
801 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[2]), "raphael");
802 }
803 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700804}
805
Ed Tanous1ff48782017-04-18 12:45:08 -0700806TEST(Crow, route_dynamic) {
807 SimpleApp app;
808 int x = 1;
809 app.route_dynamic("/")([&] {
810 x = 2;
811 return "";
812 });
Ed Tanous8041f312017-04-03 09:47:01 -0700813
Ed Tanous1ff48782017-04-18 12:45:08 -0700814 app.route_dynamic("/set4")([&](const request&) {
815 x = 4;
816 return "";
817 });
818 app.route_dynamic("/set5")([&](const request&, response& res) {
819 x = 5;
820 res.end();
821 });
Ed Tanous8041f312017-04-03 09:47:01 -0700822
Ed Tanous1ff48782017-04-18 12:45:08 -0700823 app.route_dynamic("/set_int/<int>")([&](int y) {
824 x = y;
825 return "";
826 });
Ed Tanous8041f312017-04-03 09:47:01 -0700827
Ed Tanous1ff48782017-04-18 12:45:08 -0700828 try {
829 app.route_dynamic("/invalid_test/<double>/<path>")([]() { return ""; });
830 fail();
831 } catch (std::exception&) {
832 }
Ed Tanous8041f312017-04-03 09:47:01 -0700833
Ed Tanous1ff48782017-04-18 12:45:08 -0700834 // app is in an invalid state when route_dynamic throws an exception.
835 try {
836 app.validate();
837 fail();
838 } catch (std::exception&) {
839 }
Ed Tanous8041f312017-04-03 09:47:01 -0700840
Ed Tanous1ff48782017-04-18 12:45:08 -0700841 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700842 boost::beast::http::request<boost::beast::http::string_body> r{};
843 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700844 response res;
845 req.url = "/";
846 app.handle(req, res);
847 ASSERT_EQUAL(x, 2);
848 }
849 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700850 boost::beast::http::request<boost::beast::http::string_body> r{};
851 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700852 response res;
853 req.url = "/set_int/42";
854 app.handle(req, res);
855 ASSERT_EQUAL(x, 42);
856 }
857 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700858 boost::beast::http::request<boost::beast::http::string_body> r{};
859 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700860 response res;
861 req.url = "/set5";
862 app.handle(req, res);
863 ASSERT_EQUAL(x, 5);
864 }
865 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700866 boost::beast::http::request<boost::beast::http::string_body> r{};
867 request req{r};
Ed Tanous1ff48782017-04-18 12:45:08 -0700868 response res;
869 req.url = "/set4";
870 app.handle(req, res);
871 ASSERT_EQUAL(x, 4);
872 }
Ed Tanous8041f312017-04-03 09:47:01 -0700873}