SNode.C
Loading...
Searching...
No Matches
express::dispatcher Namespace Reference

Classes

struct  MountMatchResult
class  ScopedPathStrip
class  ScopedParams
class  ApplicationDispatcher
class  MiddlewareDispatcher
class  RouterDispatcher

Functions

bool isParamNameChar (const char c)
bool routeNeedsRegex (std::string_view path)
bool isRegexMetaToEscape (const char c)
void appendEscapedLiteral (std::string &out, const char c)
bool extractBalancedParenInner (std::string_view s, const std::size_t openPos, std::string &innerOut, std::size_t &closePosOut)
std::string makeInnerGroupsNonCapturing (std::string_view pattern)
int hexToInt (const char c)
bool decodeURIComponent (std::string_view in, std::string &out)
std::string decodeQueryComponent (std::string_view in)
bool matchAndFillParamsAndConsume (const std::regex &rx, const std::vector< std::string > &names, std::string_view reqPath, std::map< std::string, std::string > &params, std::size_t &consumedLength, bool &decodeError)
std::unordered_map< std::string, std::string > parseQuery (std::string_view qs)
std::pair< std::regex, std::vector< std::string > > compileParamRegex (std::string_view mountPath, bool isPrefix, bool strictRouting, bool caseInsensitive)
bool boundaryPrefix (std::string_view path, std::string_view base, bool caseInsensitive)
bool equalPath (std::string_view a, std::string_view b, bool caseInsensitive)
std::string_view trimOneTrailingSlash (std::string_view s)
void splitPathAndQuery (std::string_view url, std::string_view &path, std::string_view &query)
bool methodMatches (std::string_view requestMethod, const std::string &mountMethod)
std::string joinMountPath (std::string_view parentMountPath, std::string_view relativeMountPath)
static MountMatchResult matchMountPointImpl (express::Controller &controller, const std::string &absoluteMountPath, const express::MountPoint &mountPoint, std::regex *cachedRegex, std::vector< std::string > *cachedNames, bool strictRouting, bool caseInsensitiveRouting)
MountMatchResult matchMountPoint (express::Controller &controller, const std::string &absoluteMountPath, const express::MountPoint &mountPoint, std::regex &cachedRegex, std::vector< std::string > &cachedNames, bool strictRouting, bool caseInsensitiveRouting)
bool ieq (char a, char b)

Function Documentation

◆ appendEscapedLiteral()

void express::dispatcher::appendEscapedLiteral ( std::string & out,
const char c )
inline

Definition at line 105 of file regex_utils.cpp.

105 {
106 if (isRegexMetaToEscape(c)) {
107 out.push_back('\\');
108 }
109 out.push_back(c);
110 }
bool isRegexMetaToEscape(const char c)

References isRegexMetaToEscape().

Referenced by compileParamRegex().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ boundaryPrefix()

bool express::dispatcher::boundaryPrefix ( std::string_view path,
std::string_view base,
bool caseInsensitive )
inline

Definition at line 527 of file regex_utils.cpp.

527 {
528 // Normalize: an empty base is equivalent to "/"
529 if (base.empty()) {
530 base = "/";
531 }
532
533 // Special case: base "/" matches any absolute path
534 if (base.size() == 1 && base[0] == '/') {
535 return !path.empty() && path.front() == '/';
536 }
537
538 // Base longer than path cannot match
539 if (base.size() > path.size()) {
540 return false;
541 }
542
543 auto eq = [&](char a, char b) {
544 return !caseInsensitive ? (a == b)
545 : (std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b)));
546 };
547
548 // Check prefix characters
549 for (size_t i = 0; i < base.size(); ++i) {
550 if (!eq(path[i], base[i])) {
551 return false;
552 }
553 }
554
555 // Boundary: either exact match, or next char is a '/'
556 return (path.size() == base.size()) || (path[base.size()] == '/');
557 }

Referenced by matchMountPointImpl().

Here is the caller graph for this function:

◆ compileParamRegex()

std::pair< std::regex, std::vector< std::string > > express::dispatcher::compileParamRegex ( std::string_view mountPath,
bool isPrefix,
bool strictRouting,
bool caseInsensitive )
inline

