2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
42#include "express/dispatcher/regex_utils.h"
44#include "express/Request.h"
46#ifndef DOXYGEN_SHOULD_SKIP_THIS
54 std::vector<std::string>
explode(
const std::string& input,
char delim) {
55 std::vector<std::string> result;
59 for (
const char ch : input) {
63 }
else if (ch ==
')') {
66 }
else if (ch == delim && parenDepth == 0) {
67 result.push_back(current);
74 if (!current.empty()) {
75 result.push_back(current);
81#define PATH_REGEX ":[a-zA-Z0-9]+(\\(.+\\))?"
99 return std::regex_search(cpath, smatch,
pathRegex());
103 std::vector<std::string> explodedString =
explode(cpath
, '/');
104 std::vector<std::string> explodedReqString =
explode(req
.url, '/');
106 for (std::vector<std::string>::size_type i = 0; i < explodedString.size() && i < explodedReqString.size(); i++) {
107 if (!explodedString[i].empty() && explodedString[i].front() ==
':') {
109 std::string regex =
"(.*)";
111 if (smatch.size() > 1) {
112 if (smatch[1] !=
"") {
117 if (std::regex_match(explodedReqString[i], std::regex(regex))) {
118 std::string attributeName = smatch[0];
119 attributeName.erase(0, 1);
120 attributeName.erase((attributeName.length() -
static_cast<std::size_t>(smatch[1].length())),
121 static_cast<std::size_t>(smatch[1].length()));
123 req
.params[attributeName] = explodedReqString[i];
129 std::unordered_map<std::string, std::string>
parseQuery(std::string_view qs) {
130 std::unordered_map<std::string, std::string> m;
132 while (i < qs.size()) {
133 const std::size_t amp = qs.find(
'&', i);
134 const std::string_view pair = (amp == std::string_view::npos) ? qs.substr(i) : qs.substr(i, amp - i);
135 const std::size_t eq = pair.find(
'=');
138 if (eq == std::string_view::npos) {
141 key.assign(pair.substr(0, eq));
142 val.assign(pair.substr(eq + 1));
145 m.emplace(std::move(key), std::move(val));
147 if (amp == std::string_view::npos) {
155 std::pair<std::regex, std::vector<std::string>>
156 compileParamRegex(std::string_view mountPath,
bool isPrefix,
bool strictRouting,
bool caseInsensitive) {
158 pat.reserve(mountPath.size() * 2);
159 std::vector<std::string> names;
162 const char* s = mountPath.data();
163 const char* e = s + mountPath.size();
167 const char* nstart = s;
168 while (s < e && (std::isalnum(
static_cast<
unsigned char>(*s)) > 0 || *s ==
'_' || *s ==
'-')) {
171 std::string name(nstart, s);
173 if (s < e && *s ==
'(') {
175 const char* rstart = s + 1;
180 }
else if (*s ==
')' && depth-- == 0) {
185 custom.assign(rstart, s);
186 if (s < e && *s ==
')') {
190 names.push_back(std::move(name));
191 if (!custom.empty()) {
199 static const std::string meta = R"(\.^$|()[]{}*+?!)";
200 if (meta.find(*s) != std::string::npos) {
211 if (!strictRouting) {
217 auto flags = std::regex::ECMAScript | (!caseInsensitive ? std::regex::flag_type{} : std::regex::icase);
219 return {std::regex(pat, flags), std::move(names)};
223 const std::unordered_map<std::string, std::string>& need) {
224 for (
const auto& kv : need) {
225 auto it = rq.find(kv.first);
226 if (it == rq.end() || it->second != kv.second) {
233 bool boundaryPrefix(std::string_view path, std::string_view base,
bool caseInsensitive) {
240 if (base.size() == 1 && base[0] ==
'/') {
241 return !path.empty() && path.front() ==
'/';
245 if (base.size() > path.size()) {
249 auto eq = [&](
char a,
char b) {
250 return !caseInsensitive ? (a == b)
251 : (std::tolower(
static_cast<
unsigned char>(a)) == std::tolower(
static_cast<
unsigned char>(b)));
255 for (size_t i = 0; i < base.size(); ++i) {
256 if (!eq(path[i], base[i])) {
262 return (path.size() == base.size()) || (path[base.size()] ==
'/');
265 bool equalPath(std::string_view a, std::string_view b,
bool caseInsensitive) {
266 if (a.size() != b.size()) {
269 for (size_t i = 0; i < a.size(); ++i) {
270 if (!caseInsensitive ? (a[i] != b[i]) : !
ieq(a[i]
, b[i]
)) {
278 if (s.size() > 1 && s.back() ==
'/') {
279 return std::string_view(s.data(), s.size() - 1);
285 const std::size_t qpos = url.find(
'?');
286 if (qpos == std::string_view::npos) {
290 path = url.substr(0, qpos);
291 query = url.substr(qpos + 1);
std::map< std::string, std::string > params
bool querySupersetMatches(const std::unordered_map< std::string, std::string > &rq, const std::unordered_map< std::string, std::string > &need)
void setParams(const std::string &cpath, Request &req)
std::vector< std::string > explode(const std::string &input, char delim)
void splitPathAndQuery(std::string_view url, std::string_view &path, std::string_view &query)
bool boundaryPrefix(std::string_view path, std::string_view base, bool caseInsensitive)
std::pair< std::regex, std::vector< std::string > > compileParamRegex(std::string_view mountPath, bool isPrefix, bool strictRouting, bool caseInsensitive)
std::smatch matchResult(const std::string &cpath)
std::unordered_map< std::string, std::string > parseQuery(std::string_view qs)
bool hasResult(const std::string &cpath)
std::string_view trimOneTrailingSlash(std::string_view s)
bool equalPath(std::string_view a, std::string_view b, bool caseInsensitive)
const std::regex & pathRegex()