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