Definition at line 339 of file regex_utils.cpp.

339 {
340 // Express v4 string route patterns support:
341 // - named params: /users/:id
342 // - custom param patterns: /users/:id(\d+)
343 // - param modifiers: ?, +, * e.g. /:id? /:path+ /:path*
344 // - wildcards: * e.g. /ab*cd /hello/world(/*)?
345 // - grouping: () (converted to non-capturing by default)
346 // - operators: ? + (regex-like, per Express examples)
347
348 std::string rx;
349 rx.reserve(mountPath.size() * 2U + 32U);
350 rx.push_back('^');
351
352 std::vector<std::string> names;
353 names.reserve(4);
354
355 std::size_t splatIndex = 0;
356
357 for (std::size_t i = 0; i < mountPath.size();) {
358 const char c = mountPath[i];
359
360 // Backslash escapes the next char literally.
361 if (c == '\\') {
362 if (i + 1 < mountPath.size()) {
363 appendEscapedLiteral(rx, mountPath[i + 1]);
364 i += 2;
365 } else {
367 ++i;
368 }
369 continue;
370 }
371
372 // Named parameter: :name, optionally :name(<regex>) and modifier ? + *
373 if (c == ':') {
374 std::size_t j = i + 1;
375 while (j < mountPath.size() && isParamNameChar(mountPath[j])) {
376 ++j;
377 }
378
379 if (j == i + 1) {
380 // Lone ':' -> treat as literal.
382 ++i;
383 continue;
384 }
385
386 std::string name(mountPath.substr(i + 1, j - (i + 1)));
387 std::string paramPattern = "[^/]+";
388
389 // Custom regex in parentheses: :name(<pattern>)
390 if (j < mountPath.size() && mountPath[j] == '(') {
391 std::string inner;
392 std::size_t closePos = 0;
393 if (extractBalancedParenInner(mountPath, j, inner, closePos)) {
394 paramPattern = makeInnerGroupsNonCapturing(inner);
395 j = closePos + 1;
396 }
397 }
398
399 char modifier = '\0';
400 if (j < mountPath.size() && (mountPath[j] == '?' || mountPath[j] == '+' || mountPath[j] == '*')) {
401 modifier = mountPath[j];
402 ++j;
403 }
404
405 // If the previous output ended with '/', include the delimiter in optional/repeat
406 // to emulate Express segment-parameter behaviour.
407 const bool hasLeadingSlash = (!rx.empty() && rx.back() == '/');
408
409 if (hasLeadingSlash && (modifier == '?' || modifier == '+' || modifier == '*')) {
410 rx.pop_back();
411
412 if (modifier == '?') {
413 rx.append("(?:/(");
414 rx.append(paramPattern);
415 rx.append("))?");
416 } else if (modifier == '+') {
417 rx.append("/(");
418 rx.append(paramPattern);
419 rx.append("(?:/");
420 rx.append(paramPattern);
421 rx.append(")*)");
422 } else { // '*'
423 rx.append("(?:/(");
424 rx.append(paramPattern);
425 rx.append("(?:/");
426 rx.append(paramPattern);
427 rx.append(")*))?");
428 }
429 } else {
430 if (modifier == '?') {
431 rx.append("(");
432 rx.append(paramPattern);
433 rx.append(")?");
434 } else if (modifier == '+') {
435 rx.append("(");
436 rx.append(paramPattern);
437 rx.append("(?:/");
438 rx.append(paramPattern);
439 rx.append(")*)");
440 } else if (modifier == '*') {
441 rx.append("(");
442 rx.append(paramPattern);
443 rx.append("(?:/");
444 rx.append(paramPattern);
445 rx.append(")*)?");
446 } else {
447 rx.append("(");
448 rx.append(paramPattern);
449 rx.append(")");
450 }
451 }
452
453 names.emplace_back(std::move(name));
454 i = j;
455 continue;
456 }
457
458 // Express wildcard token '*': matches any sequence including '/'.
459 if (c == '*') {
460 rx.append("(.*)");
461 names.emplace_back(std::to_string(splatIndex++)); // Express-style numeric params
462 ++i;
463 continue;
464 }
465
466 // Grouping: convert to non-capturing by default to keep capture numbering stable.
467 if (c == '(') {
468 if (i + 1 < mountPath.size() && mountPath[i + 1] == '?') {
469 // Keep special groups as-is.
470 rx.push_back('(');
471 } else {
472 rx.append("(?:");
473 }
474 ++i;
475 continue;
476 }
477
478 if (c == ')') {
479 rx.push_back(')');
480 ++i;
481 continue;
482 }
483
484 // Operators allowed by Express v4 string routes.
485 if (c == '?' || c == '+') {
486 rx.push_back(c);
487 ++i;
488 continue;
489 }
490
491 // Ordinary literal.
493 ++i;
494 }
495
496 if (isPrefix) {
497 rx.append("(?:/|$)"); // boundary prefix
498 } else {
499 if (!strictRouting) {
500 rx.append("/?"); // allow single trailing slash
501 }
502 rx.push_back('$');
503 }
504
505 std::regex::flag_type flags = std::regex::ECMAScript;
506 if (caseInsensitive) {
507 flags |= std::regex::icase;
508 }
509
510 try {
511 std::regex re(rx, flags);
512 // Cache sentinel: allow caching even for regex routes without capture groups.
513 if (names.empty()) {
514 names.emplace_back("__compiled");
515 }
516 return {std::move(re), std::move(names)};
517 } catch (const std::regex_error&) {
518 std::regex re("a^", flags); // match-nothing
519 if (names.empty()) {
520 names.emplace_back("__compiled");
521 }
522
523 return {std::move(re), std::move(names)};
524 }
525 }
std::string makeInnerGroupsNonCapturing(std::string_view pattern)
bool extractBalancedParenInner(std::string_view s, const std::size_t openPos, std::string &innerOut, std::size_t &closePosOut)
bool isParamNameChar(const char c)
void appendEscapedLiteral(std::string &out, const char c)

