blob: 8072c92e4ed484cfb1b513075a5290f9aa476b59 [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;
15void error_print()
16{
17 cerr << endl;
18}
19
20template <typename A, typename ...Args>
21void error_print(const A& a, Args...args)
22{
23 cerr<<a;
24 error_print(args...);
25}
26
27template <typename ...Args>
28void fail(Args...args) { error_print(args...);failed__ = true; }
29
30#define ASSERT_EQUAL(a, b) if ((a) != (b)) fail(__FILE__ ":", __LINE__, ": Assert fail: expected ", (a), " actual ", (b), ", " #a " == " #b ", at " __FILE__ ":",__LINE__)
31#define ASSERT_NOTEQUAL(a, b) if ((a) == (b)) fail(__FILE__ ":", __LINE__, ": Assert fail: not expected ", (a), ", " #a " != " #b ", at " __FILE__ ":",__LINE__)
32
33#define DISABLE_TEST(x) struct test##x{void test();}x##_; \
34 void test##x::test()
35
36
37#define LOCALHOST_ADDRESS "127.0.0.1"
38
39
40TEST(Crow, Rule)
41{
42 TaggedRule<> r("/http/");
43 r.name("abc");
44
45 // empty handler - fail to validate
46 try
47 {
48 r.validate();
49 fail("empty handler should fail to validate");
50 }
51 catch(runtime_error& e)
52 {
53 }
54
55 int x = 0;
56
57 // registering handler
58 r([&x]{x = 1;return "";});
59
60 r.validate();
61
62 response res;
63
64 // executing handler
65 ASSERT_EQUAL(0, x);
66 r.handle(request(), res, routing_params());
67 ASSERT_EQUAL(1, x);
68
69 // registering handler with request argument
70 r([&x](const crow::request&){x = 2;return "";});
71
72 r.validate();
73
74 // executing handler
75 ASSERT_EQUAL(1, x);
76 r.handle(request(), res, routing_params());
77 ASSERT_EQUAL(2, x);
78}
79
80TEST(Crow, ParameterTagging)
81{
82 static_assert(black_magic::is_valid("<int><int><int>"), "valid url");
83 static_assert(!black_magic::is_valid("<int><int<<int>"), "invalid url");
84 static_assert(!black_magic::is_valid("nt>"), "invalid url");
85 ASSERT_EQUAL(1, black_magic::get_parameter_tag("<int>"));
86 ASSERT_EQUAL(2, black_magic::get_parameter_tag("<uint>"));
87 ASSERT_EQUAL(3, black_magic::get_parameter_tag("<float>"));
88 ASSERT_EQUAL(3, black_magic::get_parameter_tag("<double>"));
89 ASSERT_EQUAL(4, black_magic::get_parameter_tag("<str>"));
90 ASSERT_EQUAL(4, black_magic::get_parameter_tag("<string>"));
91 ASSERT_EQUAL(5, black_magic::get_parameter_tag("<path>"));
92 ASSERT_EQUAL(6*6+6+1, black_magic::get_parameter_tag("<int><int><int>"));
93 ASSERT_EQUAL(6*6+6+2, black_magic::get_parameter_tag("<uint><int><int>"));
94 ASSERT_EQUAL(6*6+6*3+2, black_magic::get_parameter_tag("<uint><double><int>"));
95
96 // url definition parsed in compile time, build into *one number*, and given to template argument
97 static_assert(std::is_same<black_magic::S<uint64_t, double, int64_t>, black_magic::arguments<6*6+6*3+2>::type>::value, "tag to type container");
98}
99
100TEST(Crow, PathRouting)
101{
102 SimpleApp app;
103
104 CROW_ROUTE(app, "/file")
105 ([]{
106 return "file";
107 });
108
109 CROW_ROUTE(app, "/path/")
110 ([]{
111 return "path";
112 });
113
114 {
115 request req;
116 response res;
117
118 req.url = "/file";
119
120 app.handle(req, res);
121
122 ASSERT_EQUAL(200, res.code);
123 }
124 {
125 request req;
126 response res;
127
128 req.url = "/file/";
129
130 app.handle(req, res);
131 ASSERT_EQUAL(404, res.code);
132 }
133 {
134 request req;
135 response res;
136
137 req.url = "/path";
138
139 app.handle(req, res);
140 ASSERT_NOTEQUAL(404, res.code);
141 }
142 {
143 request req;
144 response res;
145
146 req.url = "/path/";
147
148 app.handle(req, res);
149 ASSERT_EQUAL(200, res.code);
150 }
151}
152
153TEST(Crow, RoutingTest)
154{
155 SimpleApp app;
156 int A{};
157 uint32_t B{};
158 double C{};
159 string D{};
160 string E{};
161
162 CROW_ROUTE(app, "/0/<uint>")
163 ([&](uint32_t b){
164 B = b;
165 return "OK";
166 });
167
168 CROW_ROUTE(app, "/1/<int>/<uint>")
169 ([&](int a, uint32_t b){
170 A = a; B = b;
171 return "OK";
172 });
173
174 CROW_ROUTE(app, "/4/<int>/<uint>/<double>/<string>")
175 ([&](int a, uint32_t b, double c, string d){
176 A = a; B = b; C = c; D = d;
177 return "OK";
178 });
179
180 CROW_ROUTE(app, "/5/<int>/<uint>/<double>/<string>/<path>")
181 ([&](int a, uint32_t b, double c, string d, string e){
182 A = a; B = b; C = c; D = d; E = e;
183 return "OK";
184 });
185
186 app.validate();
187 //app.debug_print();
188 {
189 request req;
190 response res;
191
192 req.url = "/-1";
193
194 app.handle(req, res);
195
196 ASSERT_EQUAL(404, res.code);
197 }
198
199 {
200 request req;
201 response res;
202
203 req.url = "/0/1001999";
204
205 app.handle(req, res);
206
207 ASSERT_EQUAL(200, res.code);
208
209 ASSERT_EQUAL(1001999, B);
210 }
211
212 {
213 request req;
214 response res;
215
216 req.url = "/1/-100/1999";
217
218 app.handle(req, res);
219
220 ASSERT_EQUAL(200, res.code);
221
222 ASSERT_EQUAL(-100, A);
223 ASSERT_EQUAL(1999, B);
224 }
225 {
226 request req;
227 response res;
228
229 req.url = "/4/5000/3/-2.71828/hellhere";
230 req.add_header("TestHeader", "Value");
231
232 app.handle(req, res);
233
234 ASSERT_EQUAL(200, res.code);
235
236 ASSERT_EQUAL(5000, A);
237 ASSERT_EQUAL(3, B);
238 ASSERT_EQUAL(-2.71828, C);
239 ASSERT_EQUAL("hellhere", D);
240 }
241 {
242 request req;
243 response res;
244
245 req.url = "/5/-5/999/3.141592/hello_there/a/b/c/d";
246 req.add_header("TestHeader", "Value");
247
248 app.handle(req, res);
249
250 ASSERT_EQUAL(200, res.code);
251
252 ASSERT_EQUAL(-5, A);
253 ASSERT_EQUAL(999, B);
254 ASSERT_EQUAL(3.141592, C);
255 ASSERT_EQUAL("hello_there", D);
256 ASSERT_EQUAL("a/b/c/d", E);
257 }
258}
259
260TEST(Crow, simple_response_routing_params)
261{
262 ASSERT_EQUAL(100, response(100).code);
263 ASSERT_EQUAL(200, response("Hello there").code);
264 ASSERT_EQUAL(500, response(500, "Internal Error?").code);
265
266 routing_params rp;
267 rp.int_params.push_back(1);
268 rp.int_params.push_back(5);
269 rp.uint_params.push_back(2);
270 rp.double_params.push_back(3);
271 rp.string_params.push_back("hello");
272 ASSERT_EQUAL(1, rp.get<int64_t>(0));
273 ASSERT_EQUAL(5, rp.get<int64_t>(1));
274 ASSERT_EQUAL(2, rp.get<uint64_t>(0));
275 ASSERT_EQUAL(3, rp.get<double>(0));
276 ASSERT_EQUAL("hello", rp.get<string>(0));
277}
278
279TEST(Crow, handler_with_response)
280{
281 SimpleApp app;
282 CROW_ROUTE(app, "/")([](const crow::request&, crow::response&)
283 {
284 });
285}
286
287TEST(Crow, http_method)
288{
289 SimpleApp app;
290
291 CROW_ROUTE(app, "/")
292 .methods("POST"_method, "GET"_method)
293 ([](const request& req){
294 if (req.method == "GET"_method)
295 return "2";
296 else
297 return "1";
298 });
299
300 CROW_ROUTE(app, "/get_only")
301 .methods("GET"_method)
302 ([](const request& /*req*/){
303 return "get";
304 });
305 CROW_ROUTE(app, "/post_only")
306 .methods("POST"_method)
307 ([](const request& /*req*/){
308 return "post";
309 });
310
311
312 // cannot have multiple handlers for the same url
313 //CROW_ROUTE(app, "/")
314 //.methods("GET"_method)
315 //([]{ return "2"; });
316
317 {
318 request req;
319 response res;
320
321 req.url = "/";
322 app.handle(req, res);
323
324 ASSERT_EQUAL("2", res.body);
325 }
326 {
327 request req;
328 response res;
329
330 req.url = "/";
331 req.method = "POST"_method;
332 app.handle(req, res);
333
334 ASSERT_EQUAL("1", res.body);
335 }
336
337 {
338 request req;
339 response res;
340
341 req.url = "/get_only";
342 app.handle(req, res);
343
344 ASSERT_EQUAL("get", res.body);
345 }
346
347 {
348 request req;
349 response res;
350
351 req.url = "/get_only";
352 req.method = "POST"_method;
353 app.handle(req, res);
354
355 ASSERT_NOTEQUAL("get", res.body);
356 }
357
358}
359
360TEST(Crow, server_handling_error_request)
361{
362 static char buf[2048];
363 SimpleApp app;
364 CROW_ROUTE(app, "/")([]{return "A";});
365 Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451);
366 auto _ = async(launch::async, [&]{server.run();});
367 std::string sendmsg = "POX";
368 asio::io_service is;
369 {
370 asio::ip::tcp::socket c(is);
371 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
372
373
374 c.send(asio::buffer(sendmsg));
375
376 try
377 {
378 c.receive(asio::buffer(buf, 2048));
379 fail();
380 }
381 catch(std::exception& e)
382 {
383 //std::cerr << e.what() << std::endl;
384 }
385 }
386 server.stop();
387}
388
389TEST(Crow, multi_server)
390{
391 static char buf[2048];
392 SimpleApp app1, app2;
393 CROW_ROUTE(app1, "/").methods("GET"_method, "POST"_method)([]{return "A";});
394 CROW_ROUTE(app2, "/").methods("GET"_method, "POST"_method)([]{return "B";});
395
396 Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451);
397 Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452);
398
399 auto _ = async(launch::async, [&]{server1.run();});
400 auto _2 = async(launch::async, [&]{server2.run();});
401
402 std::string sendmsg = "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(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
407
408 c.send(asio::buffer(sendmsg));
409
410 size_t recved = c.receive(asio::buffer(buf, 2048));
411 ASSERT_EQUAL('A', buf[recved-1]);
412 }
413
414 {
415 asio::ip::tcp::socket c(is);
416 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45452));
417
418 for(auto ch:sendmsg)
419 {
420 char buf[1] = {ch};
421 c.send(asio::buffer(buf));
422 }
423
424 size_t recved = c.receive(asio::buffer(buf, 2048));
425 ASSERT_EQUAL('B', buf[recved-1]);
426 }
427
428 server1.stop();
429 server2.stop();
430}
431
432TEST(Crow, json_read)
433{
434 {
435 const char* json_error_tests[] =
436 {
437 "{} 3", "{{}", "{3}",
438 "3.4.5", "+3", "3-2", "00", "03", "1e3e3", "1e+.3",
439 "nll", "f", "t",
440 "{\"x\":3,}",
441 "{\"x\"}",
442 "{\"x\":3 q}",
443 "{\"x\":[3 4]}",
444 "{\"x\":[\"",
445 "{\"x\":[[], 4],\"y\",}",
446 "{\"x\":[3",
447 "{\"x\":[ null, false, true}",
448 };
449 for(auto s:json_error_tests)
450 {
451 auto x = json::load(s);
452 if (x)
453 {
454 fail("should fail to parse ", s);
455 return;
456 }
457 }
458 }
459
460 auto x = json::load(R"({"message":"hello, world"})");
461 if (!x)
462 fail("fail to parse");
463 ASSERT_EQUAL("hello, world", x["message"]);
464 ASSERT_EQUAL(1, x.size());
465 ASSERT_EQUAL(false, x.has("mess"));
466 ASSERT_THROW(x["mess"], std::exception);
467 // TODO returning false is better than exception
468 //ASSERT_THROW(3 == x["message"], std::exception);
469 ASSERT_EQUAL(12, x["message"].size());
470
471 std::string s = R"({"int":3, "ints" :[1,2,3,4,5] })";
472 auto y = json::load(s);
473 ASSERT_EQUAL(3, y["int"]);
474 ASSERT_EQUAL(3.0, y["int"]);
475 ASSERT_NOTEQUAL(3.01, y["int"]);
476 ASSERT_EQUAL(5, y["ints"].size());
477 ASSERT_EQUAL(1, y["ints"][0]);
478 ASSERT_EQUAL(2, y["ints"][1]);
479 ASSERT_EQUAL(3, y["ints"][2]);
480 ASSERT_EQUAL(4, y["ints"][3]);
481 ASSERT_EQUAL(5, y["ints"][4]);
482 ASSERT_EQUAL(1u, y["ints"][0]);
483 ASSERT_EQUAL(1.f, y["ints"][0]);
484
485 int q = (int)y["ints"][1];
486 ASSERT_EQUAL(2, q);
487 q = y["ints"][2].i();
488 ASSERT_EQUAL(3, q);
489
490 std::string s2 = R"({"bools":[true, false], "doubles":[1.2, -3.4]})";
491 auto z = json::load(s2);
492 ASSERT_EQUAL(2, z["bools"].size());
493 ASSERT_EQUAL(2, z["doubles"].size());
494 ASSERT_EQUAL(true, z["bools"][0].b());
495 ASSERT_EQUAL(false, z["bools"][1].b());
496 ASSERT_EQUAL(1.2, z["doubles"][0].d());
497 ASSERT_EQUAL(-3.4, z["doubles"][1].d());
498
499 std::string s3 = R"({"uint64": 18446744073709551615})";
500 auto z1 = json::load(s3);
501 ASSERT_EQUAL(18446744073709551615ull, z1["uint64"].u());
502}
503
504TEST(Crow, json_read_real)
505{
506 vector<std::string> v{"0.036303908355795146", "0.18320417789757412",
507 "0.05319940476190476", "0.15224702380952382", "0", "0.3296201145552561",
508 "0.47921580188679247", "0.05873511904761905", "0.1577827380952381",
509 "0.4996841307277628", "0.6425412735849056", "0.052113095238095236",
510 "0.12830357142857143", "0.7871041105121294", "0.954220013477089",
511 "0.05869047619047619", "0.1625", "0.8144794474393531",
512 "0.9721613881401617", "0.1399404761904762", "0.24470238095238095",
513 "0.04527459568733154", "0.2096950808625337", "0.35267857142857145",
514 "0.42791666666666667", "0.855731974393531", "0.9352467991913747",
515 "0.3816220238095238", "0.4282886904761905", "0.39414167789757415",
516 "0.5316079851752021", "0.3809375", "0.4571279761904762",
517 "0.03522995283018868", "0.1915641846361186", "0.6164136904761904",
518 "0.7192708333333333", "0.05675117924528302", "0.21308541105121293",
519 "0.7045386904761904", "0.8016815476190476"};
520 for(auto x:v)
521 {
522 CROW_LOG_DEBUG << x;
523 ASSERT_EQUAL(json::load(x).d(), boost::lexical_cast<double>(x));
524 }
525
526 auto ret = json::load(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"}]})---");
527 ASSERT_TRUE(ret);
528}
529
530TEST(Crow, json_read_unescaping)
531{
532 {
533 auto x = json::load(R"({"data":"\ud55c\n\t\r"})");
534 if (!x)
535 {
536 fail("fail to parse");
537 return;
538 }
539 ASSERT_EQUAL(6, x["data"].size());
540 ASSERT_EQUAL("한\n\t\r", x["data"]);
541 }
542 {
543 // multiple r_string instance
544 auto x = json::load(R"({"data":"\ud55c\n\t\r"})");
545 auto a = x["data"].s();
546 auto b = x["data"].s();
547 ASSERT_EQUAL(6, a.size());
548 ASSERT_EQUAL(6, b.size());
549 ASSERT_EQUAL(6, x["data"].size());
550 }
551}
552
553TEST(Crow, json_write)
554{
555 json::wvalue x;
556 x["message"] = "hello world";
557 ASSERT_EQUAL(R"({"message":"hello world"})", json::dump(x));
558 x["message"] = std::string("string value");
559 ASSERT_EQUAL(R"({"message":"string value"})", json::dump(x));
560 x["message"]["x"] = 3;
561 ASSERT_EQUAL(R"({"message":{"x":3}})", json::dump(x));
562 x["message"]["y"] = 5;
563 ASSERT_TRUE(R"({"message":{"x":3,"y":5}})" == json::dump(x) || R"({"message":{"y":5,"x":3}})" == json::dump(x));
564 x["message"] = 5.5;
565 ASSERT_EQUAL(R"({"message":5.5})", json::dump(x));
566
567 json::wvalue y;
568 y["scores"][0] = 1;
569 y["scores"][1] = "king";
570 y["scores"][2] = 3.5;
571 ASSERT_EQUAL(R"({"scores":[1,"king",3.5]})", json::dump(y));
572
573 y["scores"][2][0] = "real";
574 y["scores"][2][1] = false;
575 y["scores"][2][2] = true;
576 ASSERT_EQUAL(R"({"scores":[1,"king",["real",false,true]]})", json::dump(y));
577
578 y["scores"]["a"]["b"]["c"] = nullptr;
579 ASSERT_EQUAL(R"({"scores":{"a":{"b":{"c":null}}}})", json::dump(y));
580
581 y["scores"] = std::vector<int>{1,2,3};
582 ASSERT_EQUAL(R"({"scores":[1,2,3]})", json::dump(y));
583
584}
585
586TEST(Crow, template_basic)
587{
588 auto t = crow::mustache::compile(R"---(attack of {{name}})---");
589 crow::mustache::context ctx;
590 ctx["name"] = "killer tomatoes";
591 auto result = t.render(ctx);
592 ASSERT_EQUAL("attack of killer tomatoes", result);
593 //crow::mustache::load("basic.mustache");
594}
595
596TEST(Crow, template_load)
597{
598 crow::mustache::set_base(".");
599 ofstream("test.mustache") << R"---(attack of {{name}})---";
600 auto t = crow::mustache::load("test.mustache");
601 crow::mustache::context ctx;
602 ctx["name"] = "killer tomatoes";
603 auto result = t.render(ctx);
604 ASSERT_EQUAL("attack of killer tomatoes", result);
605 unlink("test.mustache");
606}
607
608TEST(Crow, black_magic)
609{
610 using namespace black_magic;
611 static_assert(std::is_same<void, last_element_type<int, char, void>::type>::value, "last_element_type");
612 static_assert(std::is_same<char, pop_back<int, char, void>::rebind<last_element_type>::type>::value, "pop_back");
613 static_assert(std::is_same<int, pop_back<int, char, void>::rebind<pop_back>::rebind<last_element_type>::type>::value, "pop_back");
614}
615
616struct NullMiddleware
617{
618 struct context {};
619
620 template <typename AllContext>
621 void before_handle(request&, response&, context&, AllContext&)
622 {}
623
624 template <typename AllContext>
625 void after_handle(request&, response&, context&, AllContext&)
626 {}
627};
628
629struct NullSimpleMiddleware
630{
631 struct context {};
632
633 void before_handle(request& /*req*/, response& /*res*/, context& /*ctx*/)
634 {}
635
636 void after_handle(request& /*req*/, response& /*res*/, context& /*ctx*/)
637 {}
638};
639
640
641TEST(Crow, middleware_simple)
642{
643 App<NullMiddleware, NullSimpleMiddleware> app;
644 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
645 CROW_ROUTE(app, "/")([&](const crow::request& req)
646 {
647 app.get_context<NullMiddleware>(req);
648 app.get_context<NullSimpleMiddleware>(req);
649 return "";
650 });
651}
652
653struct IntSettingMiddleware
654{
655 struct context { int val; };
656
657 template <typename AllContext>
658 void before_handle(request&, response&, context& ctx, AllContext& )
659 {
660 ctx.val = 1;
661 }
662
663 template <typename AllContext>
664 void after_handle(request&, response&, context& ctx, AllContext& )
665 {
666 ctx.val = 2;
667 }
668};
669
670std::vector<std::string> test_middleware_context_vector;
671
672struct FirstMW
673{
674 struct context
675 {
676 std::vector<string> v;
677 };
678
679 void before_handle(request& /*req*/, response& /*res*/, context& ctx)
680 {
681 ctx.v.push_back("1 before");
682 }
683
684 void after_handle(request& /*req*/, response& /*res*/, context& ctx)
685 {
686 ctx.v.push_back("1 after");
687 test_middleware_context_vector = ctx.v;
688 }
689};
690
691struct SecondMW
692{
693 struct context {};
694 template <typename AllContext>
695 void before_handle(request& req, response& res, context&, AllContext& all_ctx)
696 {
697 all_ctx.template get<FirstMW>().v.push_back("2 before");
698 if (req.url == "/break")
699 res.end();
700 }
701
702 template <typename AllContext>
703 void after_handle(request&, response&, context&, AllContext& all_ctx)
704 {
705 all_ctx.template get<FirstMW>().v.push_back("2 after");
706 }
707};
708
709struct ThirdMW
710{
711 struct context {};
712 template <typename AllContext>
713 void before_handle(request&, response&, context&, AllContext& all_ctx)
714 {
715 all_ctx.template get<FirstMW>().v.push_back("3 before");
716 }
717
718 template <typename AllContext>
719 void after_handle(request&, response&, context&, AllContext& all_ctx)
720 {
721 all_ctx.template get<FirstMW>().v.push_back("3 after");
722 }
723};
724
725TEST(Crow, middleware_context)
726{
727
728 static char buf[2048];
729 // SecondMW depends on FirstMW (it uses all_ctx.get<FirstMW>)
730 // so it leads to compile error if we remove FirstMW from definition
731 // App<IntSettingMiddleware, SecondMW> app;
732 // or change the order of FirstMW and SecondMW
733 // App<IntSettingMiddleware, SecondMW, FirstMW> app;
734
735 App<IntSettingMiddleware, FirstMW, SecondMW, ThirdMW> app;
736
737 int x{};
738 CROW_ROUTE(app, "/")([&](const request& req){
739 {
740 auto& ctx = app.get_context<IntSettingMiddleware>(req);
741 x = ctx.val;
742 }
743 {
744 auto& ctx = app.get_context<FirstMW>(req);
745 ctx.v.push_back("handle");
746 }
747
748 return "";
749 });
750 CROW_ROUTE(app, "/break")([&](const request& req){
751 {
752 auto& ctx = app.get_context<FirstMW>(req);
753 ctx.v.push_back("handle");
754 }
755
756 return "";
757 });
758
759 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
760 auto _ = async(launch::async, [&]{server.run();});
761 std::string sendmsg = "GET /\r\n\r\n";
762 asio::io_service is;
763 {
764 asio::ip::tcp::socket c(is);
765 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
766
767
768 c.send(asio::buffer(sendmsg));
769
770 c.receive(asio::buffer(buf, 2048));
771 c.close();
772 }
773 {
774 auto& out = test_middleware_context_vector;
775 ASSERT_EQUAL(1, x);
776 ASSERT_EQUAL(7, out.size());
777 ASSERT_EQUAL("1 before", out[0]);
778 ASSERT_EQUAL("2 before", out[1]);
779 ASSERT_EQUAL("3 before", out[2]);
780 ASSERT_EQUAL("handle", out[3]);
781 ASSERT_EQUAL("3 after", out[4]);
782 ASSERT_EQUAL("2 after", out[5]);
783 ASSERT_EQUAL("1 after", out[6]);
784 }
785 std::string sendmsg2 = "GET /break\r\n\r\n";
786 {
787 asio::ip::tcp::socket c(is);
788 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
789
790 c.send(asio::buffer(sendmsg2));
791
792 c.receive(asio::buffer(buf, 2048));
793 c.close();
794 }
795 {
796 auto& out = test_middleware_context_vector;
797 ASSERT_EQUAL(4, out.size());
798 ASSERT_EQUAL("1 before", out[0]);
799 ASSERT_EQUAL("2 before", out[1]);
800 ASSERT_EQUAL("2 after", out[2]);
801 ASSERT_EQUAL("1 after", out[3]);
802 }
803 server.stop();
804}
805
806TEST(Crow, middleware_cookieparser)
807{
808 static char buf[2048];
809
810 App<CookieParser> app;
811
812 std::string value1;
813 std::string value2;
814
815 CROW_ROUTE(app, "/")([&](const request& req){
816 {
817 auto& ctx = app.get_context<CookieParser>(req);
818 value1 = ctx.get_cookie("key1");
819 value2 = ctx.get_cookie("key2");
820 }
821
822 return "";
823 });
824
825 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
826 auto _ = async(launch::async, [&]{server.run();});
827 std::string sendmsg = "GET /\r\nCookie: key1=value1; key2=\"val\\\"ue2\"\r\n\r\n";
828 asio::io_service is;
829 {
830 asio::ip::tcp::socket c(is);
831 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
832
833 c.send(asio::buffer(sendmsg));
834
835 c.receive(asio::buffer(buf, 2048));
836 c.close();
837 }
838 {
839 ASSERT_EQUAL("value1", value1);
840 ASSERT_EQUAL("val\"ue2", value2);
841 }
842 server.stop();
843}
844
845TEST(Crow, bug_quick_repeated_request)
846{
847 static char buf[2048];
848
849 SimpleApp app;
850
851 CROW_ROUTE(app, "/")([&]{
852 return "hello";
853 });
854
855 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
856 auto _ = async(launch::async, [&]{server.run();});
857 std::string sendmsg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
858 asio::io_service is;
859 {
860 std::vector<std::future<void>> v;
861 for(int i = 0; i < 5; i++)
862 {
863 v.push_back(async(launch::async,
864 [&]
865 {
866 asio::ip::tcp::socket c(is);
867 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
868
869 for(int j = 0; j < 5; j ++)
870 {
871 c.send(asio::buffer(sendmsg));
872
873 size_t received = c.receive(asio::buffer(buf, 2048));
874 ASSERT_EQUAL("hello", std::string(buf + received - 5, buf + received));
875 }
876 c.close();
877 }));
878 }
879 }
880 server.stop();
881}
882
883TEST(Crow, simple_url_params)
884{
885 static char buf[2048];
886
887 SimpleApp app;
888
889 query_string last_url_params;
890
891 CROW_ROUTE(app, "/params")
892 ([&last_url_params](const crow::request& req){
893 last_url_params = std::move(req.url_params);
894 return "OK";
895 });
896
897 ///params?h=1&foo=bar&lol&count[]=1&count[]=4&pew=5.2
898
899 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
900 auto _ = async(launch::async, [&]{server.run();});
901 asio::io_service is;
902 std::string sendmsg;
903
904 // check empty params
905 sendmsg = "GET /params\r\n\r\n";
906 {
907 asio::ip::tcp::socket c(is);
908 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
909 c.send(asio::buffer(sendmsg));
910 c.receive(asio::buffer(buf, 2048));
911 c.close();
912
913 stringstream ss;
914 ss << last_url_params;
915
916 ASSERT_EQUAL("[ ]", ss.str());
917 }
918 // check single presence
919 sendmsg = "GET /params?foobar\r\n\r\n";
920 {
921 asio::ip::tcp::socket c(is);
922 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
923 c.send(asio::buffer(sendmsg));
924 c.receive(asio::buffer(buf, 2048));
925 c.close();
926
927 ASSERT_TRUE(last_url_params.get("missing") == nullptr);
928 ASSERT_TRUE(last_url_params.get("foobar") != nullptr);
929 ASSERT_TRUE(last_url_params.get_list("missing").empty());
930 }
931 // check multiple presence
932 sendmsg = "GET /params?foo&bar&baz\r\n\r\n";
933 {
934 asio::ip::tcp::socket c(is);
935 c.connect(asio::ip::tcp::endpoint(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("foo") != nullptr);
942 ASSERT_TRUE(last_url_params.get("bar") != nullptr);
943 ASSERT_TRUE(last_url_params.get("baz") != nullptr);
944 }
945 // check single value
946 sendmsg = "GET /params?hello=world\r\n\r\n";
947 {
948 asio::ip::tcp::socket c(is);
949 c.connect(asio::ip::tcp::endpoint(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_EQUAL(string(last_url_params.get("hello")), "world");
955 }
956 // check multiple value
957 sendmsg = "GET /params?hello=world&left=right&up=down\r\n\r\n";
958 {
959 asio::ip::tcp::socket c(is);
960 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
961 c.send(asio::buffer(sendmsg));
962 c.receive(asio::buffer(buf, 2048));
963 c.close();
964
965 ASSERT_EQUAL(string(last_url_params.get("hello")), "world");
966 ASSERT_EQUAL(string(last_url_params.get("left")), "right");
967 ASSERT_EQUAL(string(last_url_params.get("up")), "down");
968 }
969 // check multiple value, multiple types
970 sendmsg = "GET /params?int=100&double=123.45&boolean=1\r\n\r\n";
971 {
972 asio::ip::tcp::socket c(is);
973 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
974 c.send(asio::buffer(sendmsg));
975 c.receive(asio::buffer(buf, 2048));
976 c.close();
977
978 ASSERT_EQUAL(boost::lexical_cast<int>(last_url_params.get("int")), 100);
979 ASSERT_EQUAL(boost::lexical_cast<double>(last_url_params.get("double")), 123.45);
980 ASSERT_EQUAL(boost::lexical_cast<bool>(last_url_params.get("boolean")), true);
981 }
982 // check single array value
983 sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n";
984 {
985 asio::ip::tcp::socket c(is);
986
987 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
988 c.send(asio::buffer(sendmsg));
989 c.receive(asio::buffer(buf, 2048));
990 c.close();
991
992 ASSERT_TRUE(last_url_params.get("tmnt") == nullptr);
993 ASSERT_EQUAL(last_url_params.get_list("tmnt").size(), 1);
994 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[0]), "leonardo");
995 }
996 // check multiple array value
997 sendmsg = "GET /params?tmnt[]=leonardo&tmnt[]=donatello&tmnt[]=raphael\r\n\r\n";
998 {
999 asio::ip::tcp::socket c(is);
1000
1001 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
1002 c.send(asio::buffer(sendmsg));
1003 c.receive(asio::buffer(buf, 2048));
1004 c.close();
1005
1006 ASSERT_EQUAL(last_url_params.get_list("tmnt").size(), 3);
1007 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[0]), "leonardo");
1008 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[1]), "donatello");
1009 ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[2]), "raphael");
1010 }
1011 server.stop();
1012}
1013
1014TEST(Crow, route_dynamic)
1015{
1016 SimpleApp app;
1017 int x = 1;
1018 app.route_dynamic("/")
1019 ([&]{
1020 x = 2;
1021 return "";
1022 });
1023
1024 app.route_dynamic("/set4")
1025 ([&](const request&){
1026 x = 4;
1027 return "";
1028 });
1029 app.route_dynamic("/set5")
1030 ([&](const request&, response& res){
1031 x = 5;
1032 res.end();
1033 });
1034
1035 app.route_dynamic("/set_int/<int>")
1036 ([&](int y){
1037 x = y;
1038 return "";
1039 });
1040
1041 try
1042 {
1043 app.route_dynamic("/invalid_test/<double>/<path>")
1044 ([](){
1045 return "";
1046 });
1047 fail();
1048 }
1049 catch(std::exception&)
1050 {
1051 }
1052
1053 // app is in an invalid state when route_dynamic throws an exception.
1054 try
1055 {
1056 app.validate();
1057 fail();
1058 }
1059 catch(std::exception&)
1060 {
1061 }
1062
1063 {
1064 request req;
1065 response res;
1066 req.url = "/";
1067 app.handle(req, res);
1068 ASSERT_EQUAL(x, 2);
1069 }
1070 {
1071 request req;
1072 response res;
1073 req.url = "/set_int/42";
1074 app.handle(req, res);
1075 ASSERT_EQUAL(x, 42);
1076 }
1077 {
1078 request req;
1079 response res;
1080 req.url = "/set5";
1081 app.handle(req, res);
1082 ASSERT_EQUAL(x, 5);
1083 }
1084 {
1085 request req;
1086 response res;
1087 req.url = "/set4";
1088 app.handle(req, res);
1089 ASSERT_EQUAL(x, 4);
1090 }
1091}