blob: a3c9ed9e0d40c44a8847830b49519fcde4ac443c [file] [log] [blame]
Ed Tanous8041f312017-04-03 09:47:01 -07001#include <iostream>
2#include <sstream>
3#include <vector>
4#include "crow.h"
5#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);
69 r.handle(request(), res, routing_params());
70 ASSERT_EQUAL(1, x);
71
72 // registering handler with request argument
73 r([&x](const crow::request&) {
74 x = 2;
75 return "";
76 });
77
78 r.validate();
79
80 // executing handler
81 ASSERT_EQUAL(1, x);
82 r.handle(request(), res, routing_params());
83 ASSERT_EQUAL(2, x);
84}
85
86TEST(Crow, ParameterTagging) {
87 static_assert(black_magic::is_valid("<int><int><int>"), "valid url");
88 static_assert(!black_magic::is_valid("<int><int<<int>"), "invalid url");
89 static_assert(!black_magic::is_valid("nt>"), "invalid url");
90 ASSERT_EQUAL(1, black_magic::get_parameter_tag("<int>"));
91 ASSERT_EQUAL(2, black_magic::get_parameter_tag("<uint>"));
92 ASSERT_EQUAL(3, black_magic::get_parameter_tag("<float>"));
93 ASSERT_EQUAL(3, black_magic::get_parameter_tag("<double>"));
94 ASSERT_EQUAL(4, black_magic::get_parameter_tag("<str>"));
95 ASSERT_EQUAL(4, black_magic::get_parameter_tag("<string>"));
96 ASSERT_EQUAL(5, black_magic::get_parameter_tag("<path>"));
97 ASSERT_EQUAL(6 * 6 + 6 + 1,
98 black_magic::get_parameter_tag("<int><int><int>"));
99 ASSERT_EQUAL(6 * 6 + 6 + 2,
100 black_magic::get_parameter_tag("<uint><int><int>"));
101 ASSERT_EQUAL(6 * 6 + 6 * 3 + 2,
102 black_magic::get_parameter_tag("<uint><double><int>"));
103
104 // url definition parsed in compile time, build into *one number*, and given
105 // to template argument
106 static_assert(
107 std::is_same<black_magic::S<uint64_t, double, int64_t>,
108 black_magic::arguments<6 * 6 + 6 * 3 + 2>::type>::value,
109 "tag to type container");
110}
111
112TEST(Crow, PathRouting) {
113 SimpleApp app;
114
115 CROW_ROUTE(app, "/file")
116 ([] { return "file"; });
117
118 CROW_ROUTE(app, "/path/")
119 ([] { return "path"; });
120
121 {
122 request req;
Ed Tanous8041f312017-04-03 09:47:01 -0700123 response res;
124
Ed Tanous1ff48782017-04-18 12:45:08 -0700125 req.url = "/file";
Ed Tanous8041f312017-04-03 09:47:01 -0700126
Ed Tanous1ff48782017-04-18 12:45:08 -0700127 app.handle(req, res);
Ed Tanous8041f312017-04-03 09:47:01 -0700128
Ed Tanous1ff48782017-04-18 12:45:08 -0700129 ASSERT_EQUAL(200, res.code);
130 }
131 {
132 request req;
133 response res;
Ed Tanous8041f312017-04-03 09:47:01 -0700134
Ed Tanous1ff48782017-04-18 12:45:08 -0700135 req.url = "/file/";
136
137 app.handle(req, res);
138 ASSERT_EQUAL(404, res.code);
139 }
140 {
141 request req;
142 response res;
143
144 req.url = "/path";
145
146 app.handle(req, res);
147 ASSERT_NOTEQUAL(404, res.code);
148 }
149 {
150 request req;
151 response res;
152
153 req.url = "/path/";
154
155 app.handle(req, res);
156 ASSERT_EQUAL(200, res.code);
157 }
Ed Tanous8041f312017-04-03 09:47:01 -0700158}
159
Ed Tanous1ff48782017-04-18 12:45:08 -0700160TEST(Crow, RoutingTest) {
161 SimpleApp app;
162 int A{};
163 uint32_t B{};
164 double C{};
165 string D{};
166 string E{};
Ed Tanous8041f312017-04-03 09:47:01 -0700167
Ed Tanous1ff48782017-04-18 12:45:08 -0700168 CROW_ROUTE(app, "/0/<uint>")
169 ([&](uint32_t b) {
170 B = b;
171 return "OK";
172 });
173
174 CROW_ROUTE(app, "/1/<int>/<uint>")
175 ([&](int a, uint32_t b) {
176 A = a;
177 B = b;
178 return "OK";
179 });
180
181 CROW_ROUTE(app, "/4/<int>/<uint>/<double>/<string>")
182 ([&](int a, uint32_t b, double c, string d) {
183 A = a;
184 B = b;
185 C = c;
186 D = d;
187 return "OK";
188 });
189
190 CROW_ROUTE(app, "/5/<int>/<uint>/<double>/<string>/<path>")
191 ([&](int a, uint32_t b, double c, string d, string e) {
192 A = a;
193 B = b;
194 C = c;
195 D = d;
196 E = e;
197 return "OK";
198 });
199
200 app.validate();
201 // app.debug_print();
202 {
203 request req;
204 response res;
205
206 req.url = "/-1";
207
208 app.handle(req, res);
209
210 ASSERT_EQUAL(404, res.code);
211 }
212
213 {
214 request req;
215 response res;
216
217 req.url = "/0/1001999";
218
219 app.handle(req, res);
220
221 ASSERT_EQUAL(200, res.code);
222
223 ASSERT_EQUAL(1001999, B);
224 }
225
226 {
227 request req;
228 response res;
229
230 req.url = "/1/-100/1999";
231
232 app.handle(req, res);
233
234 ASSERT_EQUAL(200, res.code);
235
236 ASSERT_EQUAL(-100, A);
237 ASSERT_EQUAL(1999, B);
238 }
239 {
240 request req;
241 response res;
242
243 req.url = "/4/5000/3/-2.71828/hellhere";
244 req.add_header("TestHeader", "Value");
245
246 app.handle(req, res);
247
248 ASSERT_EQUAL(200, res.code);
249
250 ASSERT_EQUAL(5000, A);
251 ASSERT_EQUAL(3, B);
252 ASSERT_EQUAL(-2.71828, C);
253 ASSERT_EQUAL("hellhere", D);
254 }
255 {
256 request req;
257 response res;
258
259 req.url = "/5/-5/999/3.141592/hello_there/a/b/c/d";
260 req.add_header("TestHeader", "Value");
261
262 app.handle(req, res);
263
264 ASSERT_EQUAL(200, res.code);
265
266 ASSERT_EQUAL(-5, A);
267 ASSERT_EQUAL(999, B);
268 ASSERT_EQUAL(3.141592, C);
269 ASSERT_EQUAL("hello_there", D);
270 ASSERT_EQUAL("a/b/c/d", E);
271 }
Ed Tanous8041f312017-04-03 09:47:01 -0700272}
273
Ed Tanous1ff48782017-04-18 12:45:08 -0700274TEST(Crow, simple_response_routing_params) {
275 ASSERT_EQUAL(100, response(100).code);
276 ASSERT_EQUAL(200, response("Hello there").code);
277 ASSERT_EQUAL(500, response(500, "Internal Error?").code);
Ed Tanous8041f312017-04-03 09:47:01 -0700278
Ed Tanous1ff48782017-04-18 12:45:08 -0700279 routing_params rp;
280 rp.int_params.push_back(1);
281 rp.int_params.push_back(5);
282 rp.uint_params.push_back(2);
283 rp.double_params.push_back(3);
284 rp.string_params.push_back("hello");
285 ASSERT_EQUAL(1, rp.get<int64_t>(0));
286 ASSERT_EQUAL(5, rp.get<int64_t>(1));
287 ASSERT_EQUAL(2, rp.get<uint64_t>(0));
288 ASSERT_EQUAL(3, rp.get<double>(0));
289 ASSERT_EQUAL("hello", rp.get<string>(0));
Ed Tanous8041f312017-04-03 09:47:01 -0700290}
291
Ed Tanous1ff48782017-04-18 12:45:08 -0700292TEST(Crow, handler_with_response) {
293 SimpleApp app;
294 CROW_ROUTE(app, "/")([](const crow::request&, crow::response&) {});
Ed Tanous8041f312017-04-03 09:47:01 -0700295}
296
Ed Tanous1ff48782017-04-18 12:45:08 -0700297TEST(Crow, http_method) {
298 SimpleApp app;
Ed Tanous8041f312017-04-03 09:47:01 -0700299
Ed Tanous1ff48782017-04-18 12:45:08 -0700300 CROW_ROUTE(app, "/").methods("POST"_method,
301 "GET"_method)([](const request& req) {
302 if (req.method == "GET"_method)
303 return "2";
304 else
305 return "1";
306 });
307
308 CROW_ROUTE(app, "/get_only")
309 .methods("GET"_method)([](const request& /*req*/) { return "get"; });
310 CROW_ROUTE(app, "/post_only")
311 .methods("POST"_method)([](const request& /*req*/) { return "post"; });
312
313 // cannot have multiple handlers for the same url
314 // CROW_ROUTE(app, "/")
315 //.methods("GET"_method)
316 //([]{ return "2"; });
317
318 {
319 request req;
320 response res;
321
322 req.url = "/";
323 app.handle(req, res);
324
325 ASSERT_EQUAL("2", res.body);
326 }
327 {
328 request req;
329 response res;
330
331 req.url = "/";
332 req.method = "POST"_method;
333 app.handle(req, res);
334
335 ASSERT_EQUAL("1", res.body);
336 }
337
338 {
339 request req;
340 response res;
341
342 req.url = "/get_only";
343 app.handle(req, res);
344
345 ASSERT_EQUAL("get", res.body);
346 }
347
348 {
349 request req;
350 response res;
351
352 req.url = "/get_only";
353 req.method = "POST"_method;
354 app.handle(req, res);
355
356 ASSERT_NOTEQUAL("get", res.body);
357 }
Ed Tanous8041f312017-04-03 09:47:01 -0700358}
359
Ed Tanous1ff48782017-04-18 12:45:08 -0700360TEST(Crow, server_handling_error_request) {
361 static char buf[2048];
362 SimpleApp app;
363 CROW_ROUTE(app, "/")([] { return "A"; });
364 Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451);
365 auto _ = async(launch::async, [&] { server.run(); });
366 std::string sendmsg = "POX";
367 asio::io_service is;
368 {
369 asio::ip::tcp::socket c(is);
370 c.connect(asio::ip::tcp::endpoint(
371 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
Ed Tanous8041f312017-04-03 09:47:01 -0700372
Ed Tanous1ff48782017-04-18 12:45:08 -0700373 c.send(asio::buffer(sendmsg));
Ed Tanous8041f312017-04-03 09:47:01 -0700374
Ed Tanous1ff48782017-04-18 12:45:08 -0700375 try {
376 c.receive(asio::buffer(buf, 2048));
377 fail();
378 } catch (std::exception& e) {
379 // std::cerr << e.what() << std::endl;
Ed Tanous8041f312017-04-03 09:47:01 -0700380 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700381 }
382 server.stop();
383}
Ed Tanous8041f312017-04-03 09:47:01 -0700384
Ed Tanous1ff48782017-04-18 12:45:08 -0700385TEST(Crow, multi_server) {
386 static char buf[2048];
387 SimpleApp app1, app2;
388 CROW_ROUTE(app1, "/").methods("GET"_method,
389 "POST"_method)([] { return "A"; });
390 CROW_ROUTE(app2, "/").methods("GET"_method,
391 "POST"_method)([] { return "B"; });
Ed Tanous8041f312017-04-03 09:47:01 -0700392
Ed Tanous1ff48782017-04-18 12:45:08 -0700393 Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451);
394 Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452);
395
396 auto _ = async(launch::async, [&] { server1.run(); });
397 auto _2 = async(launch::async, [&] { server2.run(); });
398
399 std::string sendmsg =
400 "POST /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=B\r\n";
401 asio::io_service is;
402 {
403 asio::ip::tcp::socket c(is);
404 c.connect(asio::ip::tcp::endpoint(
405 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
406
407 c.send(asio::buffer(sendmsg));
408
409 size_t recved = c.receive(asio::buffer(buf, 2048));
410 ASSERT_EQUAL('A', buf[recved - 1]);
411 }
412
413 {
414 asio::ip::tcp::socket c(is);
415 c.connect(asio::ip::tcp::endpoint(
416 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45452));
417
418 for (auto ch : sendmsg) {
419 char buf[1] = {ch};
420 c.send(asio::buffer(buf));
Ed Tanous8041f312017-04-03 09:47:01 -0700421 }
422
Ed Tanous1ff48782017-04-18 12:45:08 -0700423 size_t recved = c.receive(asio::buffer(buf, 2048));
424 ASSERT_EQUAL('B', buf[recved - 1]);
425 }
Ed Tanous8041f312017-04-03 09:47:01 -0700426
Ed Tanous1ff48782017-04-18 12:45:08 -0700427 server1.stop();
428 server2.stop();
429}
Ed Tanous8041f312017-04-03 09:47:01 -0700430
Ed Tanous1ff48782017-04-18 12:45:08 -0700431TEST(Crow, json_read) {
432 {
433 const char* json_error_tests[] = {
434 "{} 3",
435 "{{}",
436 "{3}",
437 "3.4.5",
438 "+3",
439 "3-2",
440 "00",
441 "03",
442 "1e3e3",
443 "1e+.3",
444 "nll",
445 "f",
446 "t",
447 "{\"x\":3,}",
448 "{\"x\"}",
449 "{\"x\":3 q}",
450 "{\"x\":[3 4]}",
451 "{\"x\":[\"",
452 "{\"x\":[[], 4],\"y\",}",
453 "{\"x\":[3",
454 "{\"x\":[ null, false, true}",
455 };
456 for (auto s : json_error_tests) {
457 auto x = json::load(s);
458 if (x) {
459 fail("should fail to parse ", s);
460 return;
461 }
Ed Tanous8041f312017-04-03 09:47:01 -0700462 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700463 }
Ed Tanous8041f312017-04-03 09:47:01 -0700464
Ed Tanous1ff48782017-04-18 12:45:08 -0700465 auto x = json::load(R"({"message":"hello, world"})");
466 if (!x) fail("fail to parse");
467 ASSERT_EQUAL("hello, world", x["message"]);
468 ASSERT_EQUAL(1, x.size());
469 ASSERT_EQUAL(false, x.has("mess"));
470 ASSERT_THROW(x["mess"], std::exception);
471 // TODO returning false is better than exception
472 // ASSERT_THROW(3 == x["message"], std::exception);
473 ASSERT_EQUAL(12, x["message"].size());
Ed Tanous8041f312017-04-03 09:47:01 -0700474
Ed Tanous1ff48782017-04-18 12:45:08 -0700475 std::string s = R"({"int":3, "ints" :[1,2,3,4,5] })";
476 auto y = json::load(s);
477 ASSERT_EQUAL(3, y["int"]);
478 ASSERT_EQUAL(3.0, y["int"]);
479 ASSERT_NOTEQUAL(3.01, y["int"]);
480 ASSERT_EQUAL(5, y["ints"].size());
481 ASSERT_EQUAL(1, y["ints"][0]);
482 ASSERT_EQUAL(2, y["ints"][1]);
483 ASSERT_EQUAL(3, y["ints"][2]);
484 ASSERT_EQUAL(4, y["ints"][3]);
485 ASSERT_EQUAL(5, y["ints"][4]);
486 ASSERT_EQUAL(1u, y["ints"][0]);
487 ASSERT_EQUAL(1.f, y["ints"][0]);
Ed Tanous8041f312017-04-03 09:47:01 -0700488
Ed Tanous1ff48782017-04-18 12:45:08 -0700489 int q = (int)y["ints"][1];
490 ASSERT_EQUAL(2, q);
491 q = y["ints"][2].i();
492 ASSERT_EQUAL(3, q);
493
494 std::string s2 = R"({"bools":[true, false], "doubles":[1.2, -3.4]})";
495 auto z = json::load(s2);
496 ASSERT_EQUAL(2, z["bools"].size());
497 ASSERT_EQUAL(2, z["doubles"].size());
498 ASSERT_EQUAL(true, z["bools"][0].b());
499 ASSERT_EQUAL(false, z["bools"][1].b());
500 ASSERT_EQUAL(1.2, z["doubles"][0].d());
501 ASSERT_EQUAL(-3.4, z["doubles"][1].d());
502
503 std::string s3 = R"({"uint64": 18446744073709551615})";
504 auto z1 = json::load(s3);
505 ASSERT_EQUAL(18446744073709551615ull, z1["uint64"].u());
506}
507
508TEST(Crow, json_read_real) {
509 vector<std::string> v{"0.036303908355795146",
510 "0.18320417789757412",
511 "0.05319940476190476",
512 "0.15224702380952382",
513 "0",
514 "0.3296201145552561",
515 "0.47921580188679247",
516 "0.05873511904761905",
517 "0.1577827380952381",
518 "0.4996841307277628",
519 "0.6425412735849056",
520 "0.052113095238095236",
521 "0.12830357142857143",
522 "0.7871041105121294",
523 "0.954220013477089",
524 "0.05869047619047619",
525 "0.1625",
526 "0.8144794474393531",
527 "0.9721613881401617",
528 "0.1399404761904762",
529 "0.24470238095238095",
530 "0.04527459568733154",
531 "0.2096950808625337",
532 "0.35267857142857145",
533 "0.42791666666666667",
534 "0.855731974393531",
535 "0.9352467991913747",
536 "0.3816220238095238",
537 "0.4282886904761905",
538 "0.39414167789757415",
539 "0.5316079851752021",
540 "0.3809375",
541 "0.4571279761904762",
542 "0.03522995283018868",
543 "0.1915641846361186",
544 "0.6164136904761904",
545 "0.7192708333333333",
546 "0.05675117924528302",
547 "0.21308541105121293",
548 "0.7045386904761904",
549 "0.8016815476190476"};
550 for (auto x : v) {
551 CROW_LOG_DEBUG << x;
552 ASSERT_EQUAL(json::load(x).d(), boost::lexical_cast<double>(x));
553 }
554
555 auto ret = json::load(
556 R"---({"balloons":[{"mode":"ellipse","left":0.036303908355795146,"right":0.18320417789757412,"top":0.05319940476190476,"bottom":0.15224702380952382,"index":"0"},{"mode":"ellipse","left":0.3296201145552561,"right":0.47921580188679247,"top":0.05873511904761905,"bottom":0.1577827380952381,"index":"1"},{"mode":"ellipse","left":0.4996841307277628,"right":0.6425412735849056,"top":0.052113095238095236,"bottom":0.12830357142857143,"index":"2"},{"mode":"ellipse","left":0.7871041105121294,"right":0.954220013477089,"top":0.05869047619047619,"bottom":0.1625,"index":"3"},{"mode":"ellipse","left":0.8144794474393531,"right":0.9721613881401617,"top":0.1399404761904762,"bottom":0.24470238095238095,"index":"4"},{"mode":"ellipse","left":0.04527459568733154,"right":0.2096950808625337,"top":0.35267857142857145,"bottom":0.42791666666666667,"index":"5"},{"mode":"ellipse","left":0.855731974393531,"right":0.9352467991913747,"top":0.3816220238095238,"bottom":0.4282886904761905,"index":"6"},{"mode":"ellipse","left":0.39414167789757415,"right":0.5316079851752021,"top":0.3809375,"bottom":0.4571279761904762,"index":"7"},{"mode":"ellipse","left":0.03522995283018868,"right":0.1915641846361186,"top":0.6164136904761904,"bottom":0.7192708333333333,"index":"8"},{"mode":"ellipse","left":0.05675117924528302,"right":0.21308541105121293,"top":0.7045386904761904,"bottom":0.8016815476190476,"index":"9"}]})---");
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100557 ASSERT_TRUE(static_cast<bool>(ret));
Ed Tanous1ff48782017-04-18 12:45:08 -0700558}
559
560TEST(Crow, json_read_unescaping) {
561 {
562 auto x = json::load(R"({"data":"\ud55c\n\t\r"})");
563 if (!x) {
564 fail("fail to parse");
565 return;
Ed Tanous8041f312017-04-03 09:47:01 -0700566 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700567 ASSERT_EQUAL(6, x["data"].size());
568 ASSERT_EQUAL("한\n\t\r", x["data"]);
569 }
570 {
571 // multiple r_string instance
572 auto x = json::load(R"({"data":"\ud55c\n\t\r"})");
573 auto a = x["data"].s();
574 auto b = x["data"].s();
575 ASSERT_EQUAL(6, a.size());
576 ASSERT_EQUAL(6, b.size());
577 ASSERT_EQUAL(6, x["data"].size());
578 }
Ed Tanous8041f312017-04-03 09:47:01 -0700579}
580
Ed Tanous1ff48782017-04-18 12:45:08 -0700581TEST(Crow, json_write) {
582 json::wvalue x;
583 x["message"] = "hello world";
584 ASSERT_EQUAL(R"({"message":"hello world"})", json::dump(x));
585 x["message"] = std::string("string value");
586 ASSERT_EQUAL(R"({"message":"string value"})", json::dump(x));
587 x["message"]["x"] = 3;
588 ASSERT_EQUAL(R"({"message":{"x":3}})", json::dump(x));
589 x["message"]["y"] = 5;
590 ASSERT_TRUE(R"({"message":{"x":3,"y":5}})" == json::dump(x) ||
591 R"({"message":{"y":5,"x":3}})" == json::dump(x));
592 x["message"] = 5.5;
593 ASSERT_EQUAL(R"({"message":5.5})", json::dump(x));
Ed Tanous8041f312017-04-03 09:47:01 -0700594
Ed Tanous1ff48782017-04-18 12:45:08 -0700595 json::wvalue y;
596 y["scores"][0] = 1;
597 y["scores"][1] = "king";
598 y["scores"][2] = 3.5;
599 ASSERT_EQUAL(R"({"scores":[1,"king",3.5]})", json::dump(y));
Ed Tanous8041f312017-04-03 09:47:01 -0700600
Ed Tanous1ff48782017-04-18 12:45:08 -0700601 y["scores"][2][0] = "real";
602 y["scores"][2][1] = false;
603 y["scores"][2][2] = true;
604 ASSERT_EQUAL(R"({"scores":[1,"king",["real",false,true]]})", json::dump(y));
Ed Tanous8041f312017-04-03 09:47:01 -0700605
Ed Tanous1ff48782017-04-18 12:45:08 -0700606 y["scores"]["a"]["b"]["c"] = nullptr;
607 ASSERT_EQUAL(R"({"scores":{"a":{"b":{"c":null}}}})", json::dump(y));
608
609 y["scores"] = std::vector<int>{1, 2, 3};
610 ASSERT_EQUAL(R"({"scores":[1,2,3]})", json::dump(y));
Ed Tanous8041f312017-04-03 09:47:01 -0700611}
612
Ed Tanous1ff48782017-04-18 12:45:08 -0700613TEST(Crow, template_basic) {
614 auto t = crow::mustache::compile(R"---(attack of {{name}})---");
615 crow::mustache::context ctx;
616 ctx["name"] = "killer tomatoes";
617 auto result = t.render(ctx);
618 ASSERT_EQUAL("attack of killer tomatoes", result);
619 // crow::mustache::load("basic.mustache");
Ed Tanous8041f312017-04-03 09:47:01 -0700620}
621
Ed Tanous1ff48782017-04-18 12:45:08 -0700622TEST(Crow, template_load) {
623 crow::mustache::set_base(".");
624 ofstream("test.mustache") << R"---(attack of {{name}})---";
625 auto t = crow::mustache::load("test.mustache");
626 crow::mustache::context ctx;
627 ctx["name"] = "killer tomatoes";
628 auto result = t.render(ctx);
629 ASSERT_EQUAL("attack of killer tomatoes", result);
630 unlink("test.mustache");
Ed Tanous8041f312017-04-03 09:47:01 -0700631}
632
Ed Tanous1ff48782017-04-18 12:45:08 -0700633TEST(Crow, black_magic) {
634 using namespace black_magic;
635 static_assert(
636 std::is_same<void, last_element_type<int, char, void>::type>::value,
637 "last_element_type");
638 static_assert(std::is_same<char, pop_back<int, char, void>::rebind<
639 last_element_type>::type>::value,
640 "pop_back");
641 static_assert(
642 std::is_same<int, pop_back<int, char, void>::rebind<pop_back>::rebind<
643 last_element_type>::type>::value,
644 "pop_back");
Ed Tanous8041f312017-04-03 09:47:01 -0700645}
646
Ed Tanous1ff48782017-04-18 12:45:08 -0700647struct NullMiddleware {
648 struct context {};
Ed Tanous8041f312017-04-03 09:47:01 -0700649
Ed Tanous1ff48782017-04-18 12:45:08 -0700650 template <typename AllContext>
651 void before_handle(request&, response&, context&, AllContext&) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700652
Ed Tanous1ff48782017-04-18 12:45:08 -0700653 template <typename AllContext>
654 void after_handle(request&, response&, context&, AllContext&) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700655};
656
Ed Tanous1ff48782017-04-18 12:45:08 -0700657struct NullSimpleMiddleware {
658 struct context {};
Ed Tanous8041f312017-04-03 09:47:01 -0700659
Ed Tanous1ff48782017-04-18 12:45:08 -0700660 void before_handle(request& /*req*/, response& /*res*/, context& /*ctx*/) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700661
Ed Tanous1ff48782017-04-18 12:45:08 -0700662 void after_handle(request& /*req*/, response& /*res*/, context& /*ctx*/) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700663};
664
Ed Tanous1ff48782017-04-18 12:45:08 -0700665TEST(Crow, middleware_simple) {
666 App<NullMiddleware, NullSimpleMiddleware> app;
667 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
668 CROW_ROUTE(app, "/")
669 ([&](const crow::request& req) {
670 app.get_context<NullMiddleware>(req);
671 app.get_context<NullSimpleMiddleware>(req);
672 return "";
673 });
Ed Tanous8041f312017-04-03 09:47:01 -0700674}
675
Ed Tanous1ff48782017-04-18 12:45:08 -0700676struct IntSettingMiddleware {
677 struct context {
678 int val;
679 };
Ed Tanous8041f312017-04-03 09:47:01 -0700680
Ed Tanous1ff48782017-04-18 12:45:08 -0700681 template <typename AllContext>
682 void before_handle(request&, response&, context& ctx, AllContext&) {
683 ctx.val = 1;
684 }
Ed Tanous8041f312017-04-03 09:47:01 -0700685
Ed Tanous1ff48782017-04-18 12:45:08 -0700686 template <typename AllContext>
687 void after_handle(request&, response&, context& ctx, AllContext&) {
688 ctx.val = 2;
689 }
Ed Tanous8041f312017-04-03 09:47:01 -0700690};
691
692std::vector<std::string> test_middleware_context_vector;
693
Ed Tanous1ff48782017-04-18 12:45:08 -0700694struct FirstMW {
695 struct context {
696 std::vector<string> v;
697 };
Ed Tanous8041f312017-04-03 09:47:01 -0700698
Ed Tanous1ff48782017-04-18 12:45:08 -0700699 void before_handle(request& /*req*/, response& /*res*/, context& ctx) {
700 ctx.v.push_back("1 before");
701 }
Ed Tanous8041f312017-04-03 09:47:01 -0700702
Ed Tanous1ff48782017-04-18 12:45:08 -0700703 void after_handle(request& /*req*/, response& /*res*/, context& ctx) {
704 ctx.v.push_back("1 after");
705 test_middleware_context_vector = ctx.v;
706 }
Ed Tanous8041f312017-04-03 09:47:01 -0700707};
708
Ed Tanous1ff48782017-04-18 12:45:08 -0700709struct SecondMW {
710 struct context {};
711 template <typename AllContext>
712 void before_handle(request& req, response& res, context&,
713 AllContext& all_ctx) {
714 all_ctx.template get<FirstMW>().v.push_back("2 before");
715 if (req.url == "/break") res.end();
716 }
Ed Tanous8041f312017-04-03 09:47:01 -0700717
Ed Tanous1ff48782017-04-18 12:45:08 -0700718 template <typename AllContext>
719 void after_handle(request&, response&, context&, AllContext& all_ctx) {
720 all_ctx.template get<FirstMW>().v.push_back("2 after");
721 }
Ed Tanous8041f312017-04-03 09:47:01 -0700722};
723
Ed Tanous1ff48782017-04-18 12:45:08 -0700724struct ThirdMW {
725 struct context {};
726 template <typename AllContext>
727 void before_handle(request&, response&, context&, AllContext& all_ctx) {
728 all_ctx.template get<FirstMW>().v.push_back("3 before");
729 }
Ed Tanous8041f312017-04-03 09:47:01 -0700730
Ed Tanous1ff48782017-04-18 12:45:08 -0700731 template <typename AllContext>
732 void after_handle(request&, response&, context&, AllContext& all_ctx) {
733 all_ctx.template get<FirstMW>().v.push_back("3 after");
734 }
Ed Tanous8041f312017-04-03 09:47:01 -0700735};
736
Ed Tanous1ff48782017-04-18 12:45:08 -0700737TEST(Crow, middleware_context) {
738 static char buf[2048];
739 // SecondMW depends on FirstMW (it uses all_ctx.get<FirstMW>)
740 // so it leads to compile error if we remove FirstMW from definition
741 // App<IntSettingMiddleware, SecondMW> app;
742 // or change the order of FirstMW and SecondMW
743 // App<IntSettingMiddleware, SecondMW, FirstMW> app;
Ed Tanous8041f312017-04-03 09:47:01 -0700744
Ed Tanous1ff48782017-04-18 12:45:08 -0700745 App<IntSettingMiddleware, FirstMW, SecondMW, ThirdMW> app;
Ed Tanous8041f312017-04-03 09:47:01 -0700746
Ed Tanous1ff48782017-04-18 12:45:08 -0700747 int x{};
748 CROW_ROUTE(app, "/")
749 ([&](const request& req) {
Ed Tanous8041f312017-04-03 09:47:01 -0700750 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700751 auto& ctx = app.get_context<IntSettingMiddleware>(req);
752 x = ctx.val;
Ed Tanous8041f312017-04-03 09:47:01 -0700753 }
754 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700755 auto& ctx = app.get_context<FirstMW>(req);
756 ctx.v.push_back("handle");
Ed Tanous8041f312017-04-03 09:47:01 -0700757 }
Ed Tanous8041f312017-04-03 09:47:01 -0700758
Ed Tanous1ff48782017-04-18 12:45:08 -0700759 return "";
760 });
761 CROW_ROUTE(app, "/break")
762 ([&](const request& req) {
Ed Tanous8041f312017-04-03 09:47:01 -0700763 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700764 auto& ctx = app.get_context<FirstMW>(req);
765 ctx.v.push_back("handle");
Ed Tanous8041f312017-04-03 09:47:01 -0700766 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700767
768 return "";
769 });
770
771 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
772 auto _ = async(launch::async, [&] { server.run(); });
773 std::string sendmsg = "GET /\r\n\r\n";
774 asio::io_service is;
775 {
776 asio::ip::tcp::socket c(is);
777 c.connect(asio::ip::tcp::endpoint(
778 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
779
780 c.send(asio::buffer(sendmsg));
781
782 c.receive(asio::buffer(buf, 2048));
783 c.close();
784 }
785 {
786 auto& out = test_middleware_context_vector;
787 ASSERT_EQUAL(1, x);
788 ASSERT_EQUAL(7, out.size());
789 ASSERT_EQUAL("1 before", out[0]);
790 ASSERT_EQUAL("2 before", out[1]);
791 ASSERT_EQUAL("3 before", out[2]);
792 ASSERT_EQUAL("handle", out[3]);
793 ASSERT_EQUAL("3 after", out[4]);
794 ASSERT_EQUAL("2 after", out[5]);
795 ASSERT_EQUAL("1 after", out[6]);
796 }
797 std::string sendmsg2 = "GET /break\r\n\r\n";
798 {
799 asio::ip::tcp::socket c(is);
800 c.connect(asio::ip::tcp::endpoint(
801 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
802
803 c.send(asio::buffer(sendmsg2));
804
805 c.receive(asio::buffer(buf, 2048));
806 c.close();
807 }
808 {
809 auto& out = test_middleware_context_vector;
810 ASSERT_EQUAL(4, out.size());
811 ASSERT_EQUAL("1 before", out[0]);
812 ASSERT_EQUAL("2 before", out[1]);
813 ASSERT_EQUAL("2 after", out[2]);
814 ASSERT_EQUAL("1 after", out[3]);
815 }
816 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700817}
818
Ed Tanous1ff48782017-04-18 12:45:08 -0700819TEST(Crow, middleware_cookieparser) {
820 static char buf[2048];
Ed Tanous8041f312017-04-03 09:47:01 -0700821
Ed Tanous1ff48782017-04-18 12:45:08 -0700822 App<CookieParser> app;
Ed Tanous8041f312017-04-03 09:47:01 -0700823
Ed Tanous1ff48782017-04-18 12:45:08 -0700824 std::string value1;
825 std::string value2;
Ed Tanous8041f312017-04-03 09:47:01 -0700826
Ed Tanous1ff48782017-04-18 12:45:08 -0700827 CROW_ROUTE(app, "/")
828 ([&](const request& req) {
829 {
830 auto& ctx = app.get_context<CookieParser>(req);
831 value1 = ctx.get_cookie("key1");
832 value2 = ctx.get_cookie("key2");
833 }
834
835 return "";
836 });
837
838 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
839 auto _ = async(launch::async, [&] { server.run(); });
840 std::string sendmsg =
841 "GET /\r\nCookie: key1=value1; key2=\"val\\\"ue2\"\r\n\r\n";
842 asio::io_service is;
843 {
844 asio::ip::tcp::socket c(is);
845 c.connect(asio::ip::tcp::endpoint(
846 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
847
848 c.send(asio::buffer(sendmsg));
849
850 c.receive(asio::buffer(buf, 2048));
851 c.close();
852 }
853 {
854 ASSERT_EQUAL("value1", value1);
855 ASSERT_EQUAL("val\"ue2", value2);
856 }
857 server.stop();
858}
859
860TEST(Crow, bug_quick_repeated_request) {
861 static char buf[2048];
862
863 SimpleApp app;
864
865 CROW_ROUTE(app, "/")([&] { return "hello"; });
866
867 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
868 auto _ = async(launch::async, [&] { server.run(); });
869 std::string sendmsg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
870 asio::io_service is;
871 {
872 std::vector<std::future<void>> v;
873 for (int i = 0; i < 5; i++) {
874 v.push_back(async(launch::async, [&] {
875 asio::ip::tcp::socket c(is);
876 c.connect(asio::ip::tcp::endpoint(
877 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
878
879 for (int j = 0; j < 5; j++) {
880 c.send(asio::buffer(sendmsg));
881
882 size_t received = c.receive(asio::buffer(buf, 2048));
883 ASSERT_EQUAL("hello",
884 std::string(buf + received - 5, buf + received));
Ed Tanous8041f312017-04-03 09:47:01 -0700885 }
Ed Tanous8041f312017-04-03 09:47:01 -0700886 c.close();
Ed Tanous1ff48782017-04-18 12:45:08 -0700887 }));
Ed Tanous8041f312017-04-03 09:47:01 -0700888 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700889 }
890 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700891}
892
Ed Tanous1ff48782017-04-18 12:45:08 -0700893TEST(Crow, simple_url_params) {
894 static char buf[2048];
Ed Tanous8041f312017-04-03 09:47:01 -0700895
Ed Tanous1ff48782017-04-18 12:45:08 -0700896 SimpleApp app;
Ed Tanous8041f312017-04-03 09:47:01 -0700897
Ed Tanous1ff48782017-04-18 12:45:08 -0700898 query_string last_url_params;
Ed Tanous8041f312017-04-03 09:47:01 -0700899
Ed Tanous1ff48782017-04-18 12:45:08 -0700900 CROW_ROUTE(app, "/params")
901 ([&last_url_params](const crow::request& req) {
902 last_url_params = std::move(req.url_params);
903 return "OK";
904 });
Ed Tanous8041f312017-04-03 09:47:01 -0700905
Ed Tanous1ff48782017-04-18 12:45:08 -0700906 /// params?h=1&foo=bar&lol&count[]=1&count[]=4&pew=5.2
Ed Tanous8041f312017-04-03 09:47:01 -0700907
Ed Tanous1ff48782017-04-18 12:45:08 -0700908 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
909 auto _ = async(launch::async, [&] { server.run(); });
910 asio::io_service is;
911 std::string sendmsg;
912
913 // check empty params
914 sendmsg = "GET /params\r\n\r\n";
915 {
916 asio::ip::tcp::socket c(is);
917 c.connect(asio::ip::tcp::endpoint(
918 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
919 c.send(asio::buffer(sendmsg));
920 c.receive(asio::buffer(buf, 2048));
921 c.close();
922
923 stringstream ss;
924 ss << last_url_params;
925
926 ASSERT_EQUAL("[ ]", ss.str());
927 }
928 // check single presence
929 sendmsg = "GET /params?foobar\r\n\r\n";
930 {
931 asio::ip::tcp::socket c(is);
932 c.connect(asio::ip::tcp::endpoint(
933 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
934 c.send(asio::buffer(sendmsg));
935 c.receive(asio::buffer(buf, 2048));
936 c.close();
937
938 ASSERT_TRUE(last_url_params.get("missing") == nullptr);
939 ASSERT_TRUE(last_url_params.get("foobar") != nullptr);
940 ASSERT_TRUE(last_url_params.get_list("missing").empty());
941 }
942 // check multiple presence
943 sendmsg = "GET /params?foo&bar&baz\r\n\r\n";
944 {
945 asio::ip::tcp::socket c(is);
946 c.connect(asio::ip::tcp::endpoint(
947 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
948 c.send(asio::buffer(sendmsg));
949 c.receive(asio::buffer(buf, 2048));
950 c.close();
951
952 ASSERT_TRUE(last_url_params.get("missing") == nullptr);
953 ASSERT_TRUE(last_url_params.get("foo") != nullptr);
954 ASSERT_TRUE(last_url_params.get("bar") != nullptr);
955 ASSERT_TRUE(last_url_params.get("baz") != nullptr);
956 }
957 // check single value
958 sendmsg = "GET /params?hello=world\r\n\r\n";
959 {
960 asio::ip::tcp::socket c(is);
961 c.connect(asio::ip::tcp::endpoint(
962 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
963 c.send(asio::buffer(sendmsg));
964 c.receive(asio::buffer(buf, 2048));
965 c.close();
966
967 ASSERT_EQUAL(string(last_url_params.get("hello")), "world");
968 }
969 // check multiple value
970 sendmsg = "GET /params?hello=world&left=right&up=down\r\n\r\n";
971 {
972 asio::ip::tcp::socket c(is);
973 c.connect(asio::ip::tcp::endpoint(
974 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
975 c.send(asio::buffer(sendmsg));
976 c.receive(asio::buffer(buf, 2048));
977 c.close();
978
979 ASSERT_EQUAL(string(last_url_params.get("hello")), "world");
980 ASSERT_EQUAL(string(last_url_params.get("left")), "right");
981 ASSERT_EQUAL(string(last_url_params.get("up")), "down");
982 }
983 // check multiple value, multiple types
984 sendmsg = "GET /params?int=100&double=123.45&boolean=1\r\n\r\n";
985 {
986 asio::ip::tcp::socket c(is);
987 c.connect(asio::ip::tcp::endpoint(
988 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
989 c.send(asio::buffer(sendmsg));
990 c.receive(asio::buffer(buf, 2048));
991 c.close();
992
993 ASSERT_EQUAL(boost::lexical_cast<int>(last_url_params.get("int")), 100);
994 ASSERT_EQUAL(boost::lexical_cast<double>(last_url_params.get("double")),
995 123.45);
996 ASSERT_EQUAL(boost::lexical_cast<bool>(last_url_params.get("boolean")),
997 true);
998 }
999 // check single array value
1000 sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n";
1001 {
1002 asio::ip::tcp::socket c(is);
1003
1004 c.connect(asio::ip::tcp::endpoint(
1005 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
1006 c.send(asio::buffer(sendmsg));
1007 c.receive(asio::buffer(buf, 2048));
1008 c.close();
1009
1010 ASSERT_TRUE(last_url_params.get("tmnt") == nullptr);
1011 ASSERT_EQUAL(last_url_params.get_list("tmnt").size(), 1);
1012 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[0]), "leonardo");
1013 }
1014 // check multiple array value
1015 sendmsg =
1016 "GET /params?tmnt[]=leonardo&tmnt[]=donatello&tmnt[]=raphael\r\n\r\n";
1017 {
1018 asio::ip::tcp::socket c(is);
1019
1020 c.connect(asio::ip::tcp::endpoint(
1021 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
1022 c.send(asio::buffer(sendmsg));
1023 c.receive(asio::buffer(buf, 2048));
1024 c.close();
1025
1026 ASSERT_EQUAL(last_url_params.get_list("tmnt").size(), 3);
1027 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[0]), "leonardo");
1028 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[1]), "donatello");
1029 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[2]), "raphael");
1030 }
1031 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -07001032}
1033
Ed Tanous1ff48782017-04-18 12:45:08 -07001034TEST(Crow, route_dynamic) {
1035 SimpleApp app;
1036 int x = 1;
1037 app.route_dynamic("/")([&] {
1038 x = 2;
1039 return "";
1040 });
Ed Tanous8041f312017-04-03 09:47:01 -07001041
Ed Tanous1ff48782017-04-18 12:45:08 -07001042 app.route_dynamic("/set4")([&](const request&) {
1043 x = 4;
1044 return "";
1045 });
1046 app.route_dynamic("/set5")([&](const request&, response& res) {
1047 x = 5;
1048 res.end();
1049 });
Ed Tanous8041f312017-04-03 09:47:01 -07001050
Ed Tanous1ff48782017-04-18 12:45:08 -07001051 app.route_dynamic("/set_int/<int>")([&](int y) {
1052 x = y;
1053 return "";
1054 });
Ed Tanous8041f312017-04-03 09:47:01 -07001055
Ed Tanous1ff48782017-04-18 12:45:08 -07001056 try {
1057 app.route_dynamic("/invalid_test/<double>/<path>")([]() { return ""; });
1058 fail();
1059 } catch (std::exception&) {
1060 }
Ed Tanous8041f312017-04-03 09:47:01 -07001061
Ed Tanous1ff48782017-04-18 12:45:08 -07001062 // app is in an invalid state when route_dynamic throws an exception.
1063 try {
1064 app.validate();
1065 fail();
1066 } catch (std::exception&) {
1067 }
Ed Tanous8041f312017-04-03 09:47:01 -07001068
Ed Tanous1ff48782017-04-18 12:45:08 -07001069 {
1070 request req;
1071 response res;
1072 req.url = "/";
1073 app.handle(req, res);
1074 ASSERT_EQUAL(x, 2);
1075 }
1076 {
1077 request req;
1078 response res;
1079 req.url = "/set_int/42";
1080 app.handle(req, res);
1081 ASSERT_EQUAL(x, 42);
1082 }
1083 {
1084 request req;
1085 response res;
1086 req.url = "/set5";
1087 app.handle(req, res);
1088 ASSERT_EQUAL(x, 5);
1089 }
1090 {
1091 request req;
1092 response res;
1093 req.url = "/set4";
1094 app.handle(req, res);
1095 ASSERT_EQUAL(x, 4);
1096 }
Ed Tanous8041f312017-04-03 09:47:01 -07001097}