References appendEscapedLiteral(), extractBalancedParenInner(), isParamNameChar(), and makeInnerGroupsNonCapturing().

Referenced by matchMountPointImpl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ decodeQueryComponent()

std::string express::dispatcher::decodeQueryComponent ( std::string_view in)
inline

Definition at line 258 of file regex_utils.cpp.

258 {
259 std::string out;
260 out.reserve(in.size());
261
262 for (std::size_t i = 0; i < in.size(); ++i) {
263 const char c = in[i];
264 if (c == '+') {
265 out.push_back(' ');
266 continue;
267 }
268 if (c == '%' && i + 2 < in.size()) {
269 const int hi = hexToInt(in[i + 1]);
270 const int lo = hexToInt(in[i + 2]);
271 if (hi >= 0 && lo >= 0) {
272 out.push_back(static_cast<char>((hi << 4) | lo));
273 i += 2;
274 continue;
275 }
276 }
277 out.push_back(c);
278 }
279
280 return out;
281 }
int hexToInt(const char c)

References hexToInt().

Referenced by parseQuery().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ decodeURIComponent()

bool express::dispatcher::decodeURIComponent ( std::string_view in,
std::string & out )
inline

Definition at line 233 of file regex_utils.cpp.

233 {
234 out.clear();
235 out.reserve(in.size());
236
237 for (std::size_t i = 0; i < in.size(); ++i) {
238 if (in[i] == '%') {
239 if (i + 2 >= in.size()) {
240 return false;
241 }
242
243 const int hi = hexToInt(in[i + 1]);
244 const int lo = hexToInt(in[i + 2]);
245 if (hi < 0 || lo < 0) {
246 return false;
247 }
248 out.push_back(static_cast<char>((hi << 4) | lo));
249 i += 2;
250 continue;
251 }
252 out.push_back(in[i]);
253 }
254
255 return true;
256 }

References hexToInt().

Referenced by matchAndFillParamsAndConsume().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ equalPath()

bool express::dispatcher::equalPath ( std::string_view a,
std::string_view b,
bool caseInsensitive )
inline

Definition at line 559 of file regex_utils.cpp.

