eb3551d0a3
This roll includes: - [inspector_protocol] Introduce protocol::Serializable [1] [1] https://codereview.chromium.org/2526603002 BUG=chromium:350797 R=dgozman@chromium.org Review-Url: https://codereview.chromium.org/2523583005 Cr-Commit-Position: refs/heads/master@{#41197}
335 lines
11 KiB
Plaintext
335 lines
11 KiB
Plaintext
// 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, int callbackId)
|
|
: m_backendImpl(std::move(backendImpl))
|
|
, m_callId(callId)
|
|
, m_callbackId(callbackId) { }
|
|
|
|
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()->markFallThrough(m_callbackId);
|
|
m_backendImpl = nullptr;
|
|
}
|
|
|
|
DispatcherBase::DispatcherBase(FrontendChannel* frontendChannel)
|
|
: m_frontendChannel(frontendChannel)
|
|
, m_lastCallbackId(0)
|
|
, m_lastCallbackFallThrough(false) { }
|
|
|
|
DispatcherBase::~DispatcherBase()
|
|
{
|
|
clearFrontend();
|
|
}
|
|
|
|
int DispatcherBase::nextCallbackId()
|
|
{
|
|
m_lastCallbackFallThrough = false;
|
|
return ++m_lastCallbackId;
|
|
}
|
|
|
|
void DispatcherBase::markFallThrough(int callbackId)
|
|
{
|
|
DCHECK(callbackId == m_lastCallbackId);
|
|
m_lastCallbackFallThrough = true;
|
|
}
|
|
|
|
// static
|
|
bool DispatcherBase::getCommandName(const String& message, String* result)
|
|
{
|
|
std::unique_ptr<protocol::Value> value = StringUtil::parseJSON(message);
|
|
if (!value)
|
|
return false;
|
|
|
|
protocol::DictionaryValue* object = DictionaryValue::cast(value.get());
|
|
if (!object)
|
|
return false;
|
|
|
|
if (!object->getString("method", result))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
, m_fallThroughForNotFound(false) { }
|
|
|
|
void UberDispatcher::setFallThroughForNotFound(bool fallThroughForNotFound)
|
|
{
|
|
m_fallThroughForNotFound = fallThroughForNotFound;
|
|
}
|
|
|
|
void UberDispatcher::registerBackend(const String& name, std::unique_ptr<protocol::DispatcherBase> dispatcher)
|
|
{
|
|
m_dispatchers[name] = std::move(dispatcher);
|
|
}
|
|
|
|
DispatchResponse::Status UberDispatcher::dispatch(std::unique_ptr<Value> parsedMessage)
|
|
{
|
|
if (!parsedMessage) {
|
|
reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kParseError, "Message must be a valid JSON");
|
|
return DispatchResponse::kError;
|
|
}
|
|
std::unique_ptr<protocol::DictionaryValue> messageObject = DictionaryValue::cast(std::move(parsedMessage));
|
|
if (!messageObject) {
|
|
reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must be an object");
|
|
return DispatchResponse::kError;
|
|
}
|
|
|
|
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' porperty");
|
|
return DispatchResponse::kError;
|
|
}
|
|
|
|
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' porperty", nullptr);
|
|
return DispatchResponse::kError;
|
|
}
|
|
|
|
size_t dotIndex = method.find(".");
|
|
if (dotIndex == StringUtil::kNotFound) {
|
|
if (m_fallThroughForNotFound)
|
|
return DispatchResponse::kFallThrough;
|
|
reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr);
|
|
return DispatchResponse::kError;
|
|
}
|
|
String domain = StringUtil::substring(method, 0, dotIndex);
|
|
auto it = m_dispatchers.find(domain);
|
|
if (it == m_dispatchers.end()) {
|
|
if (m_fallThroughForNotFound)
|
|
return DispatchResponse::kFallThrough;
|
|
reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr);
|
|
return DispatchResponse::kError;
|
|
}
|
|
return it->second->dispatch(callId, method, 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 %}
|