blob: 8f166523692ed7b724218d70707d66dbc33f7c90 [file] [log] [blame]
// Copyright 2018 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 "components/mirroring/service/receiver_response.h"
#include "base/base64.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "components/mirroring/service/value_util.h"
#include "third_party/jsoncpp/source/include/json/reader.h"
#include "third_party/jsoncpp/source/include/json/writer.h"
namespace mirroring {
namespace {
// Get the response type from the type string value in the JSON message.
ResponseType ResponseTypeFromString(const std::string& type) {
if (type == "ANSWER")
return ResponseType::ANSWER;
if (type == "CAPABILITIES_RESPONSE")
return ResponseType::CAPABILITIES_RESPONSE;
if (type == "RPC")
return ResponseType::RPC;
return ResponseType::UNKNOWN;
}
// JSON helper methods. Note that these are *heavily* based on the
// util/json_helpers.h methods from Open Screen. When the mirroring service
// moves to depend on libcast, the duplicate code should pretty much all go
// away.
bool GetInt(const Json::Value& value, int* out) {
// We are generally very forgiving of missing fields, so don't return an
// error if it is just missing.
if (!value) {
*out = -1;
return true;
}
// If it's present, though, it must be valid.
if (!value.isInt()) {
return false;
}
const int i = value.asInt();
if (i < 0) {
return false;
}
*out = i;
return true;
}
bool GetString(const Json::Value& value, std::string* out) {
if (!value) {
*out = {};
return true;
}
if (!value.isString()) {
return false;
}
*out = value.asString();
return true;
}
template <typename T>
using Parser = base::RepeatingCallback<bool(const Json::Value&, T*)>;
// Returns whether or not an error occurred. For the purpose of this function,
// if the value is empty or not an array, that is not an error, and it thus
// returns true after setting the |out| vector to empty.
template <typename T>
bool GetArray(const Json::Value& value, Parser<T> parser, std::vector<T>* out) {
out->clear();
if (!value.isArray() || value.empty()) {
return true;
}
out->reserve(value.size());
for (auto i : value) {
T v;
if (!parser.Run(i, &v)) {
out->clear();
return false;
}
out->push_back(std::move(v));
}
return true;
}
bool GetStringArray(const Json::Value& value, std::vector<std::string>* out) {
return GetArray<std::string>(value, base::BindRepeating(&GetString), out);
}
ResponseType GetResponseType(const Json::Value& root_node) {
std::string type;
if (!GetString(root_node["type"], &type)) {
return ResponseType::UNKNOWN;
}
return ResponseTypeFromString(base::ToUpperASCII(type));
}
std::string GetDetails(const Json::Value& value) {
if (!value) {
return {};
}
Json::StreamWriterBuilder builder;
return Json::writeString(builder, value);
}
std::unique_ptr<ReceiverError> ParseError(const Json::Value& value) {
auto error = std::make_unique<ReceiverError>();
if (!GetInt(value["code"], &(error->code)) ||
!GetString(value["description"], &(error->description))) {
return {};
}
// We are generally pretty forgiving about details: throwing an error
// because the Receiver didn't properly fill out the detail of an error
// message doesn't really make sense.
error->details = GetDetails(value["details"]);
return error;
}
std::unique_ptr<ReceiverCapability> ParseCapability(const Json::Value& value) {
auto capability = std::make_unique<ReceiverCapability>();
if (!value)
return {};
if (!GetInt(value["remoting"], &(capability->remoting))) {
capability->remoting = ReceiverCapability::kRemotingVersionUnknown;
}
if (!GetStringArray(value["mediaCaps"], &(capability->media_caps))) {
return {};
}
return capability;
}
} // namespace
ReceiverCapability::ReceiverCapability() = default;
ReceiverCapability::~ReceiverCapability() = default;
ReceiverCapability::ReceiverCapability(ReceiverCapability&& receiver_response) =
default;
ReceiverCapability::ReceiverCapability(
const ReceiverCapability& receiver_response) = default;
ReceiverCapability& ReceiverCapability::operator=(
ReceiverCapability&& receiver_response) = default;
ReceiverCapability& ReceiverCapability::operator=(
const ReceiverCapability& receiver_response) = default;
ReceiverError::ReceiverError() = default;
ReceiverError::~ReceiverError() = default;
ReceiverError::ReceiverError(ReceiverError&& receiver_response) = default;
ReceiverError::ReceiverError(const ReceiverError& receiver_response) = default;
ReceiverError& ReceiverError::operator=(ReceiverError&& receiver_response) =
default;
ReceiverError& ReceiverError::operator=(
const ReceiverError& receiver_response) = default;
ReceiverResponse::ReceiverResponse() = default;
ReceiverResponse::~ReceiverResponse() = default;
ReceiverResponse::ReceiverResponse(ReceiverResponse&& receiver_response) =
default;
ReceiverResponse& ReceiverResponse::operator=(
ReceiverResponse&& receiver_response) = default;
// static
std::unique_ptr<ReceiverResponse> ReceiverResponse::Parse(
const std::string& message_data) {
Json::CharReaderBuilder builder;
Json::CharReaderBuilder::strictMode(&builder.settings_);
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
Json::Value root_node;
std::string error_msg;
const bool succeeded = reader->parse(
message_data.data(), message_data.data() + message_data.length(),
&root_node, &error_msg);
if (!succeeded) {
DVLOG(1) << "Failed to parse reciever message: " << error_msg;
return nullptr;
}
auto response = std::make_unique<ReceiverResponse>();
std::string result;
if (!root_node || !GetInt(root_node["sessionId"], &(response->session_id_)) ||
!GetInt(root_node["seqNum"], &(response->sequence_number_)) ||
!GetString(root_node["result"], &result)) {
return nullptr;
}
response->type_ = GetResponseType(root_node);
// For backwards compatibility with <= M85, RPC responses lack a result field.
response->valid_ = (result == "ok" || response->type_ == ResponseType::RPC);
if (!response->valid_) {
response->error_ = ParseError(root_node["error"]);
return response;
}
switch (response->type_) {
case ResponseType::ANSWER:
response->answer_ = std::make_unique<openscreen::cast::Answer>();
if (!openscreen::cast::Answer::ParseAndValidate(
root_node["answer"], response->answer_.get())) {
response->valid_ = false;
}
break;
case ResponseType::CAPABILITIES_RESPONSE:
response->capabilities_ = ParseCapability(root_node["capabilities"]);
if (!response->capabilities_) {
response->valid_ = false;
}
break;
case ResponseType::RPC: {
std::string raw_rpc;
if (!GetString(root_node["rpc"], &raw_rpc) ||
!base::Base64Decode(raw_rpc, &(response->rpc_))) {
response->valid_ = false;
}
} break;
case ResponseType::UNKNOWN:
default:
response->valid_ = false;
break;
}
return response;
}
std::unique_ptr<ReceiverResponse> ReceiverResponse::CloneForTesting() const {
auto clone = std::make_unique<ReceiverResponse>();
clone->type_ = type_;
clone->session_id_ = session_id_;
clone->sequence_number_ = sequence_number_;
clone->valid_ = valid_;
if (!valid_) {
if (error_) {
clone->error_ = std::make_unique<ReceiverError>(*error_);
}
return clone;
}
// We assume that if the message wasn't classified as an error,
// it has a body.
switch (type_) {
case ResponseType::ANSWER:
clone->answer_ = std::make_unique<openscreen::cast::Answer>(*answer_);
break;
case ResponseType::CAPABILITIES_RESPONSE:
clone->capabilities_ =
std::make_unique<ReceiverCapability>(*capabilities_);
break;
case ResponseType::RPC:
clone->rpc_ = rpc_;
break;
case ResponseType::UNKNOWN:
break;
}
return clone;
}
// static
ReceiverResponse ReceiverResponse::CreateAnswerResponseForTesting(
int32_t sequence_number,
std::unique_ptr<openscreen::cast::Answer> answer) {
ReceiverResponse response;
response.type_ = ResponseType::ANSWER;
response.sequence_number_ = sequence_number;
response.answer_ = std::move(answer);
response.valid_ = true;
return response;
}
// static
ReceiverResponse ReceiverResponse::CreateCapabilitiesResponseForTesting(
int32_t sequence_number,
std::unique_ptr<ReceiverCapability> capabilities) {
ReceiverResponse response;
response.type_ = ResponseType::CAPABILITIES_RESPONSE;
response.sequence_number_ = sequence_number;
response.capabilities_ = std::move(capabilities);
response.valid_ = true;
return response;
}
} // namespace mirroring