559 {
560 if (a.size() != b.size()) {
561 return false;
562 }
563 for (size_t i = 0; i < a.size(); ++i) {
564 if (!caseInsensitive ? (a[i] != b[i]) : !ieq(a[i], b[i])) {
565 return false;
566 }
567 }
568 return true;
569 }
bool ieq(char a, char b)
Definition regex_utils.h:66

References ieq().

Referenced by matchMountPointImpl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ extractBalancedParenInner()

bool express::dispatcher::extractBalancedParenInner ( std::string_view s,
const std::size_t openPos,
std::string & innerOut,
std::size_t & closePosOut )
inline

Definition at line 113 of file regex_utils.cpp.

113 {
114 if (openPos >= s.size() || s[openPos] != '(') {
115 return false;
116 }
117
118 bool escaped = false;
119 bool inCharClass = false;
120 int depth = 0;
121
122 for (std::size_t i = openPos; i < s.size(); ++i) {
123 const char c = s[i];
124
125 if (escaped) {
126 escaped = false;
127 continue;
128 }
129
130 if (c == '\\') {
131 escaped = true;
132 continue;
133 }
134
135 if (inCharClass) {
136 if (c == ']') {
137 inCharClass = false;
138 }
139 continue;
140 }
141
142 if (c == '[') {
143 inCharClass = true;
144 continue;
145 }
146
147 if (c == '(') {
148 ++depth;
149 continue;
150 }
151
152 if (c == ')') {
153 --depth;
154 if (depth == 0) {
155 closePosOut = i;
156 innerOut.assign(s.substr(openPos + 1, closePosOut - openPos - 1));
157 return true;
158 }
159 continue;
160 }
161 }
162
163 return false;
164 }

Referenced by compileParamRegex().

Here is the caller graph for this function:

◆ hexToInt()

int express::dispatcher::hexToInt ( const char c)
inline

Definition at line 220 of file regex_utils.cpp.

220 {
221 if (c >= '0' && c <= '9') {
222 return c - '0';
223 }
224 if (c >= 'a' && c <= 'f') {
225 return c - 'a' + 10;
226 }
227 if (c >= 'A' && c <= 'F') {
228 return c - 'A' + 10;
229 }
230 return -1;
231 }

Referenced by decodeQueryComponent(), and decodeURIComponent().

Here is the caller graph for this function:

◆ ieq()

bool express::dispatcher::ieq ( char a,
char b )
inline

Definition at line 66 of file regex_utils.h.

66 {
67 return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b));
68 }

Referenced by equalPath().

Here is the caller graph for this function:

◆ isParamNameChar()

bool express::dispatcher::isParamNameChar ( const char c)
inline

Definition at line 59 of file regex_utils.cpp.

59 {
60 const unsigned char uc = static_cast<unsigned char>(c);
61 return (std::isalnum(uc) != 0) || (c == '_');
62 }

Referenced by compileParamRegex().

Here is the caller graph for this function:

◆ isRegexMetaToEscape()

bool express::dispatcher::isRegexMetaToEscape ( const char c)
inline

Definition at line 83 of file regex_utils.cpp.

83 {
84 switch (c) {
85 case '.':
86 case '^':
87 case '$':
88 case '|':
89 case '(':
90 case ')':
91 case '[':
92 case ']':
93 case '{':
94 case '}':
95 case '*':
96 case '+':
97 case '?':
98 case '\\':
99 return true;
100 default:
101 return false;
102 }
103 }

Referenced by appendEscapedLiteral().

Here is the caller graph for this function:

◆ joinMountPath()

std::string express::dispatcher::joinMountPath ( std::string_view parentMountPath,
std::string_view relativeMountPath )

Definition at line 597 of file regex_utils.cpp.

597 {
598 if (parentMountPath.empty()) {
599 return std::string(relativeMountPath);
600 }
601 if (relativeMountPath.empty()) {
602 return std::string(parentMountPath);
603 }
604
605 const bool parentSlash = (!parentMountPath.empty() && parentMountPath.back() == '/');
606 const bool relSlash = (!relativeMountPath.empty() && relativeMountPath.front() == '/');
607
608 if (parentSlash && relSlash) {
609 // Special-case root: "/" + "/x" must become "/x", not "//x".
610 if (parentMountPath.size() == 1) {
611 return std::string(relativeMountPath);
612 }
613 return std::string(parentMountPath) + std::string(relativeMountPath.substr(1));
614 }
615 if (!parentSlash && !relSlash) {
616 return std::string(parentMountPath) + "/" + std::string(relativeMountPath);
617 }
618 return std::string(parentMountPath) + std::string(relativeMountPath);
619 }

