restful.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. // Licensed to the Apache Software Foundation (ASF) under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. The ASF licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing,
  12. // software distributed under the License is distributed on an
  13. // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. // KIND, either express or implied. See the License for the
  15. // specific language governing permissions and limitations
  16. // under the License.
  17. #include <google/protobuf/descriptor.h>
  18. #include "brpc/log.h"
  19. #include "brpc/restful.h"
  20. #include "brpc/details/method_status.h"
  21. namespace brpc {
  22. // Define in http_parser.cpp
  23. extern bool is_url_char(char c);
  24. inline butil::StringPiece remove_last_char(butil::StringPiece s) {
  25. if (!s.empty()) {
  26. s.remove_suffix(1);
  27. }
  28. return s;
  29. }
  30. std::ostream& operator<<(std::ostream& os, const RestfulMethodPath& p) {
  31. if (!p.service_name.empty()) {
  32. os << '/' << p.service_name;
  33. }
  34. if (p.has_wildcard) {
  35. os << p.prefix << '*' << remove_last_char(p.postfix);
  36. } else {
  37. os << remove_last_char(p.prefix);
  38. }
  39. return os;
  40. }
  41. std::string RestfulMethodPath::to_string() const {
  42. std::string s;
  43. s.reserve(service_name.size() + prefix.size() + 2 + postfix.size());
  44. if (!service_name.empty()) {
  45. s.push_back('/');
  46. s.append(service_name);
  47. }
  48. if (has_wildcard) {
  49. s.append(prefix);
  50. s.push_back('*');
  51. butil::StringPiece tmp = remove_last_char(postfix);
  52. s.append(tmp.data(), tmp.size());
  53. } else {
  54. butil::StringPiece tmp = remove_last_char(prefix);
  55. s.append(tmp.data(), tmp.size());
  56. }
  57. return s;
  58. }
  59. struct DebugPrinter {
  60. explicit DebugPrinter(const RestfulMethodPath& p) : path(&p) {}
  61. const RestfulMethodPath* path;
  62. };
  63. std::ostream& operator<<(std::ostream& os, const DebugPrinter& p) {
  64. os << "{service=" << p.path->service_name
  65. << " prefix=" << p.path->prefix
  66. << " postfix=" << p.path->postfix
  67. << " wildcard=" << p.path->has_wildcard
  68. << '}';
  69. return os;
  70. }
  71. bool ParseRestfulPath(butil::StringPiece path,
  72. RestfulMethodPath* path_out) {
  73. path.trim_spaces();
  74. if (path.empty()) {
  75. LOG(ERROR) << "Parameter[path] is empty";
  76. return false;
  77. }
  78. // Check validity of the path.
  79. // TODO(gejun): Probably too strict.
  80. int star_index = -1;
  81. for (const char* p = path.data(); p != path.data() + path.size(); ++p) {
  82. if (*p == '*') {
  83. if (star_index < 0) {
  84. star_index = (int)(p - path.data());
  85. } else {
  86. LOG(ERROR) << "More than one wildcard in restful_path=`"
  87. << path << '\'';
  88. return false;
  89. }
  90. } else if (!is_url_char(*p)) {
  91. LOG(ERROR) << "Invalid character=`" << *p << "' (index="
  92. << p - path.data() << ") in path=`" << path << '\'';
  93. return false;
  94. }
  95. }
  96. path_out->has_wildcard = (star_index >= 0);
  97. butil::StringPiece first_part;
  98. butil::StringPiece second_part;
  99. if (star_index < 0) {
  100. first_part = path;
  101. } else {
  102. first_part = path.substr(0, star_index);
  103. second_part = path.substr(star_index + 1);
  104. }
  105. // Extract service_name and prefix from first_part
  106. // The prefix is normalized as:
  107. // / - "*B => M"
  108. // /A - "/A*B => M" (disabled for performance)
  109. // /A/ - "/A/*B => M"
  110. path_out->service_name.clear();
  111. path_out->prefix.clear();
  112. {
  113. // remove heading slashes.
  114. size_t i = 0;
  115. for (; i < first_part.size() && first_part[i] == '/'; ++i) {}
  116. first_part.remove_prefix(i);
  117. }
  118. const size_t slash_pos = first_part.find('/');
  119. if (slash_pos != butil::StringPiece::npos) {
  120. path_out->service_name.assign(first_part.data(), slash_pos);
  121. butil::StringPiece prefix_raw = first_part.substr(slash_pos + 1);
  122. butil::StringSplitter sp(prefix_raw.data(),
  123. prefix_raw.data() + prefix_raw.size(), '/');
  124. for (; sp; ++sp) {
  125. // Put first component into service_name and others into prefix.
  126. if (path_out->prefix.empty()) {
  127. path_out->prefix.reserve(prefix_raw.size() + 2);
  128. }
  129. path_out->prefix.push_back('/');
  130. path_out->prefix.append(sp.field(), sp.length());
  131. }
  132. if (!path_out->has_wildcard ||
  133. prefix_raw.empty() ||
  134. prefix_raw.back() == '/') {
  135. path_out->prefix.push_back('/');
  136. } else {
  137. LOG(ERROR) << "Pattern A* (A is not ended with /) in path=`"
  138. << path << "' is disallowed for performance concerns";
  139. return false;
  140. }
  141. } else if (!path_out->has_wildcard) {
  142. // no slashes, no wildcard. Example: abc => Method
  143. path_out->service_name.assign(first_part.data(), first_part.size());
  144. path_out->prefix.push_back('/');
  145. } else { // no slashes, has wildcard. Example: abc* => Method
  146. if (!first_part.empty()) {
  147. LOG(ERROR) << "Pattern A* (A is not ended with /) in path=`"
  148. << path << "' is disallowed for performance concerns";
  149. return false;
  150. }
  151. path_out->prefix.push_back('/');
  152. path_out->prefix.append(first_part.data(), first_part.size());
  153. }
  154. // Normalize second_part as postfix:
  155. // / - "A* => M" or "A => M"
  156. // B/ - "A*B => M"
  157. // /B/ - "A*/B => M"
  158. path_out->postfix.clear();
  159. if (path_out->has_wildcard) {
  160. if (second_part.empty() || second_part[0] == '/') {
  161. path_out->postfix.push_back('/');
  162. }
  163. butil::StringSplitter sp2(second_part.data(),
  164. second_part.data() + second_part.size(), '/');
  165. for (; sp2; ++sp2) {
  166. if (path_out->postfix.empty()) {
  167. path_out->postfix.reserve(second_part.size() + 2);
  168. }
  169. path_out->postfix.append(sp2.field(), sp2.length());
  170. path_out->postfix.push_back('/');
  171. }
  172. } else {
  173. path_out->postfix.push_back('/');
  174. }
  175. VLOG(RPC_VLOG_LEVEL + 1) << "orig_path=" << path
  176. << " first_part=" << first_part
  177. << " second_part=" << second_part
  178. << " path=" << DebugPrinter(*path_out);
  179. return true;
  180. }
  181. bool ParseRestfulMappings(const butil::StringPiece& mappings,
  182. std::vector<RestfulMapping>* list) {
  183. if (list == NULL) {
  184. LOG(ERROR) << "Param[list] is NULL";
  185. return false;
  186. }
  187. list->clear();
  188. list->reserve(8);
  189. butil::StringSplitter sp(
  190. mappings.data(), mappings.data() + mappings.size(), ',');
  191. int nmappings = 0;
  192. for (; sp; ++sp) {
  193. ++nmappings;
  194. size_t i = 0;
  195. const char* p = sp.field();
  196. const size_t n = sp.length();
  197. bool added_sth = false;
  198. for (; i < n; ++i) {
  199. // find =
  200. if (p[i] != '=') {
  201. continue;
  202. }
  203. const size_t equal_sign_pos = i;
  204. for (; i < n && p[i] == '='; ++i) {} // skip repeated =
  205. // If the = ends with >, it's the arrow that we're finding.
  206. // otherwise just skip and keep searching.
  207. if (i < n && p[i] == '>') {
  208. RestfulMapping m;
  209. // Parse left part of the arrow as url path.
  210. butil::StringPiece path(sp.field(), equal_sign_pos);
  211. if (!ParseRestfulPath(path, &m.path)) {
  212. LOG(ERROR) << "Fail to parse path=`" << path << '\'';
  213. return false;
  214. }
  215. // Treat right part of the arrow as method_name.
  216. butil::StringPiece method_name_piece(p + i + 1, n - (i + 1));
  217. method_name_piece.trim_spaces();
  218. if (method_name_piece.empty()) {
  219. LOG(ERROR) << "No method name in " << nmappings
  220. << "-th mapping";
  221. return false;
  222. }
  223. m.method_name.assign(method_name_piece.data(),
  224. method_name_piece.size());
  225. list->push_back(m);
  226. added_sth = true;
  227. break;
  228. }
  229. }
  230. // If we don't get a valid mapping from the string, issue error.
  231. if (!added_sth) {
  232. LOG(ERROR) << "Invalid mapping: "
  233. << butil::StringPiece(sp.field(), sp.length());
  234. return false;
  235. }
  236. }
  237. return true;
  238. }
  239. RestfulMap::~RestfulMap() {
  240. ClearMethods();
  241. }
  242. // This function inserts a mapping into _dedup_map.
  243. bool RestfulMap::AddMethod(const RestfulMethodPath& path,
  244. google::protobuf::Service* service,
  245. const Server::MethodProperty::OpaqueParams& params,
  246. const std::string& method_name,
  247. MethodStatus* status) {
  248. if (service == NULL) {
  249. LOG(ERROR) << "Param[service] is NULL";
  250. return false;
  251. }
  252. const google::protobuf::MethodDescriptor* md =
  253. service->GetDescriptor()->FindMethodByName(method_name);
  254. if (md == NULL) {
  255. LOG(ERROR) << service->GetDescriptor()->full_name()
  256. << " has no method called `" << method_name << '\'';
  257. return false;
  258. }
  259. if (path.service_name != _service_name) {
  260. LOG(ERROR) << "Impossible: path.service_name does not match name"
  261. " of this RestfulMap";
  262. return false;
  263. }
  264. // Use the string-form of path as key is a MUST to implement
  265. // RemoveByPathString which is used in Server.RemoveMethodsOf
  266. std::string dedup_key = path.to_string();
  267. DedupMap::const_iterator it = _dedup_map.find(dedup_key);
  268. if (it != _dedup_map.end()) {
  269. LOG(ERROR) << "Already mapped `" << it->second.path
  270. << "' to `" << it->second.method->full_name() << '\'';
  271. return false;
  272. }
  273. RestfulMethodProperty& info = _dedup_map[dedup_key];
  274. info.is_builtin_service = false;
  275. info.own_method_status = false;
  276. info.params = params;
  277. info.service = service;
  278. info.method = md;
  279. info.status = status;
  280. info.path = path;
  281. info.ownership = SERVER_DOESNT_OWN_SERVICE;
  282. RPC_VLOG << "Mapped `" << path << "' to `" << md->full_name() << '\'';
  283. return true;
  284. }
  285. void RestfulMap::ClearMethods() {
  286. _sorted_paths.clear();
  287. for (DedupMap::iterator it = _dedup_map.begin();
  288. it != _dedup_map.end(); ++it) {
  289. if (it->second.own_method_status) {
  290. delete it->second.status;
  291. }
  292. }
  293. _dedup_map.clear();
  294. }
  295. struct CompareItemInPathList {
  296. bool operator()(const RestfulMethodProperty* e1,
  297. const RestfulMethodProperty* e2) const {
  298. const int rc1 = e1->path.prefix.compare(e2->path.prefix);
  299. if (rc1 != 0) {
  300. return rc1 < 0;
  301. }
  302. // /A/*/B is put before /A/B so that we try exact patterns first
  303. // (the matching is in reversed order)
  304. if (e1->path.has_wildcard != e2->path.has_wildcard) {
  305. return e1->path.has_wildcard > e2->path.has_wildcard;
  306. }
  307. // Compare postfix from back to front.
  308. // TODO: Optimize this.
  309. std::string::const_reverse_iterator it1 = e1->path.postfix.rbegin();
  310. std::string::const_reverse_iterator it2 = e2->path.postfix.rbegin();
  311. while (it1 != e1->path.postfix.rend() &&
  312. it2 != e2->path.postfix.rend()) {
  313. if (*it1 != *it2) {
  314. return (*it1 < *it2);
  315. }
  316. ++it1;
  317. ++it2;
  318. }
  319. return (it1 == e1->path.postfix.rend())
  320. > (it2 == e2->path.postfix.rend());
  321. }
  322. };
  323. void RestfulMap::PrepareForFinding() {
  324. _sorted_paths.clear();
  325. _sorted_paths.reserve(_dedup_map.size());
  326. for (DedupMap::iterator it = _dedup_map.begin(); it != _dedup_map.end();
  327. ++it) {
  328. _sorted_paths.push_back(&it->second);
  329. }
  330. std::sort(_sorted_paths.begin(), _sorted_paths.end(),
  331. CompareItemInPathList());
  332. if (VLOG_IS_ON(RPC_VLOG_LEVEL + 1)) {
  333. std::ostringstream os;
  334. os << "_sorted_paths(" << _service_name << "):";
  335. for (PathList::const_iterator it = _sorted_paths.begin();
  336. it != _sorted_paths.end(); ++it) {
  337. os << ' ' << (*it)->path;
  338. }
  339. VLOG(RPC_VLOG_LEVEL + 1) << os.str();
  340. }
  341. }
  342. // Remove last component from the (normalized) path:
  343. // Say /A/B/C/ -> /A/B/
  344. // Notice that /A/ is modified to / and returns true.
  345. static bool RemoveLastComponent(butil::StringPiece* path) {
  346. if (path->empty()) {
  347. return false;
  348. }
  349. if (path->back() == '/') {
  350. path->remove_suffix(1);
  351. }
  352. size_t slash_pos = path->rfind('/');
  353. if (slash_pos == std::string::npos) {
  354. return false;
  355. }
  356. path->remove_suffix(path->size() - slash_pos - 1); // keep the slash
  357. return true;
  358. }
  359. // Normalized as /A/B/C/
  360. static std::string NormalizeSlashes(const butil::StringPiece& path) {
  361. std::string out_path;
  362. out_path.reserve(path.size() + 2);
  363. butil::StringSplitter sp(path.data(), path.data() + path.size(), '/');
  364. for (; sp; ++sp) {
  365. out_path.push_back('/');
  366. out_path.append(sp.field(), sp.length());
  367. }
  368. out_path.push_back('/');
  369. return out_path;
  370. }
  371. size_t RestfulMap::RemoveByPathString(const std::string& path) {
  372. // removal only happens when server stops, clear _sorted_paths to make
  373. // sure wild pointers do not exist.
  374. if (!_sorted_paths.empty()) {
  375. _sorted_paths.clear();
  376. }
  377. return _dedup_map.erase(path);
  378. }
  379. struct PrefixLess {
  380. bool operator()(const butil::StringPiece& path,
  381. const RestfulMethodProperty* p) const {
  382. return path < p->path.prefix;
  383. }
  384. };
  385. const Server::MethodProperty*
  386. RestfulMap::FindMethodProperty(const butil::StringPiece& method_path,
  387. std::string* unresolved_path) const {
  388. if (_sorted_paths.empty()) {
  389. LOG(ERROR) << "_sorted_paths is empty, method_path=" << method_path;
  390. return NULL;
  391. }
  392. const std::string full_path = NormalizeSlashes(method_path);
  393. butil::StringPiece sub_path = full_path;
  394. PathList::const_iterator last_find_pos = _sorted_paths.end();
  395. do {
  396. if (last_find_pos == _sorted_paths.begin()) {
  397. return NULL;
  398. }
  399. // Note: stop trying places that we already visited or skipped.
  400. PathList::const_iterator it =
  401. std::upper_bound(_sorted_paths.begin(), last_find_pos/*note*/,
  402. sub_path, PrefixLess());
  403. if (it != _sorted_paths.begin()) {
  404. --it;
  405. }
  406. bool matched = false;
  407. bool remove_heading_slash_from_unresolved = false;
  408. butil::StringPiece left;
  409. do {
  410. const RestfulMethodPath& rpath = (*it)->path;
  411. if (!sub_path.starts_with(rpath.prefix)) {
  412. VLOG(RPC_VLOG_LEVEL + 1)
  413. << "sub_path=" << sub_path << " does not match prefix="
  414. << rpath.prefix << " full_path=" << full_path
  415. << " candidate=" << DebugPrinter(rpath);
  416. // NOTE: We can stop trying patterns before *it because pattern
  417. // "/A*B => M" is disabled which makes prefixes of all restful
  418. // paths end with /. If `full_path' matches with a prefix, the
  419. // prefix must be a sub path of the full_path, which makes
  420. // prefix matching runs at most #components-of-path times.
  421. // Otherwise we have to match all "/A*B" patterns before *it,
  422. // which is more complicated but rarely needed by users.
  423. break;
  424. }
  425. left = full_path;
  426. // Remove matched prefix from `left'.
  427. if (!rpath.prefix.empty()) {
  428. // make sure `left' is still starting with /
  429. size_t removal = rpath.prefix.size();
  430. if (rpath.prefix[removal - 1] == '/') {
  431. --removal;
  432. remove_heading_slash_from_unresolved = true;
  433. }
  434. left.remove_prefix(removal);
  435. }
  436. // Match postfix.
  437. if (left.ends_with(rpath.postfix)) {
  438. left.remove_suffix(rpath.postfix.size());
  439. if (!left.empty() && !rpath.has_wildcard) {
  440. VLOG(RPC_VLOG_LEVEL + 1)
  441. << "Unmatched extra=" << left
  442. << " sub_path=" << sub_path
  443. << " full_path=" << full_path
  444. << " candidate=" << DebugPrinter(rpath);
  445. } else {
  446. matched = true;
  447. VLOG(RPC_VLOG_LEVEL + 1)
  448. << "Matched sub_path=" << sub_path
  449. << " full_path=" << full_path
  450. << " with restful_path=" << DebugPrinter(rpath);
  451. break;
  452. }
  453. }
  454. if (it == _sorted_paths.begin()) {
  455. VLOG(RPC_VLOG_LEVEL + 1)
  456. << "Hit beginning, sub_path=" << sub_path
  457. << " full_path=" << full_path
  458. << " candidate=" << DebugPrinter(rpath);
  459. return NULL;
  460. }
  461. // Matched with prefix but postfix or wildcard, moving forward
  462. --it;
  463. } while (true);
  464. last_find_pos = it;
  465. if (!matched) {
  466. continue;
  467. }
  468. if (unresolved_path) {
  469. if (!left.empty()) {
  470. if (remove_heading_slash_from_unresolved && left[0] == '/') {
  471. unresolved_path->assign(left.data() + 1, left.size() - 1);
  472. } else {
  473. unresolved_path->assign(left.data(), left.size());
  474. }
  475. } else {
  476. unresolved_path->clear();
  477. }
  478. }
  479. return *it;
  480. } while (RemoveLastComponent(&sub_path));
  481. // ^^^^^^^^
  482. // sub_path can be / to match patterns like "*.flv => M" whose prefix is /
  483. return NULL;
  484. }
  485. } // namespace brpc