| 401 | }; |
| 402 | |
| 403 | const Server::MethodProperty* |
| 404 | RestfulMap::FindMethodProperty(const butil::StringPiece& method_path, |
| 405 | std::string* unresolved_path) const { |
| 406 | if (_sorted_paths.empty()) { |
| 407 | LOG(ERROR) << "_sorted_paths is empty, method_path=" << method_path; |
| 408 | return NULL; |
| 409 | } |
| 410 | const std::string full_path = NormalizeSlashes(method_path); |
| 411 | butil::StringPiece sub_path = full_path; |
| 412 | PathList::const_iterator last_find_pos = _sorted_paths.end(); |
| 413 | do { |
| 414 | if (last_find_pos == _sorted_paths.begin()) { |
| 415 | return NULL; |
| 416 | } |
| 417 | // Note: stop trying places that we already visited or skipped. |
| 418 | PathList::const_iterator it = |
| 419 | std::upper_bound(_sorted_paths.begin(), last_find_pos/*note*/, |
| 420 | sub_path, PrefixLess()); |
| 421 | if (it != _sorted_paths.begin()) { |
| 422 | --it; |
| 423 | } |
| 424 | |
| 425 | bool matched = false; |
| 426 | bool remove_heading_slash_from_unresolved = false; |
| 427 | butil::StringPiece left; |
| 428 | do { |
| 429 | const RestfulMethodPath& rpath = (*it)->path; |
| 430 | if (!sub_path.starts_with(rpath.prefix)) { |
| 431 | VLOG(RPC_VLOG_LEVEL + 1) |
| 432 | << "sub_path=" << sub_path << " does not match prefix=" |
| 433 | << rpath.prefix << " full_path=" << full_path |
| 434 | << " candidate=" << DebugPrinter(rpath); |
| 435 | // NOTE: We can stop trying patterns before *it because pattern |
| 436 | // "/A*B => M" is disabled which makes prefixes of all restful |
| 437 | // paths end with /. If `full_path' matches with a prefix, the |
| 438 | // prefix must be a sub path of the full_path, which makes |
| 439 | // prefix matching runs at most #components-of-path times. |
| 440 | // Otherwise we have to match all "/A*B" patterns before *it, |
| 441 | // which is more complicated but rarely needed by users. |
| 442 | break; |
| 443 | } |
| 444 | left = full_path; |
| 445 | // Remove matched prefix from `left'. |
| 446 | if (!rpath.prefix.empty()) { |
| 447 | // make sure `left' is still starting with / |
| 448 | size_t removal = rpath.prefix.size(); |
| 449 | if (rpath.prefix[removal - 1] == '/') { |
| 450 | --removal; |
| 451 | remove_heading_slash_from_unresolved = true; |
| 452 | } |
| 453 | left.remove_prefix(removal); |
| 454 | } |
| 455 | // Match postfix. |
| 456 | if (left.ends_with(rpath.postfix)) { |
| 457 | left.remove_suffix(rpath.postfix.size()); |
| 458 | if (!left.empty() && !rpath.has_wildcard) { |
| 459 | VLOG(RPC_VLOG_LEVEL + 1) |
| 460 | << "Unmatched extra=" << left |
no test coverage detected