blob: 1f8360f01c0450ce1a9a5e618f1d486658ce7c82 [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 }
Patrick Williamsc5967042021-10-06 12:39:54 -050061 catch (const 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");
Ed Tanous988403c2020-08-24 11:29:49 -0700101 ASSERT_EQUAL(1, black_magic::getParameterTag("<int>"));
102 ASSERT_EQUAL(2, black_magic::getParameterTag("<uint>"));
103 ASSERT_EQUAL(3, black_magic::getParameterTag("<float>"));
104 ASSERT_EQUAL(3, black_magic::getParameterTag("<double>"));
105 ASSERT_EQUAL(4, black_magic::getParameterTag("<str>"));
106 ASSERT_EQUAL(4, black_magic::getParameterTag("<string>"));
107 ASSERT_EQUAL(5, black_magic::getParameterTag("<path>"));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 ASSERT_EQUAL(6 * 6 + 6 + 1,
Ed Tanous988403c2020-08-24 11:29:49 -0700109 black_magic::getParameterTag("<int><int><int>"));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700110 ASSERT_EQUAL(6 * 6 + 6 + 2,
Ed Tanous988403c2020-08-24 11:29:49 -0700111 black_magic::getParameterTag("<uint><int><int>"));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 ASSERT_EQUAL(6 * 6 + 6 * 3 + 2,
Ed Tanous988403c2020-08-24 11:29:49 -0700113 black_magic::getParameterTag("<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
Ed Tanousb41187f2019-10-24 16:30:02 -0700327 BMCWEB_ROUTE(app, "/").methods(boost::beast::http::verb::post,
328 boost::beast::http::verb::get)(
329 [](const Request& req) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700330 if (req.method() == boost::beast::http::verb::get)
331 return "2";
332 else
333 return "1";
334 });
Ed Tanous1abe55e2018-09-05 08:30:59 -0700335
336 BMCWEB_ROUTE(app, "/get_only")
Ed Tanousb41187f2019-10-24 16:30:02 -0700337 .methods(boost::beast::http::verb::get)(
338 [](const Request& /*req*/) { return "get"; });
Ed Tanous1abe55e2018-09-05 08:30:59 -0700339 BMCWEB_ROUTE(app, "/post_only")
Ed Tanousb41187f2019-10-24 16:30:02 -0700340 .methods(boost::beast::http::verb::post)(
341 [](const Request& /*req*/) { return "post"; });
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342
343 // cannot have multiple handlers for the same url
344 // BMCWEB_ROUTE(app, "/")
Ed Tanousb41187f2019-10-24 16:30:02 -0700345 //.methods(boost::beast::http::verb::get)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700346 //([]{ return "2"; });
347
348 {
349 boost::beast::http::request<boost::beast::http::string_body> r{};
350 Request req{r};
351 Response res;
352
353 req.url = "/";
354 app.handle(req, res);
355
356 ASSERT_EQUAL("2", res.body());
357 }
358 {
359 boost::beast::http::request<boost::beast::http::string_body> r{};
360 Request req{r};
361 Response res;
362
363 req.url = "/";
Ed Tanousb41187f2019-10-24 16:30:02 -0700364 r.method(boost::beast::http::verb::post);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700365 app.handle(req, res);
366
367 ASSERT_EQUAL("1", res.body());
368 }
369
370 {
371 boost::beast::http::request<boost::beast::http::string_body> r{};
372 Request req{r};
373 Response res;
374
375 req.url = "/get_only";
376 app.handle(req, res);
377
378 ASSERT_EQUAL("get", res.body());
379 }
380
381 {
382 boost::beast::http::request<boost::beast::http::string_body> r{};
383 Request req{r};
384 Response res;
385
386 req.url = "/get_only";
Ed Tanousb41187f2019-10-24 16:30:02 -0700387 r.method(boost::beast::http::verb::post);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700388 app.handle(req, res);
389
390 ASSERT_NOTEQUAL("get", res.body());
391 }
392}
393
394TEST(Crow, server_handling_error_request)
395{
396 static char buf[2048];
397 SimpleApp app;
398 BMCWEB_ROUTE(app, "/")([] { return "A"; });
399 Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451);
400 auto _ = async(launch::async, [&] { server.run(); });
401 std::string sendmsg = "POX";
Ed Tanous8f626352018-12-19 14:51:54 -0800402 asio::io_context is;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700403 {
Ed Tanous1ff48782017-04-18 12:45:08 -0700404 asio::ip::tcp::socket c(is);
405 c.connect(asio::ip::tcp::endpoint(
406 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
407
Ed Tanous1abe55e2018-09-05 08:30:59 -0700408 c.send(asio::buffer(sendmsg));
Ed Tanous1ff48782017-04-18 12:45:08 -0700409
Ed Tanous1abe55e2018-09-05 08:30:59 -0700410 try
411 {
412 c.receive(asio::buffer(buf, 2048));
413 fail();
Ed Tanous8041f312017-04-03 09:47:01 -0700414 }
Patrick Williamsc5967042021-10-06 12:39:54 -0500415 catch (const std::exception& e)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 {
417 // std::cerr << e.what() << std::endl;
418 }
Ed Tanous8041f312017-04-03 09:47:01 -0700419 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420 server.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700421}
422
Ed Tanous1abe55e2018-09-05 08:30:59 -0700423TEST(Crow, multi_server)
424{
425 static char buf[2048];
426 SimpleApp app1, app2;
Ed Tanousb41187f2019-10-24 16:30:02 -0700427 BMCWEB_ROUTE(app1, "/").methods(boost::beast::http::verb::get,
428 boost::beast::http::verb::post)(
429 [] { return "A"; });
430 BMCWEB_ROUTE(app2, "/").methods(boost::beast::http::verb::get,
431 boost::beast::http::verb::post)(
432 [] { return "B"; });
Ed Tanous8041f312017-04-03 09:47:01 -0700433
Ed Tanous1abe55e2018-09-05 08:30:59 -0700434 Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451);
435 Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452);
Ed Tanous8041f312017-04-03 09:47:01 -0700436
Ed Tanous1abe55e2018-09-05 08:30:59 -0700437 auto _ = async(launch::async, [&] { server1.run(); });
438 auto _2 = async(launch::async, [&] { server2.run(); });
Ed Tanous8041f312017-04-03 09:47:01 -0700439
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 std::string sendmsg =
441 "POST /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=b\r\n";
Ed Tanous8f626352018-12-19 14:51:54 -0800442 asio::io_context is;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700443 {
444 asio::ip::tcp::socket c(is);
445 c.connect(asio::ip::tcp::endpoint(
446 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
Ed Tanous8041f312017-04-03 09:47:01 -0700447
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 c.send(asio::buffer(sendmsg));
Ed Tanous8041f312017-04-03 09:47:01 -0700449
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 size_t recved = c.receive(asio::buffer(buf, 2048));
451 ASSERT_EQUAL('A', buf[recved - 1]);
452 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700453
Ed Tanous1abe55e2018-09-05 08:30:59 -0700454 {
455 asio::ip::tcp::socket c(is);
456 c.connect(asio::ip::tcp::endpoint(
457 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45452));
Ed Tanous1ff48782017-04-18 12:45:08 -0700458
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 for (auto ch : sendmsg)
460 {
461 char buf[1] = {ch};
462 c.send(asio::buffer(buf));
463 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700464
Ed Tanous1abe55e2018-09-05 08:30:59 -0700465 size_t recved = c.receive(asio::buffer(buf, 2048));
466 ASSERT_EQUAL('b', buf[recved - 1]);
467 }
Ed Tanous1ff48782017-04-18 12:45:08 -0700468
Ed Tanous1abe55e2018-09-05 08:30:59 -0700469 server1.stop();
470 server2.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700471}
472
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473TEST(Crow, black_magic)
474{
475 using namespace black_magic;
476 static_assert(
477 std::is_same<void, LastElementType<int, char, void>::type>::value,
478 "LastElementType");
479 static_assert(
480 std::is_same<char, PopBack<int, char,
481 void>::rebind<LastElementType>::type>::value,
482 "pop_back");
483 static_assert(
484 std::is_same<int, PopBack<int, char, void>::rebind<PopBack>::rebind<
485 LastElementType>::type>::value,
486 "pop_back");
487}
Ed Tanous8041f312017-04-03 09:47:01 -0700488
Ed Tanous1abe55e2018-09-05 08:30:59 -0700489struct NullMiddleware
490{
491 struct Context
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500492 {};
Ed Tanous8041f312017-04-03 09:47:01 -0700493
Ed Tanous1abe55e2018-09-05 08:30:59 -0700494 template <typename AllContext>
495 void beforeHandle(Request&, Response&, Context&, AllContext&)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500496 {}
Ed Tanous8041f312017-04-03 09:47:01 -0700497
Ed Tanous1abe55e2018-09-05 08:30:59 -0700498 template <typename AllContext>
499 void afterHandle(Request&, Response&, Context&, AllContext&)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500500 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700501};
Ed Tanous8041f312017-04-03 09:47:01 -0700502
Ed Tanous1abe55e2018-09-05 08:30:59 -0700503struct NullSimpleMiddleware
504{
505 struct Context
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500506 {};
Ed Tanous8041f312017-04-03 09:47:01 -0700507
Ed Tanous1abe55e2018-09-05 08:30:59 -0700508 void beforeHandle(Request& /*req*/, Response& /*res*/, Context& /*ctx*/)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500509 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700510
511 void afterHandle(Request& /*req*/, Response& /*res*/, Context& /*ctx*/)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500512 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700513};
514
515TEST(Crow, middleware_simple)
516{
517 App<NullMiddleware, NullSimpleMiddleware> app;
518 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
519 BMCWEB_ROUTE(app, "/")
520 ([&](const crow::Request& req) {
521 app.getContext<NullMiddleware>(req);
522 app.getContext<NullSimpleMiddleware>(req);
523 return "";
524 });
525}
526
527struct IntSettingMiddleware
528{
529 struct Context
530 {
531 int val;
532 };
533
534 template <typename AllContext>
535 void beforeHandle(Request&, Response&, Context& ctx, AllContext&)
536 {
537 ctx.val = 1;
538 }
539
540 template <typename AllContext>
541 void afterHandle(Request&, Response&, Context& ctx, AllContext&)
542 {
543 ctx.val = 2;
544 }
545};
546
547std::vector<std::string> test_middleware_context_vector;
548
549struct FirstMW
550{
551 struct Context
552 {
553 std::vector<string> v;
554 };
555
556 void beforeHandle(Request& /*req*/, Response& /*res*/, Context& ctx)
557 {
558 ctx.v.push_back("1 before");
559 }
560
561 void afterHandle(Request& /*req*/, Response& /*res*/, Context& ctx)
562 {
563 ctx.v.push_back("1 after");
564 test_middleware_context_vector = ctx.v;
565 }
566};
567
568struct SecondMW
569{
570 struct Context
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500571 {};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572 template <typename AllContext>
573 void beforeHandle(Request& req, Response& res, Context&,
574 AllContext& all_ctx)
575 {
576 all_ctx.template get<FirstMW>().v.push_back("2 before");
577 if (req.url == "/break")
578 res.end();
579 }
580
581 template <typename AllContext>
582 void afterHandle(Request&, Response&, Context&, AllContext& all_ctx)
583 {
584 all_ctx.template get<FirstMW>().v.push_back("2 after");
585 }
586};
587
588struct ThirdMW
589{
590 struct Context
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500591 {};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700592 template <typename AllContext>
593 void beforeHandle(Request&, Response&, Context&, AllContext& all_ctx)
594 {
595 all_ctx.template get<FirstMW>().v.push_back("3 before");
596 }
597
598 template <typename AllContext>
599 void afterHandle(Request&, Response&, Context&, AllContext& all_ctx)
600 {
601 all_ctx.template get<FirstMW>().v.push_back("3 after");
602 }
603};
604
605TEST(Crow, middlewareContext)
606{
607 static char buf[2048];
608 // SecondMW depends on FirstMW (it uses all_ctx.get<FirstMW>)
609 // so it leads to compile error if we remove FirstMW from definition
610 // App<IntSettingMiddleware, SecondMW> app;
611 // or change the order of FirstMW and SecondMW
612 // App<IntSettingMiddleware, SecondMW, FirstMW> app;
613
614 App<IntSettingMiddleware, FirstMW, SecondMW, ThirdMW> app;
615
616 int x{};
617 BMCWEB_ROUTE(app, "/")
618 ([&](const Request& req) {
619 {
620 auto& ctx = app.getContext<IntSettingMiddleware>(req);
621 x = ctx.val;
622 }
623 {
624 auto& ctx = app.getContext<FirstMW>(req);
625 ctx.v.push_back("handle");
626 }
627
628 return "";
629 });
630 BMCWEB_ROUTE(app, "/break")
631 ([&](const Request& req) {
632 {
633 auto& ctx = app.getContext<FirstMW>(req);
634 ctx.v.push_back("handle");
635 }
636
637 return "";
638 });
639
640 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
641 auto _ = async(launch::async, [&] { server.run(); });
642 std::string sendmsg = "GET /\r\n\r\n";
Ed Tanous8f626352018-12-19 14:51:54 -0800643 asio::io_context is;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700644 {
645 asio::ip::tcp::socket c(is);
646 c.connect(asio::ip::tcp::endpoint(
647 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
648
649 c.send(asio::buffer(sendmsg));
650
651 c.receive(asio::buffer(buf, 2048));
652 c.close();
653 }
654 {
655 auto& out = test_middleware_context_vector;
656 ASSERT_EQUAL(1, x);
657 ASSERT_EQUAL(7, out.size());
658 ASSERT_EQUAL("1 before", out[0]);
659 ASSERT_EQUAL("2 before", out[1]);
660 ASSERT_EQUAL("3 before", out[2]);
661 ASSERT_EQUAL("handle", out[3]);
662 ASSERT_EQUAL("3 after", out[4]);
663 ASSERT_EQUAL("2 after", out[5]);
664 ASSERT_EQUAL("1 after", out[6]);
665 }
666 std::string sendmsg2 = "GET /break\r\n\r\n";
667 {
668 asio::ip::tcp::socket c(is);
669 c.connect(asio::ip::tcp::endpoint(
670 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
671
672 c.send(asio::buffer(sendmsg2));
673
674 c.receive(asio::buffer(buf, 2048));
675 c.close();
676 }
677 {
678 auto& out = test_middleware_context_vector;
679 ASSERT_EQUAL(4, out.size());
680 ASSERT_EQUAL("1 before", out[0]);
681 ASSERT_EQUAL("2 before", out[1]);
682 ASSERT_EQUAL("2 after", out[2]);
683 ASSERT_EQUAL("1 after", out[3]);
684 }
685 server.stop();
686}
687
688TEST(Crow, bug_quick_repeated_request)
689{
690 static char buf[2048];
691
692 SimpleApp app;
693
694 BMCWEB_ROUTE(app, "/")([&] { return "hello"; });
695
696 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
697 auto _ = async(launch::async, [&] { server.run(); });
698 std::string sendmsg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
Ed Tanous8f626352018-12-19 14:51:54 -0800699 asio::io_context is;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700700 {
701 std::vector<std::future<void>> v;
702 for (int i = 0; i < 5; i++)
703 {
704 v.push_back(async(launch::async, [&] {
705 asio::ip::tcp::socket c(is);
706 c.connect(asio::ip::tcp::endpoint(
707 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
708
709 for (int j = 0; j < 5; j++)
710 {
711 c.send(asio::buffer(sendmsg));
712
713 size_t received = c.receive(asio::buffer(buf, 2048));
714 ASSERT_EQUAL("hello", std::string(buf + received - 5,
715 buf + received));
716 }
717 c.close();
718 }));
719 }
720 }
721 server.stop();
722}
723
724TEST(Crow, simple_url_params)
725{
726 static char buf[2048];
727
728 SimpleApp app;
729
730 QueryString lastUrlParams;
731
732 BMCWEB_ROUTE(app, "/params")
733 ([&lastUrlParams](const crow::Request& req) {
734 lastUrlParams = std::move(req.urlParams);
735 return "OK";
736 });
737
738 /// params?h=1&foo=bar&lol&count[]=1&count[]=4&pew=5.2
739
740 decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
741 auto _ = async(launch::async, [&] { server.run(); });
Ed Tanous8f626352018-12-19 14:51:54 -0800742 asio::io_context is;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743 std::string sendmsg;
744
745 // check empty params
746 sendmsg = "GET /params\r\n\r\n";
747 {
748 asio::ip::tcp::socket c(is);
749 c.connect(asio::ip::tcp::endpoint(
750 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
751 c.send(asio::buffer(sendmsg));
752 c.receive(asio::buffer(buf, 2048));
753 c.close();
754
755 stringstream ss;
756 ss << lastUrlParams;
757
758 ASSERT_EQUAL("[ ]", ss.str());
759 }
760 // check single presence
761 sendmsg = "GET /params?foobar\r\n\r\n";
762 {
763 asio::ip::tcp::socket c(is);
764 c.connect(asio::ip::tcp::endpoint(
765 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
766 c.send(asio::buffer(sendmsg));
767 c.receive(asio::buffer(buf, 2048));
768 c.close();
769
770 ASSERT_TRUE(lastUrlParams.get("missing") == nullptr);
771 ASSERT_TRUE(lastUrlParams.get("foobar") != nullptr);
772 ASSERT_TRUE(lastUrlParams.getList("missing").empty());
773 }
774 // check multiple presence
775 sendmsg = "GET /params?foo&bar&baz\r\n\r\n";
776 {
777 asio::ip::tcp::socket c(is);
778 c.connect(asio::ip::tcp::endpoint(
779 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
780 c.send(asio::buffer(sendmsg));
781 c.receive(asio::buffer(buf, 2048));
782 c.close();
783
784 ASSERT_TRUE(lastUrlParams.get("missing") == nullptr);
785 ASSERT_TRUE(lastUrlParams.get("foo") != nullptr);
786 ASSERT_TRUE(lastUrlParams.get("bar") != nullptr);
787 ASSERT_TRUE(lastUrlParams.get("baz") != nullptr);
788 }
789 // check single value
790 sendmsg = "GET /params?hello=world\r\n\r\n";
791 {
792 asio::ip::tcp::socket c(is);
793 c.connect(asio::ip::tcp::endpoint(
794 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
795 c.send(asio::buffer(sendmsg));
796 c.receive(asio::buffer(buf, 2048));
797 c.close();
798
799 ASSERT_EQUAL(string(lastUrlParams.get("hello")), "world");
800 }
801 // check multiple value
802 sendmsg = "GET /params?hello=world&left=right&up=down\r\n\r\n";
803 {
804 asio::ip::tcp::socket c(is);
805 c.connect(asio::ip::tcp::endpoint(
806 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
807 c.send(asio::buffer(sendmsg));
808 c.receive(asio::buffer(buf, 2048));
809 c.close();
810
811 ASSERT_EQUAL(string(lastUrlParams.get("hello")), "world");
812 ASSERT_EQUAL(string(lastUrlParams.get("left")), "right");
813 ASSERT_EQUAL(string(lastUrlParams.get("up")), "down");
814 }
815 // check multiple value, multiple types
816 sendmsg = "GET /params?int=100&double=123.45&boolean=1\r\n\r\n";
817 {
818 asio::ip::tcp::socket c(is);
819 c.connect(asio::ip::tcp::endpoint(
820 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
821 c.send(asio::buffer(sendmsg));
822 c.receive(asio::buffer(buf, 2048));
823 c.close();
824
825 ASSERT_EQUAL(boost::lexical_cast<int>(lastUrlParams.get("int")), 100);
826 ASSERT_EQUAL(boost::lexical_cast<double>(lastUrlParams.get("double")),
827 123.45);
828 ASSERT_EQUAL(boost::lexical_cast<bool>(lastUrlParams.get("boolean")),
829 true);
830 }
831 // check single array value
832 sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n";
833 {
834 asio::ip::tcp::socket c(is);
835
836 c.connect(asio::ip::tcp::endpoint(
837 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
838 c.send(asio::buffer(sendmsg));
839 c.receive(asio::buffer(buf, 2048));
840 c.close();
841
842 ASSERT_TRUE(lastUrlParams.get("tmnt") == nullptr);
843 ASSERT_EQUAL(lastUrlParams.getList("tmnt").size(), 1);
844 ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[0]), "leonardo");
845 }
846 // check multiple array value
847 sendmsg =
848 "GET /params?tmnt[]=leonardo&tmnt[]=donatello&tmnt[]=raphael\r\n\r\n";
849 {
850 asio::ip::tcp::socket c(is);
851
852 c.connect(asio::ip::tcp::endpoint(
853 asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
854 c.send(asio::buffer(sendmsg));
855 c.receive(asio::buffer(buf, 2048));
856 c.close();
857
858 ASSERT_EQUAL(lastUrlParams.getList("tmnt").size(), 3);
859 ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[0]), "leonardo");
860 ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[1]), "donatello");
861 ASSERT_EQUAL(string(lastUrlParams.getList("tmnt")[2]), "raphael");
862 }
863 server.stop();
864}
865
866TEST(Crow, routeDynamic)
867{
868 SimpleApp app;
869 int x = 1;
870 app.routeDynamic("/")([&] {
871 x = 2;
872 return "";
873 });
874
875 app.routeDynamic("/set4")([&](const Request&) {
876 x = 4;
877 return "";
878 });
879 app.routeDynamic("/set5")([&](const Request&, Response& res) {
880 x = 5;
881 res.end();
882 });
883
884 app.routeDynamic("/set_int/<int>")([&](int y) {
885 x = y;
886 return "";
887 });
888
889 try
890 {
891 app.routeDynamic("/invalid_test/<double>/<path>")([]() { return ""; });
892 fail();
893 }
Patrick Williamsc5967042021-10-06 12:39:54 -0500894 catch (const std::exception&)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500895 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700896
897 // app is in an invalid state when routeDynamic throws an exception.
898 try
899 {
900 app.validate();
901 fail();
902 }
Patrick Williamsc5967042021-10-06 12:39:54 -0500903 catch (const std::exception&)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500904 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700905
906 {
907 boost::beast::http::request<boost::beast::http::string_body> r{};
908 Request req{r};
909 Response res;
910 req.url = "/";
911 app.handle(req, res);
912 ASSERT_EQUAL(x, 2);
913 }
914 {
915 boost::beast::http::request<boost::beast::http::string_body> r{};
916 Request req{r};
917 Response res;
918 req.url = "/set_int/42";
919 app.handle(req, res);
920 ASSERT_EQUAL(x, 42);
921 }
922 {
923 boost::beast::http::request<boost::beast::http::string_body> r{};
924 Request req{r};
925 Response res;
926 req.url = "/set5";
927 app.handle(req, res);
928 ASSERT_EQUAL(x, 5);
929 }
930 {
931 boost::beast::http::request<boost::beast::http::string_body> r{};
932 Request req{r};
933 Response res;
934 req.url = "/set4";
935 app.handle(req, res);
936 ASSERT_EQUAL(x, 4);
937 }
Ed Tanous8041f312017-04-03 09:47:01 -0700938}