blob: f53e519fb62ffb53ae0367632a5be874b7cc358b [file] [log] [blame]
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001#include "crow.h"
2
Ed Tanous8041f312017-04-03 09:47:01 -07003#include <iostream>
4#include <sstream>
5#include <vector>
Ed Tanous1abe55e2018-09-05 08:30:59 -07006
Ed Tanous8041f312017-04-03 09:47:01 -07007#include "gtest/gtest.h"
Ed Tanous8041f312017-04-03 09:47:01 -07008
9using namespace std;
10using namespace crow;
11
12bool failed__ = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -070013void error_print()
14{
15 cerr << endl;
16}
Ed Tanous1ff48782017-04-18 12:45:08 -070017
18template <typename A, typename... Args>
Ed Tanous1abe55e2018-09-05 08:30:59 -070019void error_print(const A& a, Args... args)
20{
21 cerr << a;
22 error_print(args...);
Ed Tanous8041f312017-04-03 09:47:01 -070023}
24
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025template <typename... Args>
26void fail(Args... args)
Ed Tanous1abe55e2018-09-05 08:30:59 -070027{
28 error_print(args...);
29 failed__ = true;
Ed Tanous8041f312017-04-03 09:47:01 -070030}
31
Ed Tanous1abe55e2018-09-05 08:30:59 -070032#define ASSERT_EQUAL(a, b) \
33 if ((a) != (b)) \
34 fail(__FILE__ ":", __LINE__, ": Assert fail: expected ", (a), " actual ", \
35 (b), ", " #a " == " #b ", at " __FILE__ ":", __LINE__)
36#define ASSERT_NOTEQUAL(a, b) \
37 if ((a) == (b)) \
38 fail(__FILE__ ":", __LINE__, ": Assert fail: not expected ", (a), \
39 ", " #a " != " #b ", at " __FILE__ ":", __LINE__)
Ed Tanous8041f312017-04-03 09:47:01 -070040
Ed Tanous1abe55e2018-09-05 08:30:59 -070041#define DISABLE_TEST(x) \
42 struct test##x \
43 { \
44 void test(); \
45 } x##_; \
46 void test##x::test()
Ed Tanous8041f312017-04-03 09:47:01 -070047
48#define LOCALHOST_ADDRESS "127.0.0.1"
49
Ed Tanous1abe55e2018-09-05 08:30:59 -070050TEST(Crow, Rule)
51{
52 TaggedRule<> r("/http/");
53 r.name("abc");
Ed Tanous8041f312017-04-03 09:47:01 -070054
Ed Tanous1abe55e2018-09-05 08:30:59 -070055 // empty handler - fail to validate
56 try
57 {
58 r.validate();
59 fail("empty handler should fail to validate");
60 }
61 catch (runtime_error& e)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050062 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -070063
64 int x = 0;
65
66 // registering handler
67 r([&x] {
68 x = 1;
69 return "";
70 });
71
Ed Tanous8041f312017-04-03 09:47:01 -070072 r.validate();
73
Ed Tanous55c7b7a2018-05-22 15:27:24 -070074 Response res;
Ed Tanous8041f312017-04-03 09:47:01 -070075
Ed Tanous1abe55e2018-09-05 08:30:59 -070076 // executing handler
77 ASSERT_EQUAL(0, x);
78 boost::beast::http::request<boost::beast::http::string_body> req{};
79 r.handle(Request(req), res, RoutingParams());
Ed Tanous1ff48782017-04-18 12:45:08 -070080 ASSERT_EQUAL(1, x);
Ed Tanous1ff48782017-04-18 12:45:08 -070081
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 // registering handler with Request argument
83 r([&x](const crow::Request&) {
84 x = 2;
85 return "";
86 });
Ed Tanous1ff48782017-04-18 12:45:08 -070087
Ed Tanous1abe55e2018-09-05 08:30:59 -070088 r.validate();
89
90 // executing handler
91 ASSERT_EQUAL(1, x);
92 r.handle(Request(req), res, RoutingParams());
93 ASSERT_EQUAL(2, x);
Ed Tanous8041f312017-04-03 09:47:01 -070094}
95
Ed Tanous1abe55e2018-09-05 08:30:59 -070096TEST(Crow, ParameterTagging)
97{
98 static_assert(black_magic::isValid("<int><int><int>"), "valid url");
99 static_assert(!black_magic::isValid("<int><int<<int>"), "invalid url");
100 static_assert(!black_magic::isValid("nt>"), "invalid url");
101 ASSERT_EQUAL(1, black_magic::get_parameter_tag("<int>"));
102 ASSERT_EQUAL(2, black_magic::get_parameter_tag("<uint>"));
103 ASSERT_EQUAL(3, black_magic::get_parameter_tag("<float>"));
104 ASSERT_EQUAL(3, black_magic::get_parameter_tag("<double>"));
105 ASSERT_EQUAL(4, black_magic::get_parameter_tag("<str>"));
106 ASSERT_EQUAL(4, black_magic::get_parameter_tag("<string>"));
107 ASSERT_EQUAL(5, black_magic::get_parameter_tag("<path>"));
108 ASSERT_EQUAL(6 * 6 + 6 + 1,
109 black_magic::get_parameter_tag("<int><int><int>"));
110 ASSERT_EQUAL(6 * 6 + 6 + 2,
111 black_magic::get_parameter_tag("<uint><int><int>"));
112 ASSERT_EQUAL(6 * 6 + 6 * 3 + 2,
113 black_magic::get_parameter_tag("<uint><double><int>"));
Ed Tanous1ff48782017-04-18 12:45:08 -0700114
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 // url definition parsed in compile time, build into *one number*, and given
116 // to template argument
117 static_assert(
118 std::is_same<black_magic::S<uint64_t, double, int64_t>,
119 black_magic::Arguments<6 * 6 + 6 * 3 + 2>::type>::value,
120 "tag to type container");
121}
Ed Tanous1ff48782017-04-18 12:45:08 -0700122
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123TEST(Crow, PathRouting)
124{
125 SimpleApp app;
Ed Tanous1ff48782017-04-18 12:45:08 -0700126
Ed Tanous1abe55e2018-09-05 08:30:59 -0700127 BMCWEB_ROUTE(app, "/file")
128 ([] { return "file"; });
129
130 BMCWEB_ROUTE(app, "/path/")
131 ([] { return "path"; });
132
133 {
134 boost::beast::http::request<boost::beast::http::string_body> r{};
135 Request req{r};
136 Response res;
137
138 req.url = "/file";
139
140 app.handle(req, res);
141
142 ASSERT_EQUAL(200, res.resultInt());
143 }
144 {
145 boost::beast::http::request<boost::beast::http::string_body> r{};
146 Request req{r};
147 Response res;
148
149 req.url = "/file/";
150
151 app.handle(req, res);
152 ASSERT_EQUAL(404, res.resultInt());
153 }
154 {
155 boost::beast::http::request<boost::beast::http::string_body> r{};
156 Request req{r};
157 Response res;
158
159 req.url = "/path";
160
161 app.handle(req, res);
162 ASSERT_NOTEQUAL(404, res.resultInt());
163 }
164 {
165 boost::beast::http::request<boost::beast::http::string_body> r{};
166 Request req{r};
167 Response res;
168
169 req.url = "/path/";
170
171 app.handle(req, res);
172 ASSERT_EQUAL(200, res.resultInt());
173 }
174}
175
176TEST(Crow, RoutingTest)
177{
178 SimpleApp app;
179 int A{};
180 uint32_t b{};
181 double C{};
182 string D{};
183 string E{};
184
185 BMCWEB_ROUTE(app, "/0/<uint>")
186 ([&](uint32_t b) {
187 b = b;
188 return "OK";
189 });
190
191 BMCWEB_ROUTE(app, "/1/<int>/<uint>")
192 ([&](int a, uint32_t b) {
193 A = a;
194 b = b;
195 return "OK";
196 });
197
198 BMCWEB_ROUTE(app, "/4/<int>/<uint>/<double>/<string>")
199 ([&](int a, uint32_t b, double c, string d) {
200 A = a;
201 b = b;
202 C = c;
203 D = d;
204 return "OK";
205 });
206
207 BMCWEB_ROUTE(app, "/5/<int>/<uint>/<double>/<string>/<path>")
208 ([&](int a, uint32_t b, double c, string d, string e) {
209 A = a;
210 b = b;
211 C = c;
212 D = d;
213 E = e;
214 return "OK";
215 });
216
217 app.validate();
218 // app.debugPrint();
219 {
220 boost::beast::http::request<boost::beast::http::string_body> r{};
221 Request req{r};
222 Response res;
223
224 req.url = "/-1";
225
226 app.handle(req, res);
227
228 ASSERT_EQUAL(404, res.resultInt());
229 }
230
231 {
232 boost::beast::http::request<boost::beast::http::string_body> r{};
233 Request req{r};
234 Response res;
235
236 req.url = "/0/1001999";
237
238 app.handle(req, res);
239
240 ASSERT_EQUAL(200, res.resultInt());
241
242 ASSERT_EQUAL(1001999, b);
243 }
244
245 {
246 boost::beast::http::request<boost::beast::http::string_body> r{};
247 Request req{r};
248 Response res;
249
250 req.url = "/1/-100/1999";
251
252 app.handle(req, res);
253
254 ASSERT_EQUAL(200, res.resultInt());
255
256 ASSERT_EQUAL(-100, A);
257 ASSERT_EQUAL(1999, b);
258 }
259 {
260 boost::beast::http::request<boost::beast::http::string_body> r{};
261 Request req{r};
262 Response res;
263
264 req.url = "/4/5000/3/-2.71828/hellhere";
265
266 app.handle(req, res);
267
268 ASSERT_EQUAL(200, res.resultInt());
269
270 ASSERT_EQUAL(5000, A);
271 ASSERT_EQUAL(3, b);
272 ASSERT_EQUAL(-2.71828, C);
273 ASSERT_EQUAL("hellhere", D);
274 }
275 {
276 boost::beast::http::request<boost::beast::http::string_body> r{};
277 Request req{r};
278 Response res;
279
280 req.url = "/5/-5/999/3.141592/hello_there/a/b/c/d";
281
282 app.handle(req, res);
283
284 ASSERT_EQUAL(200, res.resultInt());
285
286 ASSERT_EQUAL(-5, A);
287 ASSERT_EQUAL(999, b);
288 ASSERT_EQUAL(3.141592, C);
289 ASSERT_EQUAL("hello_there", D);
290 ASSERT_EQUAL("a/b/c/d", E);
291 }
292}
293
294TEST(Crow, simple_response_RoutingParams)
295{
296 ASSERT_EQUAL(100,
297 Response(boost::beast::http::status::continue_).resultInt());
298 ASSERT_EQUAL(200, Response("Hello there").resultInt());
299 ASSERT_EQUAL(500,
300 Response(boost::beast::http::status::internal_server_error,
301 "Internal Error?")
302 .resultInt());
303
304 RoutingParams rp;
305 rp.intParams.push_back(1);
306 rp.intParams.push_back(5);
307 rp.uintParams.push_back(2);
308 rp.doubleParams.push_back(3);
309 rp.stringParams.push_back("hello");
310 ASSERT_EQUAL(1, rp.get<int64_t>(0));
311 ASSERT_EQUAL(5, rp.get<int64_t>(1));
312 ASSERT_EQUAL(2, rp.get<uint64_t>(0));
313 ASSERT_EQUAL(3, rp.get<double>(0));
314 ASSERT_EQUAL("hello", rp.get<string>(0));
315}
316
317TEST(Crow, handler_with_response)
318{
319 SimpleApp app;
320 BMCWEB_ROUTE(app, "/")([](const crow::Request&, crow::Response&) {});
321}
322
323TEST(Crow, http_method)
324{
325 SimpleApp app;
326
327 BMCWEB_ROUTE(app, "/").methods("POST"_method,
328 "GET"_method)([](const Request& req) {
329 if (req.method() == "GET"_method)
330 return "2";
331 else
332 return "1";
333 });
334
335 BMCWEB_ROUTE(app, "/get_only")
336 .methods("GET"_method)([](const Request& /*req*/) { return "get"; });
337 BMCWEB_ROUTE(app, "/post_only")
338 .methods("POST"_method)([](const Request& /*req*/) { return "post"; });
339
340 // cannot have multiple handlers for the same url
341 // BMCWEB_ROUTE(app, "/")
342 //.methods("GET"_method)
343 //([]{ return "2"; });
344
345 {
346 boost::beast::http::request<boost::beast::http::string_body> r{};
347 Request req{r};
348 Response res;
349
350 req.url = "/";
351 app.handle(req, res);
352
353 ASSERT_EQUAL("2", res.body());
354 }
355 {
356 boost::beast::http::request<boost::beast::http::string_body> r{};
357 Request req{r};
358 Response res;
359
360 req.url = "/";
361 r.method("POST"_method);
362 app.handle(req, res);
363
364 ASSERT_EQUAL("1", res.body());
365 }
366
367 {
368 boost::beast::http::request<boost::beast::http::string_body> r{};
369 Request req{r};
370 Response res;
371
372 req.url = "/get_only";
373 app.handle(req, res);
374
375 ASSERT_EQUAL("get", res.body());
376 }
377
378 {
379 boost::beast::http::request<boost::beast::http::string_body> r{};
380 Request req{r};
381 Response res;
382
383 req.url = "/get_only";
384 r.method("POST"_method);
385 app.handle(req, res);
386
387 ASSERT_NOTEQUAL("get", res.body());
388 }
389}
390
391TEST(Crow, server_handling_error_request)
392{
393 static char buf[2048];
394 SimpleApp app;
395 BMCWEB_ROUTE(app, "/")([] { return "A"; });
396 Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451);
397 auto _ = async(launch::async, [&] { server.run(); });
398 std::string sendmsg = "POX";
Ed Tanous8f626352018-12-19 14:51:54 -0800399 asio::io_context is;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700400 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700401 asio::ip::tcp::socket c(is);
402 c.connect(asio::ip::tcp::endpoint(
403 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
404
Ed Tanous1abe55e2018-09-05 08:30:59 -0700405 c.send(asio::buffer(sendmsg));
Ed Tanous1ff48782017-04-18 12:45:08 -0700406
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 try
408 {
409 c.receive(asio::buffer(buf, 2048));
410 fail();
Ed Tanous8041f312017-04-03 09:47:01 -0700411 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700412 catch (std::exception& e)
413 {
414 // std::cerr << e.what() << std::endl;
415 }
Ed Tanous8041f312017-04-03 09:47:01 -0700416 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700417 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700418}
419
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420TEST(Crow, multi_server)
421{
422 static char buf[2048];
423 SimpleApp app1, app2;
424 BMCWEB_ROUTE(app1, "/").methods("GET"_method,
425 "POST"_method)([] { return "A"; });
426 BMCWEB_ROUTE(app2, "/").methods("GET"_method,
427 "POST"_method)([] { return "B"; });
Ed Tanous8041f312017-04-03 09:47:01 -0700428
Ed Tanous1abe55e2018-09-05 08:30:59 -0700429 Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451);
430 Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452);
Ed Tanous8041f312017-04-03 09:47:01 -0700431
Ed Tanous1abe55e2018-09-05 08:30:59 -0700432 auto _ = async(launch::async, [&] { server1.run(); });
433 auto _2 = async(launch::async, [&] { server2.run(); });
Ed Tanous8041f312017-04-03 09:47:01 -0700434
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435 std::string sendmsg =
436 "POST /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=b\r\n";
Ed Tanous8f626352018-12-19 14:51:54 -0800437 asio::io_context is;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 {
439 asio::ip::tcp::socket c(is);
440 c.connect(asio::ip::tcp::endpoint(
441 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
Ed Tanous8041f312017-04-03 09:47:01 -0700442
Ed Tanous1abe55e2018-09-05 08:30:59 -0700443 c.send(asio::buffer(sendmsg));
Ed Tanous8041f312017-04-03 09:47:01 -0700444
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 size_t recved = c.receive(asio::buffer(buf, 2048));
446 ASSERT_EQUAL('A', buf[recved - 1]);
447 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700448
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 {
450 asio::ip::tcp::socket c(is);
451 c.connect(asio::ip::tcp::endpoint(
452 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45452));
Ed Tanous1ff48782017-04-18 12:45:08 -0700453
Ed Tanous1abe55e2018-09-05 08:30:59 -0700454 for (auto ch : sendmsg)
455 {
456 char buf[1] = {ch};
457 c.send(asio::buffer(buf));
458 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700459
Ed Tanous1abe55e2018-09-05 08:30:59 -0700460 size_t recved = c.receive(asio::buffer(buf, 2048));
461 ASSERT_EQUAL('b', buf[recved - 1]);
462 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700463
Ed Tanous1abe55e2018-09-05 08:30:59 -0700464 server1.stop();
465 server2.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700466}
467
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468TEST(Crow, black_magic)
469{
470 using namespace black_magic;
471 static_assert(
472 std::is_same<void, LastElementType<int, char, void>::type>::value,
473 "LastElementType");
474 static_assert(
475 std::is_same<char, PopBack<int, char,
476 void>::rebind<LastElementType>::type>::value,
477 "pop_back");
478 static_assert(
479 std::is_same<int, PopBack<int, char, void>::rebind<PopBack>::rebind<
480 LastElementType>::type>::value,
481 "pop_back");
482}
Ed Tanous8041f312017-04-03 09:47:01 -0700483
Ed Tanous1abe55e2018-09-05 08:30:59 -0700484struct NullMiddleware
485{
486 struct Context
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500487 {};
Ed Tanous8041f312017-04-03 09:47:01 -0700488
Ed Tanous1abe55e2018-09-05 08:30:59 -0700489 template <typename AllContext>
490 void beforeHandle(Request&, Response&, Context&, AllContext&)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500491 {}
Ed Tanous8041f312017-04-03 09:47:01 -0700492
Ed Tanous1abe55e2018-09-05 08:30:59 -0700493 template <typename AllContext>
494 void afterHandle(Request&, Response&, Context&, AllContext&)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500495 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700496};
Ed Tanous8041f312017-04-03 09:47:01 -0700497
Ed Tanous1abe55e2018-09-05 08:30:59 -0700498struct NullSimpleMiddleware
499{
500 struct Context
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500501 {};
Ed Tanous8041f312017-04-03 09:47:01 -0700502
Ed Tanous1abe55e2018-09-05 08:30:59 -0700503 void beforeHandle(Request& /*req*/, Response& /*res*/, Context& /*ctx*/)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500504 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700505
506 void afterHandle(Request& /*req*/, Response& /*res*/, Context& /*ctx*/)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500507 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700508};
509
510TEST(Crow, middleware_simple)
511{
512 App<NullMiddleware, NullSimpleMiddleware> app;
513 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
514 BMCWEB_ROUTE(app, "/")
515 ([&](const crow::Request& req) {
516 app.getContext<NullMiddleware>(req);
517 app.getContext<NullSimpleMiddleware>(req);
518 return "";
519 });
520}
521
522struct IntSettingMiddleware
523{
524 struct Context
525 {
526 int val;
527 };
528
529 template <typename AllContext>
530 void beforeHandle(Request&, Response&, Context& ctx, AllContext&)
531 {
532 ctx.val = 1;
533 }
534
535 template <typename AllContext>
536 void afterHandle(Request&, Response&, Context& ctx, AllContext&)
537 {
538 ctx.val = 2;
539 }
540};
541
542std::vector<std::string> test_middleware_context_vector;
543
544struct FirstMW
545{
546 struct Context
547 {
548 std::vector<string> v;
549 };
550
551 void beforeHandle(Request& /*req*/, Response& /*res*/, Context& ctx)
552 {
553 ctx.v.push_back("1 before");
554 }
555
556 void afterHandle(Request& /*req*/, Response& /*res*/, Context& ctx)
557 {
558 ctx.v.push_back("1 after");
559 test_middleware_context_vector = ctx.v;
560 }
561};
562
563struct SecondMW
564{
565 struct Context
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500566 {};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700567 template <typename AllContext>
568 void beforeHandle(Request& req, Response& res, Context&,
569 AllContext& all_ctx)
570 {
571 all_ctx.template get<FirstMW>().v.push_back("2 before");
572 if (req.url == "/break")
573 res.end();
574 }
575
576 template <typename AllContext>
577 void afterHandle(Request&, Response&, Context&, AllContext& all_ctx)
578 {
579 all_ctx.template get<FirstMW>().v.push_back("2 after");
580 }
581};
582
583struct ThirdMW
584{
585 struct Context
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500586 {};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 template <typename AllContext>
588 void beforeHandle(Request&, Response&, Context&, AllContext& all_ctx)
589 {
590 all_ctx.template get<FirstMW>().v.push_back("3 before");
591 }
592
593 template <typename AllContext>
594 void afterHandle(Request&, Response&, Context&, AllContext& all_ctx)
595 {
596 all_ctx.template get<FirstMW>().v.push_back("3 after");
597 }
598};
599
600TEST(Crow, middlewareContext)
601{
602 static char buf[2048];
603 // SecondMW depends on FirstMW (it uses all_ctx.get<FirstMW>)
604 // so it leads to compile error if we remove FirstMW from definition
605 // App<IntSettingMiddleware, SecondMW> app;
606 // or change the order of FirstMW and SecondMW
607 // App<IntSettingMiddleware, SecondMW, FirstMW> app;
608
609 App<IntSettingMiddleware, FirstMW, SecondMW, ThirdMW> app;
610
611 int x{};
612 BMCWEB_ROUTE(app, "/")
613 ([&](const Request& req) {
614 {
615 auto& ctx = app.getContext<IntSettingMiddleware>(req);
616 x = ctx.val;
617 }
618 {
619 auto& ctx = app.getContext<FirstMW>(req);
620 ctx.v.push_back("handle");
621 }
622
623 return "";
624 });
625 BMCWEB_ROUTE(app, "/break")
626 ([&](const Request& req) {
627 {
628 auto& ctx = app.getContext<FirstMW>(req);
629 ctx.v.push_back("handle");
630 }
631
632 return "";
633 });
634
635 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
636 auto _ = async(launch::async, [&] { server.run(); });
637 std::string sendmsg = "GET /\r\n\r\n";
Ed Tanous8f626352018-12-19 14:51:54 -0800638 asio::io_context is;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 {
640 asio::ip::tcp::socket c(is);
641 c.connect(asio::ip::tcp::endpoint(
642 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
643
644 c.send(asio::buffer(sendmsg));
645
646 c.receive(asio::buffer(buf, 2048));
647 c.close();
648 }
649 {
650 auto& out = test_middleware_context_vector;
651 ASSERT_EQUAL(1, x);
652 ASSERT_EQUAL(7, out.size());
653 ASSERT_EQUAL("1 before", out[0]);
654 ASSERT_EQUAL("2 before", out[1]);
655 ASSERT_EQUAL("3 before", out[2]);
656 ASSERT_EQUAL("handle", out[3]);
657 ASSERT_EQUAL("3 after", out[4]);
658 ASSERT_EQUAL("2 after", out[5]);
659 ASSERT_EQUAL("1 after", out[6]);
660 }
661 std::string sendmsg2 = "GET /break\r\n\r\n";
662 {
663 asio::ip::tcp::socket c(is);
664 c.connect(asio::ip::tcp::endpoint(
665 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
666
667 c.send(asio::buffer(sendmsg2));
668
669 c.receive(asio::buffer(buf, 2048));
670 c.close();
671 }
672 {
673 auto& out = test_middleware_context_vector;
674 ASSERT_EQUAL(4, out.size());
675 ASSERT_EQUAL("1 before", out[0]);
676 ASSERT_EQUAL("2 before", out[1]);
677 ASSERT_EQUAL("2 after", out[2]);
678 ASSERT_EQUAL("1 after", out[3]);
679 }
680 server.stop();
681}
682
683TEST(Crow, bug_quick_repeated_request)
684{
685 static char buf[2048];
686
687 SimpleApp app;
688
689 BMCWEB_ROUTE(app, "/")([&] { return "hello"; });
690
691 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
692 auto _ = async(launch::async, [&] { server.run(); });
693 std::string sendmsg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
Ed Tanous8f626352018-12-19 14:51:54 -0800694 asio::io_context is;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700695 {
696 std::vector<std::future<void>> v;
697 for (int i = 0; i < 5; i++)
698 {
699 v.push_back(async(launch::async, [&] {
700 asio::ip::tcp::socket c(is);
701 c.connect(asio::ip::tcp::endpoint(
702 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
703
704 for (int j = 0; j < 5; j++)
705 {
706 c.send(asio::buffer(sendmsg));
707
708 size_t received = c.receive(asio::buffer(buf, 2048));
709 ASSERT_EQUAL("hello", std::string(buf + received - 5,
710 buf + received));
711 }
712 c.close();
713 }));
714 }
715 }
716 server.stop();
717}
718
719TEST(Crow, simple_url_params)
720{
721 static char buf[2048];
722
723 SimpleApp app;
724
725 QueryString lastUrlParams;
726
727 BMCWEB_ROUTE(app, "/params")
728 ([&lastUrlParams](const crow::Request& req) {
729 lastUrlParams = std::move(req.urlParams);
730 return "OK";
731 });
732
733 /// params?h=1&foo=bar&lol&count[]=1&count[]=4&pew=5.2
734
735 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
736 auto _ = async(launch::async, [&] { server.run(); });
Ed Tanous8f626352018-12-19 14:51:54 -0800737 asio::io_context is;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700738 std::string sendmsg;
739
740 // check empty params
741 sendmsg = "GET /params\r\n\r\n";
742 {
743 asio::ip::tcp::socket c(is);
744 c.connect(asio::ip::tcp::endpoint(
745 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
746 c.send(asio::buffer(sendmsg));
747 c.receive(asio::buffer(buf, 2048));
748 c.close();
749
750 stringstream ss;
751 ss << lastUrlParams;
752
753 ASSERT_EQUAL("[ ]", ss.str());
754 }
755 // check single presence
756 sendmsg = "GET /params?foobar\r\n\r\n";
757 {
758 asio::ip::tcp::socket c(is);
759 c.connect(asio::ip::tcp::endpoint(
760 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
761 c.send(asio::buffer(sendmsg));
762 c.receive(asio::buffer(buf, 2048));
763 c.close();
764
765 ASSERT_TRUE(lastUrlParams.get("missing") == nullptr);
766 ASSERT_TRUE(lastUrlParams.get("foobar") != nullptr);
767 ASSERT_TRUE(lastUrlParams.getList("missing").empty());
768 }
769 // check multiple presence
770 sendmsg = "GET /params?foo&bar&baz\r\n\r\n";
771 {
772 asio::ip::tcp::socket c(is);
773 c.connect(asio::ip::tcp::endpoint(
774 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
775 c.send(asio::buffer(sendmsg));
776 c.receive(asio::buffer(buf, 2048));
777 c.close();
778
779 ASSERT_TRUE(lastUrlParams.get("missing") == nullptr);
780 ASSERT_TRUE(lastUrlParams.get("foo") != nullptr);
781 ASSERT_TRUE(lastUrlParams.get("bar") != nullptr);
782 ASSERT_TRUE(lastUrlParams.get("baz") != nullptr);
783 }
784 // check single value
785 sendmsg = "GET /params?hello=world\r\n\r\n";
786 {
787 asio::ip::tcp::socket c(is);
788 c.connect(asio::ip::tcp::endpoint(
789 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
790 c.send(asio::buffer(sendmsg));
791 c.receive(asio::buffer(buf, 2048));
792 c.close();
793
794 ASSERT_EQUAL(string(lastUrlParams.get("hello")), "world");
795 }
796 // check multiple value
797 sendmsg = "GET /params?hello=world&left=right&up=down\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 c.send(asio::buffer(sendmsg));
803 c.receive(asio::buffer(buf, 2048));
804 c.close();
805
806 ASSERT_EQUAL(string(lastUrlParams.get("hello")), "world");
807 ASSERT_EQUAL(string(lastUrlParams.get("left")), "right");
808 ASSERT_EQUAL(string(lastUrlParams.get("up")), "down");
809 }
810 // check multiple value, multiple types
811 sendmsg = "GET /params?int=100&double=123.45&boolean=1\r\n\r\n";
812 {
813 asio::ip::tcp::socket c(is);
814 c.connect(asio::ip::tcp::endpoint(
815 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
816 c.send(asio::buffer(sendmsg));
817 c.receive(asio::buffer(buf, 2048));
818 c.close();
819
820 ASSERT_EQUAL(boost::lexical_cast<int>(lastUrlParams.get("int")), 100);
821 ASSERT_EQUAL(boost::lexical_cast<double>(lastUrlParams.get("double")),
822 123.45);
823 ASSERT_EQUAL(boost::lexical_cast<bool>(lastUrlParams.get("boolean")),
824 true);
825 }
826 // check single array value
827 sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n";
828 {
829 asio::ip::tcp::socket c(is);
830
831 c.connect(asio::ip::tcp::endpoint(
832 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
833 c.send(asio::buffer(sendmsg));
834 c.receive(asio::buffer(buf, 2048));
835 c.close();
836
837 ASSERT_TRUE(lastUrlParams.get("tmnt") == nullptr);
838 ASSERT_EQUAL(lastUrlParams.getList("tmnt").size(), 1);
839 ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[0]), "leonardo");
840 }
841 // check multiple array value
842 sendmsg =
843 "GET /params?tmnt[]=leonardo&tmnt[]=donatello&tmnt[]=raphael\r\n\r\n";
844 {
845 asio::ip::tcp::socket c(is);
846
847 c.connect(asio::ip::tcp::endpoint(
848 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
849 c.send(asio::buffer(sendmsg));
850 c.receive(asio::buffer(buf, 2048));
851 c.close();
852
853 ASSERT_EQUAL(lastUrlParams.getList("tmnt").size(), 3);
854 ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[0]), "leonardo");
855 ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[1]), "donatello");
856 ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[2]), "raphael");
857 }
858 server.stop();
859}
860
861TEST(Crow, routeDynamic)
862{
863 SimpleApp app;
864 int x = 1;
865 app.routeDynamic("/")([&] {
866 x = 2;
867 return "";
868 });
869
870 app.routeDynamic("/set4")([&](const Request&) {
871 x = 4;
872 return "";
873 });
874 app.routeDynamic("/set5")([&](const Request&, Response& res) {
875 x = 5;
876 res.end();
877 });
878
879 app.routeDynamic("/set_int/<int>")([&](int y) {
880 x = y;
881 return "";
882 });
883
884 try
885 {
886 app.routeDynamic("/invalid_test/<double>/<path>")([]() { return ""; });
887 fail();
888 }
889 catch (std::exception&)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500890 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700891
892 // app is in an invalid state when routeDynamic throws an exception.
893 try
894 {
895 app.validate();
896 fail();
897 }
898 catch (std::exception&)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500899 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700900
901 {
902 boost::beast::http::request<boost::beast::http::string_body> r{};
903 Request req{r};
904 Response res;
905 req.url = "/";
906 app.handle(req, res);
907 ASSERT_EQUAL(x, 2);
908 }
909 {
910 boost::beast::http::request<boost::beast::http::string_body> r{};
911 Request req{r};
912 Response res;
913 req.url = "/set_int/42";
914 app.handle(req, res);
915 ASSERT_EQUAL(x, 42);
916 }
917 {
918 boost::beast::http::request<boost::beast::http::string_body> r{};
919 Request req{r};
920 Response res;
921 req.url = "/set5";
922 app.handle(req, res);
923 ASSERT_EQUAL(x, 5);
924 }
925 {
926 boost::beast::http::request<boost::beast::http::string_body> r{};
927 Request req{r};
928 Response res;
929 req.url = "/set4";
930 app.handle(req, res);
931 ASSERT_EQUAL(x, 4);
932 }
Ed Tanous8041f312017-04-03 09:47:01 -0700933}