Referenced by express::dispatcher::ApplicationDispatcher::getRoutes(), express::dispatcher::MiddlewareDispatcher::getRoutes(), and express::dispatcher::ScopedPathStrip::ScopedPathStrip().

Here is the caller graph for this function:

◆ makeInnerGroupsNonCapturing()

std::string express::dispatcher::makeInnerGroupsNonCapturing ( std::string_view pattern)
inline

Definition at line 168 of file regex_utils.cpp.

168 {
169 std::string out;
170 out.reserve(pattern.size());
171
172 bool escaped = false;
173 bool inCharClass = false;
174
175 for (std::size_t i = 0; i < pattern.size(); ++i) {
176 const char c = pattern[i];
177
178 if (escaped) {
179 out.push_back(c);
180 escaped = false;
181 continue;
182 }
183
184 if (c == '\\') {
185 out.push_back(c);
186 escaped = true;
187 continue;
188 }
189
190 if (inCharClass) {
191 out.push_back(c);
192 if (c == ']') {
193 inCharClass = false;
194 }
195 continue;
196 }
197
198 if (c == '[') {
199 out.push_back(c);
200 inCharClass = true;
201 continue;
202 }
203
204 if (c == '(') {
205 // Preserve special constructs like (?:...), (?=...), (?!...), (?<=...), etc.
206 if ((i + 1) < pattern.size() && pattern[i + 1] == '?') {
207 out.push_back('(');
208 } else {
209 out.append("(?:");
210 }
211 continue;
212 }
213
214 out.push_back(c);
215 }
216
217 return out;
218 }

Referenced by compileParamRegex().

Here is the caller graph for this function:

◆ matchAndFillParamsAndConsume()

bool express::dispatcher::matchAndFillParamsAndConsume ( const std::regex & rx,
const std::vector< std::string > & names,
std::string_view reqPath,
std::map< std::string, std::string > & params,
std::size_t & consumedLength,
bool & decodeError )
inline

Definition at line 283 of file regex_utils.cpp.

288 {
289 decodeError = false;
290 std::cmatch m;
291 if (!std::regex_search(reqPath.begin(), reqPath.end(), m, rx)) {
292 consumedLength = 0;
293 return false;
294 }
295 consumedLength = static_cast<std::size_t>(m.length(0));
296 const size_t g = (!m.empty()) ? (m.size() - 1) : 0;
297 const size_t n = std::min(names.size(), g);
298 std::string decoded;
299 for (size_t i = 0; i < n; ++i) {
300 if (!names[i].empty() && m[i + 1].matched) {
301 if (!decodeURIComponent(m[i + 1].str(), decoded)) {
302 decodeError = true;
303 params.clear();
304 return true;
305 }
306 params[names[i]] = decoded;
307 }
308 }
309 return true;
310 }
bool decodeURIComponent(std::string_view in, std::string &out)

References decodeURIComponent().

Referenced by matchMountPointImpl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ matchMountPoint()

MountMatchResult express::dispatcher::matchMountPoint ( express::Controller & controller,
const std::string & absoluteMountPath,
const express::MountPoint & mountPoint,
std::regex & cachedRegex,
std::vector< std::string > & cachedNames,
bool strictRouting,
bool caseInsensitiveRouting )

Definition at line 701 of file regex_utils.cpp.

707 {
708 return matchMountPointImpl(
709 controller, absoluteMountPath, mountPoint, &cachedRegex, &cachedNames, strictRouting, caseInsensitiveRouting);
710 }
static MountMatchResult matchMountPointImpl(express::Controller &controller, const std::string &absoluteMountPath, const express::MountPoint &mountPoint, std::regex *cachedRegex, std::vector< std::string > *cachedNames, bool strictRouting, bool caseInsensitiveRouting)

References matchMountPointImpl().

