dispatch.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef CRDTP_DISPATCH_H_
  5. #define CRDTP_DISPATCH_H_
  6. #include <cassert>
  7. #include <cstdint>
  8. #include <functional>
  9. #include <string>
  10. #include <unordered_set>
  11. #include "export.h"
  12. #include "serializable.h"
  13. #include "span.h"
  14. #include "status.h"
  15. namespace crdtp {
  16. class DeserializerState;
  17. class ErrorSupport;
  18. class FrontendChannel;
  19. namespace cbor {
  20. class CBORTokenizer;
  21. } // namespace cbor
  22. // =============================================================================
  23. // DispatchResponse - Error status and chaining / fall through
  24. // =============================================================================
  25. enum class DispatchCode {
  26. SUCCESS = 1,
  27. FALL_THROUGH = 2,
  28. // For historical reasons, these error codes correspond to commonly used
  29. // XMLRPC codes (e.g. see METHOD_NOT_FOUND in
  30. // https://github.com/python/cpython/blob/master/Lib/xmlrpc/client.py).
  31. PARSE_ERROR = -32700,
  32. INVALID_REQUEST = -32600,
  33. METHOD_NOT_FOUND = -32601,
  34. INVALID_PARAMS = -32602,
  35. INTERNAL_ERROR = -32603,
  36. SERVER_ERROR = -32000,
  37. };
  38. // Information returned by command handlers. Usually returned after command
  39. // execution attempts.
  40. class CRDTP_EXPORT DispatchResponse {
  41. public:
  42. const std::string& Message() const { return message_; }
  43. DispatchCode Code() const { return code_; }
  44. bool IsSuccess() const { return code_ == DispatchCode::SUCCESS; }
  45. bool IsFallThrough() const { return code_ == DispatchCode::FALL_THROUGH; }
  46. bool IsError() const { return code_ < DispatchCode::SUCCESS; }
  47. static DispatchResponse Success();
  48. static DispatchResponse FallThrough();
  49. // Indicates that a message could not be parsed. E.g., malformed JSON.
  50. static DispatchResponse ParseError(std::string message);
  51. // Indicates that a request is lacking required top-level properties
  52. // ('id', 'method'), has top-level properties of the wrong type, or has
  53. // unknown top-level properties.
  54. static DispatchResponse InvalidRequest(std::string message);
  55. // Indicates that a protocol method such as "Page.bringToFront" could not be
  56. // dispatched because it's not known to the (domain) dispatcher.
  57. static DispatchResponse MethodNotFound(std::string message);
  58. // Indicates that the params sent to a domain handler are invalid.
  59. static DispatchResponse InvalidParams(std::string message);
  60. // Used for application level errors, e.g. within protocol agents.
  61. static DispatchResponse InternalError();
  62. // Used for application level errors, e.g. within protocol agents.
  63. static DispatchResponse ServerError(std::string message);
  64. private:
  65. DispatchResponse() = default;
  66. DispatchCode code_;
  67. std::string message_;
  68. };
  69. // =============================================================================
  70. // Dispatchable - a shallow parser for CBOR encoded DevTools messages
  71. // =============================================================================
  72. // This parser extracts only the known top-level fields from a CBOR encoded map;
  73. // method, id, sessionId, and params.
  74. class CRDTP_EXPORT Dispatchable {
  75. public:
  76. // This constructor parses the |serialized| message. If successful,
  77. // |ok()| will yield |true|, and |Method()|, |SessionId()|, |CallId()|,
  78. // |Params()| can be used to access, the extracted contents. Otherwise,
  79. // |ok()| will yield |false|, and |DispatchError()| can be
  80. // used to send a response or notification to the client.
  81. explicit Dispatchable(span<uint8_t> serialized);
  82. // The serialized message that we just parsed.
  83. span<uint8_t> Serialized() const { return serialized_; }
  84. // Yields true if parsing was successful. This is cheaper than calling
  85. // ::DispatchError().
  86. bool ok() const;
  87. // If !ok(), returns a DispatchResponse with appropriate code and error
  88. // which can be sent to the client as a response or notification.
  89. DispatchResponse DispatchError() const;
  90. // Top level field: the command to be executed, fully qualified by
  91. // domain. E.g. "Page.createIsolatedWorld".
  92. span<uint8_t> Method() const { return method_; }
  93. // Used to identify protocol connections attached to a specific
  94. // target. See Target.attachToTarget, Target.setAutoAttach.
  95. span<uint8_t> SessionId() const { return session_id_; }
  96. // The call id, a sequence number that's used in responses to indicate
  97. // the request to which the response belongs.
  98. int32_t CallId() const { return call_id_; }
  99. bool HasCallId() const { return has_call_id_; }
  100. // The payload of the request in CBOR format. The |Dispatchable| parser does
  101. // not parse into this; it only provides access to its raw contents here.
  102. span<uint8_t> Params() const { return params_; }
  103. private:
  104. bool MaybeParseProperty(cbor::CBORTokenizer* tokenizer);
  105. bool MaybeParseCallId(cbor::CBORTokenizer* tokenizer);
  106. bool MaybeParseMethod(cbor::CBORTokenizer* tokenizer);
  107. bool MaybeParseParams(cbor::CBORTokenizer* tokenizer);
  108. bool MaybeParseSessionId(cbor::CBORTokenizer* tokenizer);
  109. span<uint8_t> serialized_;
  110. Status status_;
  111. bool has_call_id_ = false;
  112. int32_t call_id_;
  113. span<uint8_t> method_;
  114. bool params_seen_ = false;
  115. span<uint8_t> params_;
  116. span<uint8_t> session_id_;
  117. };
  118. // =============================================================================
  119. // Helpers for creating protocol cresponses and notifications.
  120. // =============================================================================
  121. // The resulting notifications can be sent to a protocol client,
  122. // usually via a FrontendChannel (see frontend_channel.h).
  123. CRDTP_EXPORT std::unique_ptr<Serializable> CreateErrorResponse(
  124. int callId,
  125. DispatchResponse dispatch_response,
  126. const ErrorSupport* errors = nullptr);
  127. CRDTP_EXPORT std::unique_ptr<Serializable> CreateErrorNotification(
  128. DispatchResponse dispatch_response);
  129. CRDTP_EXPORT std::unique_ptr<Serializable> CreateResponse(
  130. int callId,
  131. std::unique_ptr<Serializable> params);
  132. CRDTP_EXPORT std::unique_ptr<Serializable> CreateNotification(
  133. const char* method,
  134. std::unique_ptr<Serializable> params = nullptr);
  135. // =============================================================================
  136. // DomainDispatcher - Dispatching betwen protocol methods within a domain.
  137. // =============================================================================
  138. // This class is subclassed by |DomainDispatcherImpl|, which we generate per
  139. // DevTools domain. It contains routines called from the generated code,
  140. // e.g. ::MaybeReportInvalidParams, which are optimized for small code size.
  141. // The most important method is ::Dispatch, which implements method dispatch
  142. // by command name lookup.
  143. class CRDTP_EXPORT DomainDispatcher {
  144. public:
  145. class CRDTP_EXPORT WeakPtr {
  146. public:
  147. explicit WeakPtr(DomainDispatcher*);
  148. ~WeakPtr();
  149. DomainDispatcher* get() { return dispatcher_; }
  150. void dispose() { dispatcher_ = nullptr; }
  151. private:
  152. DomainDispatcher* dispatcher_;
  153. };
  154. class CRDTP_EXPORT Callback {
  155. public:
  156. virtual ~Callback();
  157. void dispose();
  158. protected:
  159. // |method| must point at static storage (a C++ string literal in practice).
  160. Callback(std::unique_ptr<WeakPtr> backend_impl,
  161. int call_id,
  162. span<uint8_t> method,
  163. span<uint8_t> message);
  164. void sendIfActive(std::unique_ptr<Serializable> partialMessage,
  165. const DispatchResponse& response);
  166. void fallThroughIfActive();
  167. private:
  168. std::unique_ptr<WeakPtr> backend_impl_;
  169. int call_id_;
  170. // Subclasses of this class are instantiated from generated code which
  171. // passes a string literal for the method name to the constructor. So the
  172. // storage for |method| is the binary of the running process.
  173. span<uint8_t> method_;
  174. std::vector<uint8_t> message_;
  175. };
  176. explicit DomainDispatcher(FrontendChannel*);
  177. virtual ~DomainDispatcher();
  178. // Given a |command_name| without domain qualification, looks up the
  179. // corresponding method. If the method is not found, returns nullptr.
  180. // Otherwise, Returns a closure that will parse the provided
  181. // Dispatchable.params() to a protocol object and execute the
  182. // apprpropriate method. If the parsing fails it will issue an
  183. // error response on the frontend channel, otherwise it will execute the
  184. // command.
  185. virtual std::function<void(const Dispatchable&)> Dispatch(
  186. span<uint8_t> command_name) = 0;
  187. // Sends a response to the client via the channel.
  188. void sendResponse(int call_id,
  189. const DispatchResponse&,
  190. std::unique_ptr<Serializable> result = nullptr);
  191. // Returns true if |errors| contains errors *and* reports these errors
  192. // as a response on the frontend channel. Called from generated code,
  193. // optimized for code size of the callee.
  194. bool MaybeReportInvalidParams(const Dispatchable& dispatchable,
  195. const ErrorSupport& errors);
  196. bool MaybeReportInvalidParams(const Dispatchable& dispatchable,
  197. const DeserializerState& state);
  198. FrontendChannel* channel() { return frontend_channel_; }
  199. void clearFrontend();
  200. std::unique_ptr<WeakPtr> weakPtr();
  201. private:
  202. FrontendChannel* frontend_channel_;
  203. std::unordered_set<WeakPtr*> weak_ptrs_;
  204. };
  205. // =============================================================================
  206. // UberDispatcher - dispatches between domains (backends).
  207. // =============================================================================
  208. class CRDTP_EXPORT UberDispatcher {
  209. public:
  210. // Return type for ::Dispatch.
  211. class CRDTP_EXPORT DispatchResult {
  212. public:
  213. DispatchResult(bool method_found, std::function<void()> runnable);
  214. // Indicates whether the method was found, that is, it could be dispatched
  215. // to a backend registered with this dispatcher.
  216. bool MethodFound() const { return method_found_; }
  217. // Runs the dispatched result. This will send the appropriate error
  218. // responses if the method wasn't found or if something went wrong during
  219. // parameter parsing.
  220. void Run();
  221. private:
  222. bool method_found_;
  223. std::function<void()> runnable_;
  224. };
  225. // |frontend_hannel| can't be nullptr.
  226. explicit UberDispatcher(FrontendChannel* frontend_channel);
  227. virtual ~UberDispatcher();
  228. // Dispatches the provided |dispatchable| considering all redirects and domain
  229. // handlers registered with this uber dispatcher. Also see |DispatchResult|.
  230. // |dispatchable.ok()| must hold - callers must check this separately and
  231. // deal with errors.
  232. DispatchResult Dispatch(const Dispatchable& dispatchable) const;
  233. // Invoked from generated code for wiring domain backends; that is,
  234. // connecting domain handlers to an uber dispatcher.
  235. // See <domain-namespace>::Dispatcher::Wire(UberDispatcher*,Backend*).
  236. FrontendChannel* channel() const {
  237. assert(frontend_channel_);
  238. return frontend_channel_;
  239. }
  240. // Invoked from generated code for wiring domain backends; that is,
  241. // connecting domain handlers to an uber dispatcher.
  242. // See <domain-namespace>::Dispatcher::Wire(UberDispatcher*,Backend*).
  243. void WireBackend(span<uint8_t> domain,
  244. const std::vector<std::pair<span<uint8_t>, span<uint8_t>>>&,
  245. std::unique_ptr<DomainDispatcher> dispatcher);
  246. private:
  247. DomainDispatcher* findDispatcher(span<uint8_t> method);
  248. FrontendChannel* const frontend_channel_;
  249. // Pairs of ascii strings of the form ("Domain1.method1","Domain2.method2")
  250. // indicating that the first element of each pair redirects to the second.
  251. // Sorted by first element.
  252. std::vector<std::pair<span<uint8_t>, span<uint8_t>>> redirects_;
  253. // Domain dispatcher instances, sorted by their domain name.
  254. std::vector<std::pair<span<uint8_t>, std::unique_ptr<DomainDispatcher>>>
  255. dispatchers_;
  256. };
  257. } // namespace crdtp
  258. #endif // CRDTP_DISPATCH_H_