Extract the route related code to new class Router and add UT.
parent
9166b5d99e
commit
53a731caf5
@ -0,0 +1,77 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "webcc/router.h"
|
||||
#include "webcc/response_builder.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class MyView : public webcc::View {
|
||||
public:
|
||||
webcc::ResponsePtr Handle(webcc::RequestPtr request) override {
|
||||
return webcc::ResponseBuilder{}.OK()();
|
||||
}
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
TEST(RouterTest, URL_RegexBasic) {
|
||||
webcc::Router router;
|
||||
|
||||
router.Route(webcc::R("/instance/(\\d+)"),
|
||||
std::make_shared<MyView>());
|
||||
|
||||
std::string url = "/instance/12345";
|
||||
webcc::UrlArgs args;
|
||||
|
||||
webcc::ViewPtr view = router.FindView("GET", url, &args);
|
||||
|
||||
EXPECT_TRUE(!!view);
|
||||
|
||||
EXPECT_EQ(1, args.size());
|
||||
EXPECT_EQ("12345", args[0]);
|
||||
|
||||
url = "/instance/abcde";
|
||||
args.clear();
|
||||
view = router.FindView("GET", url, &args);
|
||||
|
||||
EXPECT_TRUE(!view);
|
||||
}
|
||||
|
||||
TEST(RouterTest, URL_RegexMultiple) {
|
||||
webcc::Router router;
|
||||
|
||||
router.Route(webcc::R("/study/(\\d+)/series/(\\d+)/instance/(\\d+)"),
|
||||
std::make_shared<MyView>());
|
||||
|
||||
std::string url = "/study/1/series/2/instance/3";
|
||||
webcc::UrlArgs args;
|
||||
|
||||
webcc::ViewPtr view = router.FindView("GET", url, &args);
|
||||
|
||||
EXPECT_TRUE(!!view);
|
||||
|
||||
EXPECT_EQ(3, args.size());
|
||||
EXPECT_EQ("1", args[0]);
|
||||
EXPECT_EQ("2", args[1]);
|
||||
EXPECT_EQ("3", args[2]);
|
||||
|
||||
url = "/study/a/series/b/instance/c";
|
||||
args.clear();
|
||||
view = router.FindView("GET", url, &args);
|
||||
|
||||
EXPECT_TRUE(!view);
|
||||
}
|
||||
|
||||
TEST(RouterTest, URL_NonRegex) {
|
||||
webcc::Router router;
|
||||
|
||||
router.Route("/instances", std::make_shared<MyView>());
|
||||
|
||||
std::string url = "/instances";
|
||||
webcc::UrlArgs args;
|
||||
|
||||
webcc::ViewPtr view = router.FindView("GET", url, &args);
|
||||
|
||||
EXPECT_TRUE(!!view);
|
||||
EXPECT_TRUE(args.empty());
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
#include "webcc/router.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
bool Router::Route(const std::string& url, ViewPtr view,
|
||||
const Strings& methods) {
|
||||
assert(view);
|
||||
|
||||
// TODO: More error check
|
||||
|
||||
routes_.push_back({ url, {}, view, methods });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Router::Route(const UrlRegex& regex_url, ViewPtr view,
|
||||
const Strings& methods) {
|
||||
assert(view);
|
||||
|
||||
// TODO: More error check
|
||||
|
||||
try {
|
||||
|
||||
routes_.push_back({ "", regex_url(), view, methods });
|
||||
|
||||
} catch (const std::regex_error& e) {
|
||||
LOG_ERRO("Not a valid regular expression: %s", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ViewPtr Router::FindView(const std::string& method, const std::string& url,
|
||||
UrlArgs* args) {
|
||||
assert(args != nullptr);
|
||||
|
||||
for (auto& route : routes_) {
|
||||
if (std::find(route.methods.begin(), route.methods.end(), method) ==
|
||||
route.methods.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (route.url.empty()) {
|
||||
std::smatch match;
|
||||
|
||||
if (std::regex_match(url, match, route.url_regex)) {
|
||||
// Any sub-matches?
|
||||
// Start from 1 because match[0] is the whole string itself.
|
||||
for (size_t i = 1; i < match.size(); ++i) {
|
||||
args->push_back(match[i].str());
|
||||
}
|
||||
|
||||
return route.view;
|
||||
}
|
||||
} else {
|
||||
if (boost::iequals(route.url, url)) {
|
||||
return route.view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ViewPtr();
|
||||
}
|
||||
|
||||
bool Router::MatchView(const std::string& method, const std::string& url,
|
||||
bool* stream) {
|
||||
assert(stream != nullptr);
|
||||
*stream = false;
|
||||
|
||||
for (auto& route : routes_) {
|
||||
if (std::find(route.methods.begin(), route.methods.end(), method) ==
|
||||
route.methods.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (route.url.empty()) {
|
||||
std::smatch match;
|
||||
|
||||
if (std::regex_match(url, match, route.url_regex)) {
|
||||
*stream = route.view->Stream(method);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (boost::iequals(route.url, url)) {
|
||||
*stream = route.view->Stream(method);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace webcc
|
@ -0,0 +1,51 @@
|
||||
#ifndef WEBCC_ROUTER_H_
|
||||
#define WEBCC_ROUTER_H_
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/view.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class Router {
|
||||
public:
|
||||
virtual ~Router() = default;
|
||||
|
||||
// Route a URL to a view.
|
||||
// The URL should start with "/". E.g., "/instances".
|
||||
bool Route(const std::string& url, ViewPtr view,
|
||||
const Strings& methods = { "GET" });
|
||||
|
||||
// Route a URL (as regular expression) to a view.
|
||||
// The URL should start with "/" and be a regular expression.
|
||||
// E.g., "/instances/(\\d+)".
|
||||
bool Route(const UrlRegex& regex_url, ViewPtr view,
|
||||
const Strings& methods = { "GET" });
|
||||
|
||||
// Find the view by HTTP method and URL (path).
|
||||
ViewPtr FindView(const std::string& method, const std::string& url,
|
||||
UrlArgs* args);
|
||||
|
||||
// Match the view by HTTP method and URL (path).
|
||||
// Return if a view is matched or not.
|
||||
// If the view asks for data streaming, |stream| will be set to true.
|
||||
bool MatchView(const std::string& method, const std::string& url,
|
||||
bool* stream);
|
||||
|
||||
private:
|
||||
struct RouteInfo {
|
||||
std::string url;
|
||||
std::regex url_regex;
|
||||
ViewPtr view;
|
||||
Strings methods;
|
||||
};
|
||||
|
||||
// Route table.
|
||||
std::vector<RouteInfo> routes_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_ROUTER_H_
|
Loading…
Reference in New Issue