dispatch.h 12 KB

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