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