Referenced by express::dispatcher::ApplicationDispatcher::dispatch(), express::dispatcher::MiddlewareDispatcher::dispatch(), and express::dispatcher::RouterDispatcher::dispatch().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ matchMountPointImpl()

MountMatchResult express::dispatcher::matchMountPointImpl ( express::Controller & controller,
const std::string & absoluteMountPath,
const express::MountPoint & mountPoint,
std::regex * cachedRegex,
std::vector< std::string > * cachedNames,
bool strictRouting,
bool caseInsensitiveRouting )
static

Definition at line 621 of file regex_utils.cpp.

627 {
628 MountMatchResult result;
629 result.isPrefix = (mountPoint.method == "use");
630
631 // Split mount & request into path + query
632 std::string_view mountPath;
633 std::string_view ignoredMountQuery;
634 splitPathAndQuery(absoluteMountPath, mountPath, ignoredMountQuery);
635 std::string_view requestPath;
636 std::string_view requestQueryString;
637 splitPathAndQuery(controller.getRequest()->url, requestPath, requestQueryString);
638 result.requestQueryPairs = parseQuery(requestQueryString);
639
640 // Normalize single trailing slash if not strict
641 if (!strictRouting) {
642 mountPath = trimOneTrailingSlash(mountPath);
643 requestPath = trimOneTrailingSlash(requestPath);
644 }
645 if (mountPath.empty()) {
646 mountPath = "/";
647 }
648
649 result.requestPath = requestPath;
650
651 bool pathMatches = false;
652
653 if (routeNeedsRegex(mountPath)) {
654 bool decodeError = false;
655 std::size_t matchLen = 0;
656
657 // Param mount: optionally compile once, match once, fill params, and record matched prefix length
658 if (cachedRegex != nullptr && cachedNames != nullptr) {
659 if (cachedNames->empty()) {
660 auto compiled = compileParamRegex(mountPath,
661 /*isPrefix*/ result.isPrefix,
662 strictRouting,
663 caseInsensitiveRouting);
664 *cachedRegex = std::move(compiled.first);
665 *cachedNames = std::move(compiled.second);
666 }
667 pathMatches = matchAndFillParamsAndConsume(*cachedRegex, *cachedNames, requestPath, result.params, matchLen, decodeError);
668 } else {
669 auto [rx, names] = compileParamRegex(mountPath,
670 /*isPrefix*/ result.isPrefix,
671 strictRouting,
672 caseInsensitiveRouting);
673 pathMatches = matchAndFillParamsAndConsume(rx, names, requestPath, result.params, matchLen, decodeError);
674 }
675
676 if (pathMatches) {
677 result.decodeError = decodeError;
678 }
679
680 if (pathMatches && result.isPrefix) {
681 result.consumedLength = matchLen;
682 }
683 } else {
684 if (result.isPrefix) {
685 // Literal boundary prefix
686 pathMatches = boundaryPrefix(requestPath, mountPath, caseInsensitiveRouting);
687 if (pathMatches) {
688 result.consumedLength = (mountPath.size() == 1 && mountPath[0] == '/') ? 0 : mountPath.size();
689 }
690 } else {
691 // End-anchored equality
692 pathMatches = equalPath(requestPath, mountPath, caseInsensitiveRouting);
693 }
694 }
695
696 result.requestMatched = pathMatches;
697
698 return result;
699 }
const std::shared_ptr< Request > & getRequest() const
bool routeNeedsRegex(std::string_view path)
bool matchAndFillParamsAndConsume(const std::regex &rx, const std::vector< std::string > &names, std::string_view reqPath, std::map< std::string, std::string > &params, std::size_t &consumedLength, bool &decodeError)
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::unordered_map< std::string, std::string > parseQuery(std::string_view qs)
std::string_view trimOneTrailingSlash(std::string_view s)
bool equalPath(std::string_view a, std::string_view b, bool caseInsensitive)
std::string method
Definition MountPoint.h:56
std::map< std::string, std::string > params
Definition regex_utils.h:83
std::unordered_map< std::string, std::string > requestQueryPairs
Definition regex_utils.h:84

