blob: e84c1a5e13e644603a9dadce553eb812140e25a0 [file] [log] [blame]
Ed Tanous8041f312017-04-03 09:47:01 -07001//#define CROW_ENABLE_LOGGING
2#define CROW_ENABLE_DEBUG
3#include <iostream>
4#include <sstream>
5#include <vector>
6#include "crow.h"
7#include "gtest/gtest.h"
8#undef CROW_LOG_LEVEL
9#define CROW_LOG_LEVEL 0
10
11using namespace std;
12using namespace crow;
13
14bool failed__ = false;
Ed Tanous1ff48782017-04-18 12:45:08 -070015void error_print() { cerr << endl; }
16
17template <typename A, typename... Args>
18void error_print(const A& a, Args... args) {
19 cerr << a;
20 error_print(args...);
Ed Tanous8041f312017-04-03 09:47:01 -070021}
22
Ed Tanous1ff48782017-04-18 12:45:08 -070023template <typename... Args>
24void fail(Args... args) {
25 error_print(args...);
26 failed__ = true;
Ed Tanous8041f312017-04-03 09:47:01 -070027}
28
Ed Tanous1ff48782017-04-18 12:45:08 -070029#define ASSERT_EQUAL(a, b) \
30 if ((a) != (b)) \
31 fail(__FILE__ ":", __LINE__, ": Assert fail: expected ", (a), " actual ", \
32 (b), ", " #a " == " #b ", at " __FILE__ ":", __LINE__)
33#define ASSERT_NOTEQUAL(a, b) \
34 if ((a) == (b)) \
35 fail(__FILE__ ":", __LINE__, ": Assert fail: not expected ", (a), \
36 ", " #a " != " #b ", at " __FILE__ ":", __LINE__)
Ed Tanous8041f312017-04-03 09:47:01 -070037
Ed Tanous1ff48782017-04-18 12:45:08 -070038#define DISABLE_TEST(x) \
39 struct test##x { \
40 void test(); \
41 } x##_; \
42 void test##x::test()
Ed Tanous8041f312017-04-03 09:47:01 -070043
44#define LOCALHOST_ADDRESS "127.0.0.1"
45
Ed Tanous1ff48782017-04-18 12:45:08 -070046TEST(Crow, Rule) {
47 TaggedRule<> r("/http/");
48 r.name("abc");
Ed Tanous8041f312017-04-03 09:47:01 -070049
Ed Tanous1ff48782017-04-18 12:45:08 -070050 // empty handler - fail to validate
51 try {
Ed Tanous8041f312017-04-03 09:47:01 -070052 r.validate();
Ed Tanous1ff48782017-04-18 12:45:08 -070053 fail("empty handler should fail to validate");
54 } catch (runtime_error& e) {
55 }
Ed Tanous8041f312017-04-03 09:47:01 -070056
Ed Tanous1ff48782017-04-18 12:45:08 -070057 int x = 0;
58
59 // registering handler
60 r([&x] {
61 x = 1;
62 return "";
63 });
64
65 r.validate();
66
67 response res;
68
69 // executing handler
70 ASSERT_EQUAL(0, x);
71 r.handle(request(), res, routing_params());
72 ASSERT_EQUAL(1, x);
73
74 // registering handler with request argument
75 r([&x](const crow::request&) {
76 x = 2;
77 return "";
78 });
79
80 r.validate();
81
82 // executing handler
83 ASSERT_EQUAL(1, x);
84 r.handle(request(), res, routing_params());
85 ASSERT_EQUAL(2, x);
86}
87
88TEST(Crow, ParameterTagging) {
89 static_assert(black_magic::is_valid("<int><int><int>"), "valid url");
90 static_assert(!black_magic::is_valid("<int><int<<int>"), "invalid url");
91 static_assert(!black_magic::is_valid("nt>"), "invalid url");
92 ASSERT_EQUAL(1, black_magic::get_parameter_tag("<int>"));
93 ASSERT_EQUAL(2, black_magic::get_parameter_tag("<uint>"));
94 ASSERT_EQUAL(3, black_magic::get_parameter_tag("<float>"));
95 ASSERT_EQUAL(3, black_magic::get_parameter_tag("<double>"));
96 ASSERT_EQUAL(4, black_magic::get_parameter_tag("<str>"));
97 ASSERT_EQUAL(4, black_magic::get_parameter_tag("<string>"));
98 ASSERT_EQUAL(5, black_magic::get_parameter_tag("<path>"));
99 ASSERT_EQUAL(6 * 6 + 6 + 1,
100 black_magic::get_parameter_tag("<int><int><int>"));
101 ASSERT_EQUAL(6 * 6 + 6 + 2,
102 black_magic::get_parameter_tag("<uint><int><int>"));
103 ASSERT_EQUAL(6 * 6 + 6 * 3 + 2,
104 black_magic::get_parameter_tag("<uint><double><int>"));
105
106 // url definition parsed in compile time, build into *one number*, and given
107 // to template argument
108 static_assert(
109 std::is_same<black_magic::S<uint64_t, double, int64_t>,
110 black_magic::arguments<6 * 6 + 6 * 3 + 2>::type>::value,
111 "tag to type container");
112}
113
114TEST(Crow, PathRouting) {
115 SimpleApp app;
116
117 CROW_ROUTE(app, "/file")
118 ([] { return "file"; });
119
120 CROW_ROUTE(app, "/path/")
121 ([] { return "path"; });
122
123 {
124 request req;
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 Tanous1ff48782017-04-18 12:45:08 -0700131 ASSERT_EQUAL(200, res.code);
132 }
133 {
134 request req;
135 response res;
Ed Tanous8041f312017-04-03 09:47:01 -0700136
Ed Tanous1ff48782017-04-18 12:45:08 -0700137 req.url = "/file/";
138
139 app.handle(req, res);
140 ASSERT_EQUAL(404, res.code);
141 }
142 {
143 request req;
144 response res;
145
146 req.url = "/path";
147
148 app.handle(req, res);
149 ASSERT_NOTEQUAL(404, res.code);
150 }
151 {
152 request req;
153 response res;
154
155 req.url = "/path/";
156
157 app.handle(req, res);
158 ASSERT_EQUAL(200, res.code);
159 }
Ed Tanous8041f312017-04-03 09:47:01 -0700160}
161
Ed Tanous1ff48782017-04-18 12:45:08 -0700162TEST(Crow, RoutingTest) {
163 SimpleApp app;
164 int A{};
165 uint32_t B{};
166 double C{};
167 string D{};
168 string E{};
Ed Tanous8041f312017-04-03 09:47:01 -0700169
Ed Tanous1ff48782017-04-18 12:45:08 -0700170 CROW_ROUTE(app, "/0/<uint>")
171 ([&](uint32_t b) {
172 B = b;
173 return "OK";
174 });
175
176 CROW_ROUTE(app, "/1/<int>/<uint>")
177 ([&](int a, uint32_t b) {
178 A = a;
179 B = b;
180 return "OK";
181 });
182
183 CROW_ROUTE(app, "/4/<int>/<uint>/<double>/<string>")
184 ([&](int a, uint32_t b, double c, string d) {
185 A = a;
186 B = b;
187 C = c;
188 D = d;
189 return "OK";
190 });
191
192 CROW_ROUTE(app, "/5/<int>/<uint>/<double>/<string>/<path>")
193 ([&](int a, uint32_t b, double c, string d, string e) {
194 A = a;
195 B = b;
196 C = c;
197 D = d;
198 E = e;
199 return "OK";
200 });
201
202 app.validate();
203 // app.debug_print();
204 {
205 request req;
206 response res;
207
208 req.url = "/-1";
209
210 app.handle(req, res);
211
212 ASSERT_EQUAL(404, res.code);
213 }
214
215 {
216 request req;
217 response res;
218
219 req.url = "/0/1001999";
220
221 app.handle(req, res);
222
223 ASSERT_EQUAL(200, res.code);
224
225 ASSERT_EQUAL(1001999, B);
226 }
227
228 {
229 request req;
230 response res;
231
232 req.url = "/1/-100/1999";
233
234 app.handle(req, res);
235
236 ASSERT_EQUAL(200, res.code);
237
238 ASSERT_EQUAL(-100, A);
239 ASSERT_EQUAL(1999, B);
240 }
241 {
242 request req;
243 response res;
244
245 req.url = "/4/5000/3/-2.71828/hellhere";
246 req.add_header("TestHeader", "Value");
247
248 app.handle(req, res);
249
250 ASSERT_EQUAL(200, res.code);
251
252 ASSERT_EQUAL(5000, A);
253 ASSERT_EQUAL(3, B);
254 ASSERT_EQUAL(-2.71828, C);
255 ASSERT_EQUAL("hellhere", D);
256 }
257 {
258 request req;
259 response res;
260
261 req.url = "/5/-5/999/3.141592/hello_there/a/b/c/d";
262 req.add_header("TestHeader", "Value");
263
264 app.handle(req, res);
265
266 ASSERT_EQUAL(200, res.code);
267
268 ASSERT_EQUAL(-5, A);
269 ASSERT_EQUAL(999, B);
270 ASSERT_EQUAL(3.141592, C);
271 ASSERT_EQUAL("hello_there", D);
272 ASSERT_EQUAL("a/b/c/d", E);
273 }
Ed Tanous8041f312017-04-03 09:47:01 -0700274}
275
Ed Tanous1ff48782017-04-18 12:45:08 -0700276TEST(Crow, simple_response_routing_params) {
277 ASSERT_EQUAL(100, response(100).code);
278 ASSERT_EQUAL(200, response("Hello there").code);
279 ASSERT_EQUAL(500, response(500, "Internal Error?").code);
Ed Tanous8041f312017-04-03 09:47:01 -0700280
Ed Tanous1ff48782017-04-18 12:45:08 -0700281 routing_params rp;
282 rp.int_params.push_back(1);
283 rp.int_params.push_back(5);
284 rp.uint_params.push_back(2);
285 rp.double_params.push_back(3);
286 rp.string_params.push_back("hello");
287 ASSERT_EQUAL(1, rp.get<int64_t>(0));
288 ASSERT_EQUAL(5, rp.get<int64_t>(1));
289 ASSERT_EQUAL(2, rp.get<uint64_t>(0));
290 ASSERT_EQUAL(3, rp.get<double>(0));
291 ASSERT_EQUAL("hello", rp.get<string>(0));
Ed Tanous8041f312017-04-03 09:47:01 -0700292}
293
Ed Tanous1ff48782017-04-18 12:45:08 -0700294TEST(Crow, handler_with_response) {
295 SimpleApp app;
296 CROW_ROUTE(app, "/")([](const crow::request&, crow::response&) {});
Ed Tanous8041f312017-04-03 09:47:01 -0700297}
298
Ed Tanous1ff48782017-04-18 12:45:08 -0700299TEST(Crow, http_method) {
300 SimpleApp app;
Ed Tanous8041f312017-04-03 09:47:01 -0700301
Ed Tanous1ff48782017-04-18 12:45:08 -0700302 CROW_ROUTE(app, "/").methods("POST"_method,
303 "GET"_method)([](const request& req) {
304 if (req.method == "GET"_method)
305 return "2";
306 else
307 return "1";
308 });
309
310 CROW_ROUTE(app, "/get_only")
311 .methods("GET"_method)([](const request& /*req*/) { return "get"; });
312 CROW_ROUTE(app, "/post_only")
313 .methods("POST"_method)([](const request& /*req*/) { return "post"; });
314
315 // cannot have multiple handlers for the same url
316 // CROW_ROUTE(app, "/")
317 //.methods("GET"_method)
318 //([]{ return "2"; });
319
320 {
321 request req;
322 response res;
323
324 req.url = "/";
325 app.handle(req, res);
326
327 ASSERT_EQUAL("2", res.body);
328 }
329 {
330 request req;
331 response res;
332
333 req.url = "/";
334 req.method = "POST"_method;
335 app.handle(req, res);
336
337 ASSERT_EQUAL("1", res.body);
338 }
339
340 {
341 request req;
342 response res;
343
344 req.url = "/get_only";
345 app.handle(req, res);
346
347 ASSERT_EQUAL("get", res.body);
348 }
349
350 {
351 request req;
352 response res;
353
354 req.url = "/get_only";
355 req.method = "POST"_method;
356 app.handle(req, res);
357
358 ASSERT_NOTEQUAL("get", res.body);
359 }
Ed Tanous8041f312017-04-03 09:47:01 -0700360}
361
Ed Tanous1ff48782017-04-18 12:45:08 -0700362TEST(Crow, server_handling_error_request) {
363 static char buf[2048];
364 SimpleApp app;
365 CROW_ROUTE(app, "/")([] { return "A"; });
366 Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451);
367 auto _ = async(launch::async, [&] { server.run(); });
368 std::string sendmsg = "POX";
369 asio::io_service is;
370 {
371 asio::ip::tcp::socket c(is);
372 c.connect(asio::ip::tcp::endpoint(
373 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
Ed Tanous8041f312017-04-03 09:47:01 -0700374
Ed Tanous1ff48782017-04-18 12:45:08 -0700375 c.send(asio::buffer(sendmsg));
Ed Tanous8041f312017-04-03 09:47:01 -0700376
Ed Tanous1ff48782017-04-18 12:45:08 -0700377 try {
378 c.receive(asio::buffer(buf, 2048));
379 fail();
380 } catch (std::exception& e) {
381 // std::cerr << e.what() << std::endl;
Ed Tanous8041f312017-04-03 09:47:01 -0700382 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700383 }
384 server.stop();
385}
Ed Tanous8041f312017-04-03 09:47:01 -0700386
Ed Tanous1ff48782017-04-18 12:45:08 -0700387TEST(Crow, multi_server) {
388 static char buf[2048];
389 SimpleApp app1, app2;
390 CROW_ROUTE(app1, "/").methods("GET"_method,
391 "POST"_method)([] { return "A"; });
392 CROW_ROUTE(app2, "/").methods("GET"_method,
393 "POST"_method)([] { return "B"; });
Ed Tanous8041f312017-04-03 09:47:01 -0700394
Ed Tanous1ff48782017-04-18 12:45:08 -0700395 Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451);
396 Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452);
397
398 auto _ = async(launch::async, [&] { server1.run(); });
399 auto _2 = async(launch::async, [&] { server2.run(); });
400
401 std::string sendmsg =
402 "POST /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=B\r\n";
403 asio::io_service is;
404 {
405 asio::ip::tcp::socket c(is);
406 c.connect(asio::ip::tcp::endpoint(
407 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
408
409 c.send(asio::buffer(sendmsg));
410
411 size_t recved = c.receive(asio::buffer(buf, 2048));
412 ASSERT_EQUAL('A', buf[recved - 1]);
413 }
414
415 {
416 asio::ip::tcp::socket c(is);
417 c.connect(asio::ip::tcp::endpoint(
418 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45452));
419
420 for (auto ch : sendmsg) {
421 char buf[1] = {ch};
422 c.send(asio::buffer(buf));
Ed Tanous8041f312017-04-03 09:47:01 -0700423 }
424
Ed Tanous1ff48782017-04-18 12:45:08 -0700425 size_t recved = c.receive(asio::buffer(buf, 2048));
426 ASSERT_EQUAL('B', buf[recved - 1]);
427 }
Ed Tanous8041f312017-04-03 09:47:01 -0700428
Ed Tanous1ff48782017-04-18 12:45:08 -0700429 server1.stop();
430 server2.stop();
431}
Ed Tanous8041f312017-04-03 09:47:01 -0700432
Ed Tanous1ff48782017-04-18 12:45:08 -0700433TEST(Crow, json_read) {
434 {
435 const char* json_error_tests[] = {
436 "{} 3",
437 "{{}",
438 "{3}",
439 "3.4.5",
440 "+3",
441 "3-2",
442 "00",
443 "03",
444 "1e3e3",
445 "1e+.3",
446 "nll",
447 "f",
448 "t",
449 "{\"x\":3,}",
450 "{\"x\"}",
451 "{\"x\":3 q}",
452 "{\"x\":[3 4]}",
453 "{\"x\":[\"",
454 "{\"x\":[[], 4],\"y\",}",
455 "{\"x\":[3",
456 "{\"x\":[ null, false, true}",
457 };
458 for (auto s : json_error_tests) {
459 auto x = json::load(s);
460 if (x) {
461 fail("should fail to parse ", s);
462 return;
463 }
Ed Tanous8041f312017-04-03 09:47:01 -0700464 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700465 }
Ed Tanous8041f312017-04-03 09:47:01 -0700466
Ed Tanous1ff48782017-04-18 12:45:08 -0700467 auto x = json::load(R"({"message":"hello, world"})");
468 if (!x) fail("fail to parse");
469 ASSERT_EQUAL("hello, world", x["message"]);
470 ASSERT_EQUAL(1, x.size());
471 ASSERT_EQUAL(false, x.has("mess"));
472 ASSERT_THROW(x["mess"], std::exception);
473 // TODO returning false is better than exception
474 // ASSERT_THROW(3 == x["message"], std::exception);
475 ASSERT_EQUAL(12, x["message"].size());
Ed Tanous8041f312017-04-03 09:47:01 -0700476
Ed Tanous1ff48782017-04-18 12:45:08 -0700477 std::string s = R"({"int":3, "ints" :[1,2,3,4,5] })";
478 auto y = json::load(s);
479 ASSERT_EQUAL(3, y["int"]);
480 ASSERT_EQUAL(3.0, y["int"]);
481 ASSERT_NOTEQUAL(3.01, y["int"]);
482 ASSERT_EQUAL(5, y["ints"].size());
483 ASSERT_EQUAL(1, y["ints"][0]);
484 ASSERT_EQUAL(2, y["ints"][1]);
485 ASSERT_EQUAL(3, y["ints"][2]);
486 ASSERT_EQUAL(4, y["ints"][3]);
487 ASSERT_EQUAL(5, y["ints"][4]);
488 ASSERT_EQUAL(1u, y["ints"][0]);
489 ASSERT_EQUAL(1.f, y["ints"][0]);
Ed Tanous8041f312017-04-03 09:47:01 -0700490
Ed Tanous1ff48782017-04-18 12:45:08 -0700491 int q = (int)y["ints"][1];
492 ASSERT_EQUAL(2, q);
493 q = y["ints"][2].i();
494 ASSERT_EQUAL(3, q);
495
496 std::string s2 = R"({"bools":[true, false], "doubles":[1.2, -3.4]})";
497 auto z = json::load(s2);
498 ASSERT_EQUAL(2, z["bools"].size());
499 ASSERT_EQUAL(2, z["doubles"].size());
500 ASSERT_EQUAL(true, z["bools"][0].b());
501 ASSERT_EQUAL(false, z["bools"][1].b());
502 ASSERT_EQUAL(1.2, z["doubles"][0].d());
503 ASSERT_EQUAL(-3.4, z["doubles"][1].d());
504
505 std::string s3 = R"({"uint64": 18446744073709551615})";
506 auto z1 = json::load(s3);
507 ASSERT_EQUAL(18446744073709551615ull, z1["uint64"].u());
508}
509
510TEST(Crow, json_read_real) {
511 vector<std::string> v{"0.036303908355795146",
512 "0.18320417789757412",
513 "0.05319940476190476",
514 "0.15224702380952382",
515 "0",
516 "0.3296201145552561",
517 "0.47921580188679247",
518 "0.05873511904761905",
519 "0.1577827380952381",
520 "0.4996841307277628",
521 "0.6425412735849056",
522 "0.052113095238095236",
523 "0.12830357142857143",
524 "0.7871041105121294",
525 "0.954220013477089",
526 "0.05869047619047619",
527 "0.1625",
528 "0.8144794474393531",
529 "0.9721613881401617",
530 "0.1399404761904762",
531 "0.24470238095238095",
532 "0.04527459568733154",
533 "0.2096950808625337",
534 "0.35267857142857145",
535 "0.42791666666666667",
536 "0.855731974393531",
537 "0.9352467991913747",
538 "0.3816220238095238",
539 "0.4282886904761905",
540 "0.39414167789757415",
541 "0.5316079851752021",
542 "0.3809375",
543 "0.4571279761904762",
544 "0.03522995283018868",
545 "0.1915641846361186",
546 "0.6164136904761904",
547 "0.7192708333333333",
548 "0.05675117924528302",
549 "0.21308541105121293",
550 "0.7045386904761904",
551 "0.8016815476190476"};
552 for (auto x : v) {
553 CROW_LOG_DEBUG << x;
554 ASSERT_EQUAL(json::load(x).d(), boost::lexical_cast<double>(x));
555 }
556
557 auto ret = json::load(
558 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"}]})---");
559 ASSERT_TRUE(ret);
560}
561
562TEST(Crow, json_read_unescaping) {
563 {
564 auto x = json::load(R"({"data":"\ud55c\n\t\r"})");
565 if (!x) {
566 fail("fail to parse");
567 return;
Ed Tanous8041f312017-04-03 09:47:01 -0700568 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700569 ASSERT_EQUAL(6, x["data"].size());
570 ASSERT_EQUAL("한\n\t\r", x["data"]);
571 }
572 {
573 // multiple r_string instance
574 auto x = json::load(R"({"data":"\ud55c\n\t\r"})");
575 auto a = x["data"].s();
576 auto b = x["data"].s();
577 ASSERT_EQUAL(6, a.size());
578 ASSERT_EQUAL(6, b.size());
579 ASSERT_EQUAL(6, x["data"].size());
580 }
Ed Tanous8041f312017-04-03 09:47:01 -0700581}
582
Ed Tanous1ff48782017-04-18 12:45:08 -0700583TEST(Crow, json_write) {
584 json::wvalue x;
585 x["message"] = "hello world";
586 ASSERT_EQUAL(R"({"message":"hello world"})", json::dump(x));
587 x["message"] = std::string("string value");
588 ASSERT_EQUAL(R"({"message":"string value"})", json::dump(x));
589 x["message"]["x"] = 3;
590 ASSERT_EQUAL(R"({"message":{"x":3}})", json::dump(x));
591 x["message"]["y"] = 5;
592 ASSERT_TRUE(R"({"message":{"x":3,"y":5}})" == json::dump(x) ||
593 R"({"message":{"y":5,"x":3}})" == json::dump(x));
594 x["message"] = 5.5;
595 ASSERT_EQUAL(R"({"message":5.5})", json::dump(x));
Ed Tanous8041f312017-04-03 09:47:01 -0700596
Ed Tanous1ff48782017-04-18 12:45:08 -0700597 json::wvalue y;
598 y["scores"][0] = 1;
599 y["scores"][1] = "king";
600 y["scores"][2] = 3.5;
601 ASSERT_EQUAL(R"({"scores":[1,"king",3.5]})", json::dump(y));
Ed Tanous8041f312017-04-03 09:47:01 -0700602
Ed Tanous1ff48782017-04-18 12:45:08 -0700603 y["scores"][2][0] = "real";
604 y["scores"][2][1] = false;
605 y["scores"][2][2] = true;
606 ASSERT_EQUAL(R"({"scores":[1,"king",["real",false,true]]})", json::dump(y));
Ed Tanous8041f312017-04-03 09:47:01 -0700607
Ed Tanous1ff48782017-04-18 12:45:08 -0700608 y["scores"]["a"]["b"]["c"] = nullptr;
609 ASSERT_EQUAL(R"({"scores":{"a":{"b":{"c":null}}}})", json::dump(y));
610
611 y["scores"] = std::vector<int>{1, 2, 3};
612 ASSERT_EQUAL(R"({"scores":[1,2,3]})", json::dump(y));
Ed Tanous8041f312017-04-03 09:47:01 -0700613}
614
Ed Tanous1ff48782017-04-18 12:45:08 -0700615TEST(Crow, template_basic) {
616 auto t = crow::mustache::compile(R"---(attack of {{name}})---");
617 crow::mustache::context ctx;
618 ctx["name"] = "killer tomatoes";
619 auto result = t.render(ctx);
620 ASSERT_EQUAL("attack of killer tomatoes", result);
621 // crow::mustache::load("basic.mustache");
Ed Tanous8041f312017-04-03 09:47:01 -0700622}
623
Ed Tanous1ff48782017-04-18 12:45:08 -0700624TEST(Crow, template_load) {
625 crow::mustache::set_base(".");
626 ofstream("test.mustache") << R"---(attack of {{name}})---";
627 auto t = crow::mustache::load("test.mustache");
628 crow::mustache::context ctx;
629 ctx["name"] = "killer tomatoes";
630 auto result = t.render(ctx);
631 ASSERT_EQUAL("attack of killer tomatoes", result);
632 unlink("test.mustache");
Ed Tanous8041f312017-04-03 09:47:01 -0700633}
634
Ed Tanous1ff48782017-04-18 12:45:08 -0700635TEST(Crow, black_magic) {
636 using namespace black_magic;
637 static_assert(
638 std::is_same<void, last_element_type<int, char, void>::type>::value,
639 "last_element_type");
640 static_assert(std::is_same<char, pop_back<int, char, void>::rebind<
641 last_element_type>::type>::value,
642 "pop_back");
643 static_assert(
644 std::is_same<int, pop_back<int, char, void>::rebind<pop_back>::rebind<
645 last_element_type>::type>::value,
646 "pop_back");
Ed Tanous8041f312017-04-03 09:47:01 -0700647}
648
Ed Tanous1ff48782017-04-18 12:45:08 -0700649struct NullMiddleware {
650 struct context {};
Ed Tanous8041f312017-04-03 09:47:01 -0700651
Ed Tanous1ff48782017-04-18 12:45:08 -0700652 template <typename AllContext>
653 void before_handle(request&, response&, context&, AllContext&) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700654
Ed Tanous1ff48782017-04-18 12:45:08 -0700655 template <typename AllContext>
656 void after_handle(request&, response&, context&, AllContext&) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700657};
658
Ed Tanous1ff48782017-04-18 12:45:08 -0700659struct NullSimpleMiddleware {
660 struct context {};
Ed Tanous8041f312017-04-03 09:47:01 -0700661
Ed Tanous1ff48782017-04-18 12:45:08 -0700662 void before_handle(request& /*req*/, response& /*res*/, context& /*ctx*/) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700663
Ed Tanous1ff48782017-04-18 12:45:08 -0700664 void after_handle(request& /*req*/, response& /*res*/, context& /*ctx*/) {}
Ed Tanous8041f312017-04-03 09:47:01 -0700665};
666
Ed Tanous1ff48782017-04-18 12:45:08 -0700667TEST(Crow, middleware_simple) {
668 App<NullMiddleware, NullSimpleMiddleware> app;
669 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
670 CROW_ROUTE(app, "/")
671 ([&](const crow::request& req) {
672 app.get_context<NullMiddleware>(req);
673 app.get_context<NullSimpleMiddleware>(req);
674 return "";
675 });
Ed Tanous8041f312017-04-03 09:47:01 -0700676}
677
Ed Tanous1ff48782017-04-18 12:45:08 -0700678struct IntSettingMiddleware {
679 struct context {
680 int val;
681 };
Ed Tanous8041f312017-04-03 09:47:01 -0700682
Ed Tanous1ff48782017-04-18 12:45:08 -0700683 template <typename AllContext>
684 void before_handle(request&, response&, context& ctx, AllContext&) {
685 ctx.val = 1;
686 }
Ed Tanous8041f312017-04-03 09:47:01 -0700687
Ed Tanous1ff48782017-04-18 12:45:08 -0700688 template <typename AllContext>
689 void after_handle(request&, response&, context& ctx, AllContext&) {
690 ctx.val = 2;
691 }
Ed Tanous8041f312017-04-03 09:47:01 -0700692};
693
694std::vector<std::string> test_middleware_context_vector;
695
Ed Tanous1ff48782017-04-18 12:45:08 -0700696struct FirstMW {
697 struct context {
698 std::vector<string> v;
699 };
Ed Tanous8041f312017-04-03 09:47:01 -0700700
Ed Tanous1ff48782017-04-18 12:45:08 -0700701 void before_handle(request& /*req*/, response& /*res*/, context& ctx) {
702 ctx.v.push_back("1 before");
703 }
Ed Tanous8041f312017-04-03 09:47:01 -0700704
Ed Tanous1ff48782017-04-18 12:45:08 -0700705 void after_handle(request& /*req*/, response& /*res*/, context& ctx) {
706 ctx.v.push_back("1 after");
707 test_middleware_context_vector = ctx.v;
708 }
Ed Tanous8041f312017-04-03 09:47:01 -0700709};
710
Ed Tanous1ff48782017-04-18 12:45:08 -0700711struct SecondMW {
712 struct context {};
713 template <typename AllContext>
714 void before_handle(request& req, response& res, context&,
715 AllContext& all_ctx) {
716 all_ctx.template get<FirstMW>().v.push_back("2 before");
717 if (req.url == "/break") res.end();
718 }
Ed Tanous8041f312017-04-03 09:47:01 -0700719
Ed Tanous1ff48782017-04-18 12:45:08 -0700720 template <typename AllContext>
721 void after_handle(request&, response&, context&, AllContext& all_ctx) {
722 all_ctx.template get<FirstMW>().v.push_back("2 after");
723 }
Ed Tanous8041f312017-04-03 09:47:01 -0700724};
725
Ed Tanous1ff48782017-04-18 12:45:08 -0700726struct ThirdMW {
727 struct context {};
728 template <typename AllContext>
729 void before_handle(request&, response&, context&, AllContext& all_ctx) {
730 all_ctx.template get<FirstMW>().v.push_back("3 before");
731 }
Ed Tanous8041f312017-04-03 09:47:01 -0700732
Ed Tanous1ff48782017-04-18 12:45:08 -0700733 template <typename AllContext>
734 void after_handle(request&, response&, context&, AllContext& all_ctx) {
735 all_ctx.template get<FirstMW>().v.push_back("3 after");
736 }
Ed Tanous8041f312017-04-03 09:47:01 -0700737};
738
Ed Tanous1ff48782017-04-18 12:45:08 -0700739TEST(Crow, middleware_context) {
740 static char buf[2048];
741 // SecondMW depends on FirstMW (it uses all_ctx.get<FirstMW>)
742 // so it leads to compile error if we remove FirstMW from definition
743 // App<IntSettingMiddleware, SecondMW> app;
744 // or change the order of FirstMW and SecondMW
745 // App<IntSettingMiddleware, SecondMW, FirstMW> app;
Ed Tanous8041f312017-04-03 09:47:01 -0700746
Ed Tanous1ff48782017-04-18 12:45:08 -0700747 App<IntSettingMiddleware, FirstMW, SecondMW, ThirdMW> app;
Ed Tanous8041f312017-04-03 09:47:01 -0700748
Ed Tanous1ff48782017-04-18 12:45:08 -0700749 int x{};
750 CROW_ROUTE(app, "/")
751 ([&](const request& req) {
Ed Tanous8041f312017-04-03 09:47:01 -0700752 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700753 auto& ctx = app.get_context<IntSettingMiddleware>(req);
754 x = ctx.val;
Ed Tanous8041f312017-04-03 09:47:01 -0700755 }
756 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700757 auto& ctx = app.get_context<FirstMW>(req);
758 ctx.v.push_back("handle");
Ed Tanous8041f312017-04-03 09:47:01 -0700759 }
Ed Tanous8041f312017-04-03 09:47:01 -0700760
Ed Tanous1ff48782017-04-18 12:45:08 -0700761 return "";
762 });
763 CROW_ROUTE(app, "/break")
764 ([&](const request& req) {
Ed Tanous8041f312017-04-03 09:47:01 -0700765 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700766 auto& ctx = app.get_context<FirstMW>(req);
767 ctx.v.push_back("handle");
Ed Tanous8041f312017-04-03 09:47:01 -0700768 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700769
770 return "";
771 });
772
773 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
774 auto _ = async(launch::async, [&] { server.run(); });
775 std::string sendmsg = "GET /\r\n\r\n";
776 asio::io_service is;
777 {
778 asio::ip::tcp::socket c(is);
779 c.connect(asio::ip::tcp::endpoint(
780 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
781
782 c.send(asio::buffer(sendmsg));
783
784 c.receive(asio::buffer(buf, 2048));
785 c.close();
786 }
787 {
788 auto& out = test_middleware_context_vector;
789 ASSERT_EQUAL(1, x);
790 ASSERT_EQUAL(7, out.size());
791 ASSERT_EQUAL("1 before", out[0]);
792 ASSERT_EQUAL("2 before", out[1]);
793 ASSERT_EQUAL("3 before", out[2]);
794 ASSERT_EQUAL("handle", out[3]);
795 ASSERT_EQUAL("3 after", out[4]);
796 ASSERT_EQUAL("2 after", out[5]);
797 ASSERT_EQUAL("1 after", out[6]);
798 }
799 std::string sendmsg2 = "GET /break\r\n\r\n";
800 {
801 asio::ip::tcp::socket c(is);
802 c.connect(asio::ip::tcp::endpoint(
803 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
804
805 c.send(asio::buffer(sendmsg2));
806
807 c.receive(asio::buffer(buf, 2048));
808 c.close();
809 }
810 {
811 auto& out = test_middleware_context_vector;
812 ASSERT_EQUAL(4, out.size());
813 ASSERT_EQUAL("1 before", out[0]);
814 ASSERT_EQUAL("2 before", out[1]);
815 ASSERT_EQUAL("2 after", out[2]);
816 ASSERT_EQUAL("1 after", out[3]);
817 }
818 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700819}
820
Ed Tanous1ff48782017-04-18 12:45:08 -0700821TEST(Crow, middleware_cookieparser) {
822 static char buf[2048];
Ed Tanous8041f312017-04-03 09:47:01 -0700823
Ed Tanous1ff48782017-04-18 12:45:08 -0700824 App<CookieParser> app;
Ed Tanous8041f312017-04-03 09:47:01 -0700825
Ed Tanous1ff48782017-04-18 12:45:08 -0700826 std::string value1;
827 std::string value2;
Ed Tanous8041f312017-04-03 09:47:01 -0700828
Ed Tanous1ff48782017-04-18 12:45:08 -0700829 CROW_ROUTE(app, "/")
830 ([&](const request& req) {
831 {
832 auto& ctx = app.get_context<CookieParser>(req);
833 value1 = ctx.get_cookie("key1");
834 value2 = ctx.get_cookie("key2");
835 }
836
837 return "";
838 });
839
840 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
841 auto _ = async(launch::async, [&] { server.run(); });
842 std::string sendmsg =
843 "GET /\r\nCookie: key1=value1; key2=\"val\\\"ue2\"\r\n\r\n";
844 asio::io_service is;
845 {
846 asio::ip::tcp::socket c(is);
847 c.connect(asio::ip::tcp::endpoint(
848 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
849
850 c.send(asio::buffer(sendmsg));
851
852 c.receive(asio::buffer(buf, 2048));
853 c.close();
854 }
855 {
856 ASSERT_EQUAL("value1", value1);
857 ASSERT_EQUAL("val\"ue2", value2);
858 }
859 server.stop();
860}
861
862TEST(Crow, bug_quick_repeated_request) {
863 static char buf[2048];
864
865 SimpleApp app;
866
867 CROW_ROUTE(app, "/")([&] { return "hello"; });
868
869 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
870 auto _ = async(launch::async, [&] { server.run(); });
871 std::string sendmsg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
872 asio::io_service is;
873 {
874 std::vector<std::future<void>> v;
875 for (int i = 0; i < 5; i++) {
876 v.push_back(async(launch::async, [&] {
877 asio::ip::tcp::socket c(is);
878 c.connect(asio::ip::tcp::endpoint(
879 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
880
881 for (int j = 0; j < 5; j++) {
882 c.send(asio::buffer(sendmsg));
883
884 size_t received = c.receive(asio::buffer(buf, 2048));
885 ASSERT_EQUAL("hello",
886 std::string(buf + received - 5, buf + received));
Ed Tanous8041f312017-04-03 09:47:01 -0700887 }
Ed Tanous8041f312017-04-03 09:47:01 -0700888 c.close();
Ed Tanous1ff48782017-04-18 12:45:08 -0700889 }));
Ed Tanous8041f312017-04-03 09:47:01 -0700890 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700891 }
892 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700893}
894
Ed Tanous1ff48782017-04-18 12:45:08 -0700895TEST(Crow, simple_url_params) {
896 static char buf[2048];
Ed Tanous8041f312017-04-03 09:47:01 -0700897
Ed Tanous1ff48782017-04-18 12:45:08 -0700898 SimpleApp app;
Ed Tanous8041f312017-04-03 09:47:01 -0700899
Ed Tanous1ff48782017-04-18 12:45:08 -0700900 query_string last_url_params;
Ed Tanous8041f312017-04-03 09:47:01 -0700901
Ed Tanous1ff48782017-04-18 12:45:08 -0700902 CROW_ROUTE(app, "/params")
903 ([&last_url_params](const crow::request& req) {
904 last_url_params = std::move(req.url_params);
905 return "OK";
906 });
Ed Tanous8041f312017-04-03 09:47:01 -0700907
Ed Tanous1ff48782017-04-18 12:45:08 -0700908 /// params?h=1&foo=bar&lol&count[]=1&count[]=4&pew=5.2
Ed Tanous8041f312017-04-03 09:47:01 -0700909
Ed Tanous1ff48782017-04-18 12:45:08 -0700910 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
911 auto _ = async(launch::async, [&] { server.run(); });
912 asio::io_service is;
913 std::string sendmsg;
914
915 // check empty params
916 sendmsg = "GET /params\r\n\r\n";
917 {
918 asio::ip::tcp::socket c(is);
919 c.connect(asio::ip::tcp::endpoint(
920 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
921 c.send(asio::buffer(sendmsg));
922 c.receive(asio::buffer(buf, 2048));
923 c.close();
924
925 stringstream ss;
926 ss << last_url_params;
927
928 ASSERT_EQUAL("[ ]", ss.str());
929 }
930 // check single presence
931 sendmsg = "GET /params?foobar\r\n\r\n";
932 {
933 asio::ip::tcp::socket c(is);
934 c.connect(asio::ip::tcp::endpoint(
935 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
936 c.send(asio::buffer(sendmsg));
937 c.receive(asio::buffer(buf, 2048));
938 c.close();
939
940 ASSERT_TRUE(last_url_params.get("missing") == nullptr);
941 ASSERT_TRUE(last_url_params.get("foobar") != nullptr);
942 ASSERT_TRUE(last_url_params.get_list("missing").empty());
943 }
944 // check multiple presence
945 sendmsg = "GET /params?foo&bar&baz\r\n\r\n";
946 {
947 asio::ip::tcp::socket c(is);
948 c.connect(asio::ip::tcp::endpoint(
949 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
950 c.send(asio::buffer(sendmsg));
951 c.receive(asio::buffer(buf, 2048));
952 c.close();
953
954 ASSERT_TRUE(last_url_params.get("missing") == nullptr);
955 ASSERT_TRUE(last_url_params.get("foo") != nullptr);
956 ASSERT_TRUE(last_url_params.get("bar") != nullptr);
957 ASSERT_TRUE(last_url_params.get("baz") != nullptr);
958 }
959 // check single value
960 sendmsg = "GET /params?hello=world\r\n\r\n";
961 {
962 asio::ip::tcp::socket c(is);
963 c.connect(asio::ip::tcp::endpoint(
964 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
965 c.send(asio::buffer(sendmsg));
966 c.receive(asio::buffer(buf, 2048));
967 c.close();
968
969 ASSERT_EQUAL(string(last_url_params.get("hello")), "world");
970 }
971 // check multiple value
972 sendmsg = "GET /params?hello=world&left=right&up=down\r\n\r\n";
973 {
974 asio::ip::tcp::socket c(is);
975 c.connect(asio::ip::tcp::endpoint(
976 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
977 c.send(asio::buffer(sendmsg));
978 c.receive(asio::buffer(buf, 2048));
979 c.close();
980
981 ASSERT_EQUAL(string(last_url_params.get("hello")), "world");
982 ASSERT_EQUAL(string(last_url_params.get("left")), "right");
983 ASSERT_EQUAL(string(last_url_params.get("up")), "down");
984 }
985 // check multiple value, multiple types
986 sendmsg = "GET /params?int=100&double=123.45&boolean=1\r\n\r\n";
987 {
988 asio::ip::tcp::socket c(is);
989 c.connect(asio::ip::tcp::endpoint(
990 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
991 c.send(asio::buffer(sendmsg));
992 c.receive(asio::buffer(buf, 2048));
993 c.close();
994
995 ASSERT_EQUAL(boost::lexical_cast<int>(last_url_params.get("int")), 100);
996 ASSERT_EQUAL(boost::lexical_cast<double>(last_url_params.get("double")),
997 123.45);
998 ASSERT_EQUAL(boost::lexical_cast<bool>(last_url_params.get("boolean")),
999 true);
1000 }
1001 // check single array value
1002 sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n";
1003 {
1004 asio::ip::tcp::socket c(is);
1005
1006 c.connect(asio::ip::tcp::endpoint(
1007 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
1008 c.send(asio::buffer(sendmsg));
1009 c.receive(asio::buffer(buf, 2048));
1010 c.close();
1011
1012 ASSERT_TRUE(last_url_params.get("tmnt") == nullptr);
1013 ASSERT_EQUAL(last_url_params.get_list("tmnt").size(), 1);
1014 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[0]), "leonardo");
1015 }
1016 // check multiple array value
1017 sendmsg =
1018 "GET /params?tmnt[]=leonardo&tmnt[]=donatello&tmnt[]=raphael\r\n\r\n";
1019 {
1020 asio::ip::tcp::socket c(is);
1021
1022 c.connect(asio::ip::tcp::endpoint(
1023 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
1024 c.send(asio::buffer(sendmsg));
1025 c.receive(asio::buffer(buf, 2048));
1026 c.close();
1027
1028 ASSERT_EQUAL(last_url_params.get_list("tmnt").size(), 3);
1029 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[0]), "leonardo");
1030 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[1]), "donatello");
1031 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[2]), "raphael");
1032 }
1033 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -07001034}
1035
Ed Tanous1ff48782017-04-18 12:45:08 -07001036TEST(Crow, route_dynamic) {
1037 SimpleApp app;
1038 int x = 1;
1039 app.route_dynamic("/")([&] {
1040 x = 2;
1041 return "";
1042 });
Ed Tanous8041f312017-04-03 09:47:01 -07001043
Ed Tanous1ff48782017-04-18 12:45:08 -07001044 app.route_dynamic("/set4")([&](const request&) {
1045 x = 4;
1046 return "";
1047 });
1048 app.route_dynamic("/set5")([&](const request&, response& res) {
1049 x = 5;
1050 res.end();
1051 });
Ed Tanous8041f312017-04-03 09:47:01 -07001052
Ed Tanous1ff48782017-04-18 12:45:08 -07001053 app.route_dynamic("/set_int/<int>")([&](int y) {
1054 x = y;
1055 return "";
1056 });
Ed Tanous8041f312017-04-03 09:47:01 -07001057
Ed Tanous1ff48782017-04-18 12:45:08 -07001058 try {
1059 app.route_dynamic("/invalid_test/<double>/<path>")([]() { return ""; });
1060 fail();
1061 } catch (std::exception&) {
1062 }
Ed Tanous8041f312017-04-03 09:47:01 -07001063
Ed Tanous1ff48782017-04-18 12:45:08 -07001064 // app is in an invalid state when route_dynamic throws an exception.
1065 try {
1066 app.validate();
1067 fail();
1068 } catch (std::exception&) {
1069 }
Ed Tanous8041f312017-04-03 09:47:01 -07001070
Ed Tanous1ff48782017-04-18 12:45:08 -07001071 {
1072 request req;
1073 response res;
1074 req.url = "/";
1075 app.handle(req, res);
1076 ASSERT_EQUAL(x, 2);
1077 }
1078 {
1079 request req;
1080 response res;
1081 req.url = "/set_int/42";
1082 app.handle(req, res);
1083 ASSERT_EQUAL(x, 42);
1084 }
1085 {
1086 request req;
1087 response res;
1088 req.url = "/set5";
1089 app.handle(req, res);
1090 ASSERT_EQUAL(x, 5);
1091 }
1092 {
1093 request req;
1094 response res;
1095 req.url = "/set4";
1096 app.handle(req, res);
1097 ASSERT_EQUAL(x, 4);
1098 }
Ed Tanous8041f312017-04-03 09:47:01 -07001099}