| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //#include "DispatcherBase.h" |
| //#include "Parser.h" |
| |
| {% for namespace in config.protocol.namespace %} |
| namespace {{namespace}} { |
| {% endfor %} |
| |
| // static |
| DispatchResponse DispatchResponse::OK() |
| { |
| DispatchResponse result; |
| result.m_status = kSuccess; |
| result.m_errorCode = kParseError; |
| return result; |
| } |
| |
| // static |
| DispatchResponse DispatchResponse::Error(const String& error) |
| { |
| DispatchResponse result; |
| result.m_status = kError; |
| result.m_errorCode = kServerError; |
| result.m_errorMessage = error; |
| return result; |
| } |
| |
| // static |
| DispatchResponse DispatchResponse::InternalError() |
| { |
| DispatchResponse result; |
| result.m_status = kError; |
| result.m_errorCode = kInternalError; |
| result.m_errorMessage = "Internal error"; |
| return result; |
| } |
| |
| // static |
| DispatchResponse DispatchResponse::InvalidParams(const String& error) |
| { |
| DispatchResponse result; |
| result.m_status = kError; |
| result.m_errorCode = kInvalidParams; |
| result.m_errorMessage = error; |
| return result; |
| } |
| |
| // static |
| DispatchResponse DispatchResponse::FallThrough() |
| { |
| DispatchResponse result; |
| result.m_status = kFallThrough; |
| result.m_errorCode = kParseError; |
| return result; |
| } |
| |
| // static |
| const char DispatcherBase::kInvalidParamsString[] = "Invalid parameters"; |
| |
| DispatcherBase::WeakPtr::WeakPtr(DispatcherBase* dispatcher) : m_dispatcher(dispatcher) { } |
| |
| DispatcherBase::WeakPtr::~WeakPtr() |
| { |
| if (m_dispatcher) |
| m_dispatcher->m_weakPtrs.erase(this); |
| } |
| |
| DispatcherBase::Callback::Callback(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId, const String& method, const String& message) |
| : m_backendImpl(std::move(backendImpl)) |
| , m_callId(callId) |
| , m_method(method) |
| , m_message(message) { } |
| |
| DispatcherBase::Callback::~Callback() = default; |
| |
| void DispatcherBase::Callback::dispose() |
| { |
| m_backendImpl = nullptr; |
| } |
| |
| void DispatcherBase::Callback::sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const DispatchResponse& response) |
| { |
| if (!m_backendImpl || !m_backendImpl->get()) |
| return; |
| m_backendImpl->get()->sendResponse(m_callId, response, std::move(partialMessage)); |
| m_backendImpl = nullptr; |
| } |
| |
| void DispatcherBase::Callback::fallThroughIfActive() |
| { |
| if (!m_backendImpl || !m_backendImpl->get()) |
| return; |
| m_backendImpl->get()->channel()->fallThrough(m_callId, m_method, m_message); |
| m_backendImpl = nullptr; |
| } |
| |
| DispatcherBase::DispatcherBase(FrontendChannel* frontendChannel) |
| : m_frontendChannel(frontendChannel) { } |
| |
| DispatcherBase::~DispatcherBase() |
| { |
| clearFrontend(); |
| } |
| |
| void DispatcherBase::sendResponse(int callId, const DispatchResponse& response, std::unique_ptr<protocol::DictionaryValue> result) |
| { |
| if (!m_frontendChannel) |
| return; |
| if (response.status() == DispatchResponse::kError) { |
| reportProtocolError(callId, response.errorCode(), response.errorMessage(), nullptr); |
| return; |
| } |
| m_frontendChannel->sendProtocolResponse(callId, InternalResponse::createResponse(callId, std::move(result))); |
| } |
| |
| void DispatcherBase::sendResponse(int callId, const DispatchResponse& response) |
| { |
| sendResponse(callId, response, DictionaryValue::create()); |
| } |
| |
| namespace { |
| |
| class ProtocolError : public Serializable { |
| public: |
| static std::unique_ptr<ProtocolError> createErrorResponse(int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors) |
| { |
| std::unique_ptr<ProtocolError> protocolError(new ProtocolError(code, errorMessage)); |
| protocolError->m_callId = callId; |
| protocolError->m_hasCallId = true; |
| if (errors && errors->hasErrors()) |
| protocolError->m_data = errors->errors(); |
| return protocolError; |
| } |
| |
| static std::unique_ptr<ProtocolError> createErrorNotification(DispatchResponse::ErrorCode code, const String& errorMessage) |
| { |
| return std::unique_ptr<ProtocolError>(new ProtocolError(code, errorMessage)); |
| } |
| |
| String serialize() override |
| { |
| std::unique_ptr<protocol::DictionaryValue> error = DictionaryValue::create(); |
| error->setInteger("code", m_code); |
| error->setString("message", m_errorMessage); |
| if (m_data.length()) |
| error->setString("data", m_data); |
| std::unique_ptr<protocol::DictionaryValue> message = DictionaryValue::create(); |
| message->setObject("error", std::move(error)); |
| if (m_hasCallId) |
| message->setInteger("id", m_callId); |
| return message->serialize(); |
| } |
| |
| ~ProtocolError() override {} |
| |
| private: |
| ProtocolError(DispatchResponse::ErrorCode code, const String& errorMessage) |
| : m_code(code) |
| , m_errorMessage(errorMessage) |
| { |
| } |
| |
| DispatchResponse::ErrorCode m_code; |
| String m_errorMessage; |
| String m_data; |
| int m_callId = 0; |
| bool m_hasCallId = false; |
| }; |
| |
| } // namespace |
| |
| static void reportProtocolErrorTo(FrontendChannel* frontendChannel, int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors) |
| { |
| if (frontendChannel) |
| frontendChannel->sendProtocolResponse(callId, ProtocolError::createErrorResponse(callId, code, errorMessage, errors)); |
| } |
| |
| static void reportProtocolErrorTo(FrontendChannel* frontendChannel, DispatchResponse::ErrorCode code, const String& errorMessage) |
| { |
| if (frontendChannel) |
| frontendChannel->sendProtocolNotification(ProtocolError::createErrorNotification(code, errorMessage)); |
| } |
| |
| void DispatcherBase::reportProtocolError(int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors) |
| { |
| reportProtocolErrorTo(m_frontendChannel, callId, code, errorMessage, errors); |
| } |
| |
| void DispatcherBase::clearFrontend() |
| { |
| m_frontendChannel = nullptr; |
| for (auto& weak : m_weakPtrs) |
| weak->dispose(); |
| m_weakPtrs.clear(); |
| } |
| |
| std::unique_ptr<DispatcherBase::WeakPtr> DispatcherBase::weakPtr() |
| { |
| std::unique_ptr<DispatcherBase::WeakPtr> weak(new DispatcherBase::WeakPtr(this)); |
| m_weakPtrs.insert(weak.get()); |
| return weak; |
| } |
| |
| UberDispatcher::UberDispatcher(FrontendChannel* frontendChannel) |
| : m_frontendChannel(frontendChannel) { } |
| |
| void UberDispatcher::registerBackend(const String& name, std::unique_ptr<protocol::DispatcherBase> dispatcher) |
| { |
| m_dispatchers[name] = std::move(dispatcher); |
| } |
| |
| void UberDispatcher::setupRedirects(const std::unordered_map<String, String>& redirects) |
| { |
| for (const auto& pair : redirects) |
| m_redirects[pair.first] = pair.second; |
| } |
| |
| bool UberDispatcher::parseCommand(Value* parsedMessage, int* outCallId, String* outMethod) { |
| if (!parsedMessage) { |
| reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kParseError, "Message must be a valid JSON"); |
| return false; |
| } |
| protocol::DictionaryValue* messageObject = DictionaryValue::cast(parsedMessage); |
| if (!messageObject) { |
| reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must be an object"); |
| return false; |
| } |
| |
| int callId = 0; |
| protocol::Value* callIdValue = messageObject->get("id"); |
| bool success = callIdValue && callIdValue->asInteger(&callId); |
| if (!success) { |
| reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must have integer 'id' property"); |
| return false; |
| } |
| if (outCallId) |
| *outCallId = callId; |
| |
| protocol::Value* methodValue = messageObject->get("method"); |
| String method; |
| success = methodValue && methodValue->asString(&method); |
| if (!success) { |
| reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kInvalidRequest, "Message must have string 'method' property", nullptr); |
| return false; |
| } |
| |
| std::unordered_map<String, String>::iterator redirectIt = m_redirects.find(method); |
| if (redirectIt != m_redirects.end()) |
| method = redirectIt->second; |
| if (outMethod) |
| *outMethod = method; |
| return true; |
| } |
| |
| protocol::DispatcherBase* UberDispatcher::findDispatcher(const String& method) { |
| size_t dotIndex = StringUtil::find(method, "."); |
| if (dotIndex == StringUtil::kNotFound) |
| return nullptr; |
| String domain = StringUtil::substring(method, 0, dotIndex); |
| auto it = m_dispatchers.find(domain); |
| if (it == m_dispatchers.end()) |
| return nullptr; |
| if (!it->second->canDispatch(method)) |
| return nullptr; |
| return it->second.get(); |
| } |
| |
| bool UberDispatcher::canDispatch(const String& method) |
| { |
| return !!findDispatcher(method); |
| } |
| |
| void UberDispatcher::dispatch(int callId, const String& method, std::unique_ptr<Value> parsedMessage, const String& rawMessage) |
| { |
| protocol::DispatcherBase* dispatcher = findDispatcher(method); |
| if (!dispatcher) { |
| reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr); |
| return; |
| } |
| std::unique_ptr<protocol::DictionaryValue> messageObject = DictionaryValue::cast(std::move(parsedMessage)); |
| dispatcher->dispatch(callId, method, rawMessage, std::move(messageObject)); |
| } |
| |
| UberDispatcher::~UberDispatcher() = default; |
| |
| // static |
| std::unique_ptr<InternalResponse> InternalResponse::createResponse(int callId, std::unique_ptr<Serializable> params) |
| { |
| return std::unique_ptr<InternalResponse>(new InternalResponse(callId, String(), std::move(params))); |
| } |
| |
| // static |
| std::unique_ptr<InternalResponse> InternalResponse::createNotification(const String& notification, std::unique_ptr<Serializable> params) |
| { |
| return std::unique_ptr<InternalResponse>(new InternalResponse(0, notification, std::move(params))); |
| } |
| |
| String InternalResponse::serialize() |
| { |
| std::unique_ptr<DictionaryValue> result = DictionaryValue::create(); |
| std::unique_ptr<Serializable> params(m_params ? std::move(m_params) : DictionaryValue::create()); |
| if (m_notification.length()) { |
| result->setString("method", m_notification); |
| result->setValue("params", SerializedValue::create(params->serialize())); |
| } else { |
| result->setInteger("id", m_callId); |
| result->setValue("result", SerializedValue::create(params->serialize())); |
| } |
| return result->serialize(); |
| } |
| |
| InternalResponse::InternalResponse(int callId, const String& notification, std::unique_ptr<Serializable> params) |
| : m_callId(callId) |
| , m_notification(notification) |
| , m_params(params ? std::move(params) : nullptr) |
| { |
| } |
| |
| {% for namespace in config.protocol.namespace %} |
| } // namespace {{namespace}} |
| {% endfor %} |