References boundaryPrefix(), compileParamRegex(), express::dispatcher::MountMatchResult::consumedLength, express::dispatcher::MountMatchResult::decodeError, equalPath(), express::Controller::getRequest(), express::dispatcher::MountMatchResult::isPrefix, matchAndFillParamsAndConsume(), express::MountPoint::method, express::dispatcher::MountMatchResult::params, parseQuery(), express::dispatcher::MountMatchResult::requestMatched, express::dispatcher::MountMatchResult::requestPath, express::dispatcher::MountMatchResult::requestQueryPairs, routeNeedsRegex(), splitPathAndQuery(), trimOneTrailingSlash(), and express::Request::url.

Referenced by matchMountPoint().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ methodMatches()

bool express::dispatcher::methodMatches ( std::string_view requestMethod,
const std::string & mountMethod )

Definition at line 589 of file regex_utils.cpp.

589 {
590 // Express v4: HEAD falls back to GET handlers if no explicit HEAD was mounted before.
591 if (requestMethod == "HEAD" && mountMethod == "GET") {
592 return true;
593 }
594 return (mountMethod == "use") || (mountMethod == "all") || (requestMethod == mountMethod);
595 }

Referenced by express::dispatcher::ApplicationDispatcher::dispatch(), express::dispatcher::MiddlewareDispatcher::dispatch(), and express::dispatcher::RouterDispatcher::dispatch().

Here is the caller graph for this function:

◆ parseQuery()

std::unordered_map< std::string, std::string > express::dispatcher::parseQuery ( std::string_view qs)
inline

Definition at line 312 of file regex_utils.cpp.

312 {
313 std::unordered_map<std::string, std::string> m;
314 std::size_t i = 0;
315 while (i < qs.size()) {
316 const std::size_t amp = qs.find('&', i);
317 const std::string_view pair = (amp == std::string_view::npos) ? qs.substr(i) : qs.substr(i, amp - i);
318 const std::size_t eq = pair.find('=');
319
320 const std::string_view rawKey = (eq == std::string_view::npos) ? pair : pair.substr(0, eq);
321 const std::string_view rawVal = (eq == std::string_view::npos) ? std::string_view{} : pair.substr(eq + 1);
322
323 const std::string key = decodeQueryComponent(rawKey);
324 const std::string val = decodeQueryComponent(rawVal);
325
326 if (!key.empty()) {
327 m[key] = val; // last value wins
328 }
329
330 if (amp == std::string_view::npos) {
331 break;
332 }
333 i = amp + 1;
334 }
335 return m;
336 }
std::string decodeQueryComponent(std::string_view in)

References decodeQueryComponent().

Referenced by matchMountPointImpl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ routeNeedsRegex()

bool express::dispatcher::routeNeedsRegex ( std::string_view path)
inline

Definition at line 65 of file regex_utils.cpp.

65 {
66 for (const char c : path) {
67 switch (c) {
68 case ':':
69 case '*':
70 case '?':
71 case '+':
72 case '(':
73 case ')':
74 case '\\':
75 return true;
76 default:
77 break;
78 }
79 }
80 return false;
81 }

Referenced by matchMountPointImpl().

Here is the caller graph for this function:

◆ splitPathAndQuery()

void express::dispatcher::splitPathAndQuery ( std::string_view url,
std::string_view & path,
std::string_view & query )
inline

Definition at line 578 of file regex_utils.cpp.

578 {
579 const std::size_t qpos = url.find('?');
580 if (qpos == std::string_view::npos) {
581 path = url;
582 query = {};
583 } else {
584 path = url.substr(0, qpos);
585 query = url.substr(qpos + 1);
586 }
587 }

Referenced by matchMountPointImpl(), and express::dispatcher::ScopedPathStrip::ScopedPathStrip().

Here is the caller graph for this function:

◆ trimOneTrailingSlash()

std::string_view express::dispatcher::trimOneTrailingSlash ( std::string_view s)
inline

Definition at line 571 of file regex_utils.cpp.

571 {
572 if (s.size() > 1 && s.back() == '/') {
573 return std::string_view(s.data(), s.size() - 1);
574 }
575 return s;
576 }

Referenced by matchMountPointImpl(), and express::dispatcher::ScopedPathStrip::ScopedPathStrip().

Here is the caller graph for this function: