diff options
author | Yuri Wiitala <miu@chromium.org> | 2020-12-08 17:23:26 -0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-12-09 02:03:05 +0000 |
commit | 66f231597428bb8843d1e11efd6e324f6934ef19 (patch) | |
tree | 0066583915df6e6f177bf701e18333d79860fe4d /cast/common | |
parent | 963b0a6bd9156b612efc8c0d8142bba7b5f4e1dc (diff) | |
download | openscreen-66f231597428bb8843d1e11efd6e324f6934ef19.tar.gz |
Remote virtual connections [2/3]: Add Open/CloseRemoteConnection().
This patch adds two new methods to ConnectionNamespaceHandler, to allow
Cast applications to establish and shutdown virtual connections over a
socket to a remote device. Internally, this causes CONNECT, CONNECTED,
and CLOSE messages to be sent between peers.
Improved/Filled-in fields for CONNECT messaging (based on Chromium's
implementation).
Bug: b/162542369
Change-Id: I26c3de9a9f79140e553905a35b8ee455f94eaf7e
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/2546883
Commit-Queue: Yuri Wiitala <miu@chromium.org>
Reviewed-by: Brandon Tolsch <btolsch@chromium.org>
Diffstat (limited to 'cast/common')
-rw-r--r-- | cast/common/channel/connection_namespace_handler.cc | 114 | ||||
-rw-r--r-- | cast/common/channel/connection_namespace_handler.h | 44 | ||||
-rw-r--r-- | cast/common/channel/message_util.cc | 87 | ||||
-rw-r--r-- | cast/common/channel/message_util.h | 14 | ||||
-rw-r--r-- | cast/common/channel/virtual_connection.h | 8 |
5 files changed, 232 insertions, 35 deletions
diff --git a/cast/common/channel/connection_namespace_handler.cc b/cast/common/channel/connection_namespace_handler.cc index dd459060..e754450d 100644 --- a/cast/common/channel/connection_namespace_handler.cc +++ b/cast/common/channel/connection_namespace_handler.cc @@ -91,13 +91,34 @@ ConnectionNamespaceHandler::ConnectionNamespaceHandler( ConnectionNamespaceHandler::~ConnectionNamespaceHandler() = default; +void ConnectionNamespaceHandler::OpenRemoteConnection( + VirtualConnection conn, + RemoteConnectionResultCallback result_callback) { + OSP_DCHECK(!vc_router_->GetConnectionData(conn)); + OSP_DCHECK(std::none_of( + pending_remote_requests_.begin(), pending_remote_requests_.end(), + [&](const PendingRequest& request) { return request.conn == conn; })); + pending_remote_requests_.push_back({conn, std::move(result_callback)}); + + SendConnect(std::move(conn)); +} + +void ConnectionNamespaceHandler::CloseRemoteConnection(VirtualConnection conn) { + if (RemoveConnection(conn, VirtualConnection::kClosedBySelf)) { + SendClose(std::move(conn)); + } +} + void ConnectionNamespaceHandler::OnMessage(VirtualConnectionRouter* router, CastSocket* socket, CastMessage message) { - if (message.payload_type() != - CastMessage_PayloadType::CastMessage_PayloadType_STRING) { + if (message.destination_id() == kBroadcastId || + message.source_id() == kBroadcastId || + message.payload_type() != + CastMessage_PayloadType::CastMessage_PayloadType_STRING) { return; } + ErrorOr<Json::Value> result = json::Parse(message.payload_utf8()); if (result.is_error()) { return; @@ -123,6 +144,8 @@ void ConnectionNamespaceHandler::OnMessage(VirtualConnectionRouter* router, HandleConnect(socket, std::move(message), std::move(value)); } else if (type_str == kMessageTypeClose) { HandleClose(socket, std::move(message), std::move(value)); + } else if (type_str == kMessageTypeConnected) { + HandleConnectedResponse(socket, std::move(message), std::move(value)); } else { // NOTE: Unknown message type so ignore it. // TODO(btolsch): Should be included in future error reporting. @@ -141,7 +164,7 @@ void ConnectionNamespaceHandler::HandleConnect(CastSocket* socket, std::move(message.source_id()), ToCastSocketId(socket)}; if (!vc_policy_->IsConnectionAllowed(virtual_conn)) { - SendClose(virtual_conn); + SendClose(std::move(virtual_conn)); return; } @@ -152,7 +175,7 @@ void ConnectionNamespaceHandler::HandleConnect(CastSocket* socket, int int_type = maybe_conn_type.value(); if (int_type < static_cast<int>(VirtualConnection::Type::kMinValue) || int_type > static_cast<int>(VirtualConnection::Type::kMaxValue)) { - SendClose(virtual_conn); + SendClose(std::move(virtual_conn)); return; } conn_type = static_cast<VirtualConnection::Type>(int_type); @@ -210,34 +233,52 @@ void ConnectionNamespaceHandler::HandleConnect(CastSocket* socket, void ConnectionNamespaceHandler::HandleClose(CastSocket* socket, CastMessage message, Json::Value parsed_message) { - VirtualConnection virtual_conn{std::move(message.destination_id()), - std::move(message.source_id()), - ToCastSocketId(socket)}; - if (!vc_router_->GetConnectionData(virtual_conn)) { + const VirtualConnection conn{std::move(*message.mutable_destination_id()), + std::move(*message.mutable_source_id()), + ToCastSocketId(socket)}; + const auto reason = GetCloseReason(parsed_message); + if (RemoveConnection(conn, reason)) { + OSP_DVLOG << "Connection closed (reason: " << reason + << "): " << conn.local_id << ", " << conn.peer_id << ", " + << conn.socket_id; + } +} + +void ConnectionNamespaceHandler::HandleConnectedResponse( + CastSocket* socket, + CastMessage message, + Json::Value parsed_message) { + const VirtualConnection conn{std::move(message.destination_id()), + std::move(message.source_id()), + ToCastSocketId(socket)}; + const auto it = std::find_if( + pending_remote_requests_.begin(), pending_remote_requests_.end(), + [&](const PendingRequest& request) { return request.conn == conn; }); + if (it == pending_remote_requests_.end()) { return; } - VirtualConnection::CloseReason reason = GetCloseReason(parsed_message); + vc_router_->AddConnection(conn, + {VirtualConnection::Type::kStrong, + {}, + {}, + VirtualConnection::ProtocolVersion::kV2_1_3}); - OSP_DVLOG << "Connection closed (reason: " << reason - << "): " << virtual_conn.local_id << ", " << virtual_conn.peer_id - << ", " << virtual_conn.socket_id; - vc_router_->RemoveConnection(virtual_conn, reason); + const auto callback = std::move(it->result_callback); + pending_remote_requests_.erase(it); + callback(true); } -void ConnectionNamespaceHandler::SendClose( - const VirtualConnection& virtual_conn) { - Json::Value close_message(Json::ValueType::objectValue); - close_message[kMessageKeyType] = kMessageTypeClose; - - ErrorOr<std::string> result = json::Stringify(close_message); - if (result.is_error()) { - return; - } +void ConnectionNamespaceHandler::SendConnect(VirtualConnection virtual_conn) { + ::cast::channel::CastMessage message = + MakeConnectMessage(virtual_conn.local_id, virtual_conn.peer_id); + vc_router_->Send(std::move(virtual_conn), std::move(message)); +} - vc_router_->Send( - std::move(virtual_conn), - MakeSimpleUTF8Message(kConnectionNamespace, std::move(result.value()))); +void ConnectionNamespaceHandler::SendClose(VirtualConnection virtual_conn) { + ::cast::channel::CastMessage message = + MakeCloseMessage(virtual_conn.local_id, virtual_conn.peer_id); + vc_router_->Send(std::move(virtual_conn), std::move(message)); } void ConnectionNamespaceHandler::SendConnectedResponse( @@ -258,5 +299,28 @@ void ConnectionNamespaceHandler::SendConnectedResponse( MakeSimpleUTF8Message(kConnectionNamespace, std::move(result.value()))); } +bool ConnectionNamespaceHandler::RemoveConnection( + const VirtualConnection& conn, + VirtualConnection::CloseReason reason) { + bool found_connection = false; + if (vc_router_->GetConnectionData(conn)) { + vc_router_->RemoveConnection(conn, reason); + found_connection = true; + } + + // Cancel pending remote request, if any. + const auto it = std::find_if( + pending_remote_requests_.begin(), pending_remote_requests_.end(), + [&](const PendingRequest& request) { return request.conn == conn; }); + if (it != pending_remote_requests_.end()) { + const auto callback = std::move(it->result_callback); + pending_remote_requests_.erase(it); + callback(false); + found_connection = true; + } + + return found_connection; +} + } // namespace cast } // namespace openscreen diff --git a/cast/common/channel/connection_namespace_handler.h b/cast/common/channel/connection_namespace_handler.h index 65388a80..4ae16509 100644 --- a/cast/common/channel/connection_namespace_handler.h +++ b/cast/common/channel/connection_namespace_handler.h @@ -5,18 +5,33 @@ #ifndef CAST_COMMON_CHANNEL_CONNECTION_NAMESPACE_HANDLER_H_ #define CAST_COMMON_CHANNEL_CONNECTION_NAMESPACE_HANDLER_H_ +#include <functional> +#include <vector> + #include "cast/common/channel/cast_message_handler.h" #include "cast/common/channel/proto/cast_channel.pb.h" +#include "cast/common/channel/virtual_connection.h" #include "util/json/json_serialization.h" namespace openscreen { namespace cast { -struct VirtualConnection; class VirtualConnectionRouter; // Handles CastMessages in the connection namespace by opening and closing // VirtualConnections on the socket on which the messages were received. +// +// This is meant to be used in either/both the initiator or responder role: +// +// 1. Initiators call Open/CloseRemoteConnection() to establish/close a virtual +// connection with a remote peer. Internally, OpenRemoteConnection() sends a +// CONNECT request to the remote peer, and the remote peer is expected to +// respond with a either a CONNECTED response or a CLOSE response. +// +// 2. Responders simply handle CONNECT or CLOSE requests, allowing or +// disallowing connections based on the VirtualConnectionPolicy, and +// ConnectionNamespaceHandler will report new/closed connections to the local +// VirtualConnectionRouter to enable/disable message routing. class ConnectionNamespaceHandler final : public CastMessageHandler { public: class VirtualConnectionPolicy { @@ -27,11 +42,23 @@ class ConnectionNamespaceHandler final : public CastMessageHandler { const VirtualConnection& virtual_conn) const = 0; }; + using RemoteConnectionResultCallback = std::function<void(bool)>; + // Both |vc_router| and |vc_policy| should outlive this object. ConnectionNamespaceHandler(VirtualConnectionRouter* vc_router, VirtualConnectionPolicy* vc_policy); ~ConnectionNamespaceHandler() override; + // Requests a virtual connection be established. The |result_callback| is + // later invoked with true/false to indicate success, based on a response from + // the remote. + void OpenRemoteConnection(VirtualConnection conn, + RemoteConnectionResultCallback result_callback); + + // Closes the virtual connection, notifying the remote by sending it a CLOSE + // message. + void CloseRemoteConnection(VirtualConnection conn); + // CastMessageHandler overrides. void OnMessage(VirtualConnectionRouter* router, CastSocket* socket, @@ -44,13 +71,26 @@ class ConnectionNamespaceHandler final : public CastMessageHandler { void HandleClose(CastSocket* socket, ::cast::channel::CastMessage message, Json::Value parsed_message); + void HandleConnectedResponse(CastSocket* socket, + ::cast::channel::CastMessage message, + Json::Value parsed_message); - void SendClose(const VirtualConnection& virtual_conn); + void SendConnect(VirtualConnection virtual_conn); + void SendClose(VirtualConnection virtual_conn); void SendConnectedResponse(const VirtualConnection& virtual_conn, int max_protocol_version); + bool RemoveConnection(const VirtualConnection& conn, + VirtualConnection::CloseReason reason); + VirtualConnectionRouter* const vc_router_; VirtualConnectionPolicy* const vc_policy_; + + struct PendingRequest { + VirtualConnection conn; + RemoteConnectionResultCallback result_callback; + }; + std::vector<PendingRequest> pending_remote_requests_; }; } // namespace cast diff --git a/cast/common/channel/message_util.cc b/cast/common/channel/message_util.cc index 44505501..92ea5007 100644 --- a/cast/common/channel/message_util.cc +++ b/cast/common/channel/message_util.cc @@ -7,14 +7,74 @@ #include <sstream> #include <utility> +#include "cast/common/channel/virtual_connection.h" +#include "util/json/json_serialization.h" +#include "util/json/json_value.h" #include "util/osp_logging.h" +#if defined(__APPLE__) || defined(__MACH__) +#include <TargetConditionals.h> +#endif + namespace openscreen { namespace cast { namespace { using ::cast::channel::CastMessage; +// The value used for "sdkType" in a virtual CONNECT request. Historically, this +// value was used in Chrome's C++ impl even though "2" refers to the Media +// Router Extension. +constexpr int kVirtualConnectSdkType = 2; + +// The value used for "connectionType" in a virtual CONNECT request. This value +// stands for CONNECTION_TYPE_LOCAL. +constexpr int kVirtualConnectTypeLocal = 1; + +// The value to be set as the "platform" value in a virtual CONNECT request. +// Source (in Chromium source tree): +// src/third_party/metrics_proto/cast_logs.proto +enum VirtualConnectPlatformValue { + kOtherPlatform = 0, + kAndroid = 1, + kIOS = 2, + kWindows = 3, + kMacOSX = 4, + kChromeOS = 5, + kLinux = 6, + kCastDevice = 7, +}; + +#if defined(__APPLE__) || defined(__MACH__) +constexpr VirtualConnectPlatformValue GetVirtualConnectPlatformMacFlavor() { +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + return kIOS; +#else + return kMacOSX; +#endif +} +#endif + +constexpr VirtualConnectPlatformValue GetVirtualConnectPlatform() { + // Based on //build/build_config.h in the Chromium project. The order of these + // matters! +#if defined(__ANDROID__) + return kAndroid; +#elif defined(__APPLE__) || defined(__MACH__) + return GetVirtualConnectPlatformMacFlavor(); +#elif defined(_WIN32) || defined(_WIN64) + return kWindows; +#elif defined(OS_CHROMEOS) + // Note: OS_CHROMEOS is defined via the compiler's command line in Chromium + // embedder builds by Chromium's //build/config/linux:runtime_library config. + return kChromeOS; +#elif defined(__linux__) + return kLinux; +#else + return kOtherPlatform; +#endif +} + CastMessage MakeConnectionMessage(const std::string& source_id, const std::string& destination_id) { CastMessage connect_message; @@ -56,7 +116,32 @@ CastMessage MakeConnectMessage(const std::string& source_id, MakeConnectionMessage(source_id, destination_id); connect_message.set_payload_type( ::cast::channel::CastMessage_PayloadType_STRING); - connect_message.set_payload_utf8(R"!({"type": "CONNECT"})!"); + + // Historically, the CONNECT message was meant to come from a Chrome browser. + // However, this library could be embedded in any app. So, properties like + // user agent, application version, etc. are not known here. + static constexpr char kUnknownVersion[] = "Unknown (Open Screen)"; + + Json::Value message(Json::objectValue); + message[kMessageKeyType] = CastMessageTypeToString(CastMessageType::kConnect); + for (int i = 0; i <= 3; ++i) { + message[kMessageKeyProtocolVersionList][i] = + ::cast::channel::CastMessage_ProtocolVersion_CASTV2_1_0 + i; + } + message[kMessageKeyUserAgent] = kUnknownVersion; + message[kMessageKeyConnType] = + static_cast<int>(VirtualConnection::Type::kStrong); + message[kMessageKeyOrigin] = Json::Value(Json::objectValue); + + Json::Value sender_info(Json::objectValue); + sender_info[kMessageKeySdkType] = kVirtualConnectSdkType; + sender_info[kMessageKeyVersion] = kUnknownVersion; + sender_info[kMessageKeyBrowserVersion] = kUnknownVersion; + sender_info[kMessageKeyPlatform] = GetVirtualConnectPlatform(); + sender_info[kMessageKeyConnectionType] = kVirtualConnectTypeLocal; + message[kMessageKeySenderInfo] = std::move(sender_info); + + connect_message.set_payload_utf8(json::Stringify(std::move(message)).value()); return connect_message; } diff --git a/cast/common/channel/message_util.h b/cast/common/channel/message_util.h index fcb25651..8e8fe823 100644 --- a/cast/common/channel/message_util.h +++ b/cast/common/channel/message_util.h @@ -42,9 +42,6 @@ static constexpr ::cast::channel::CastMessage_ProtocolVersion // JSON message key strings. static constexpr char kMessageKeyType[] = "type"; -static constexpr char kMessageKeyConnType[] = "connType"; -static constexpr char kMessageKeyUserAgent[] = "userAgent"; -static constexpr char kMessageKeySenderInfo[] = "senderInfo"; static constexpr char kMessageKeyProtocolVersion[] = "protocolVersion"; static constexpr char kMessageKeyProtocolVersionList[] = "protocolVersionList"; static constexpr char kMessageKeyReasonCode[] = "reasonCode"; @@ -61,6 +58,17 @@ static constexpr char kMessageTypeConnected[] = "CONNECTED"; static constexpr char kMessageValueAppAvailable[] = "APP_AVAILABLE"; static constexpr char kMessageValueAppUnavailable[] = "APP_UNAVAILABLE"; +// JSON message key strings specific to CONNECT messages. +static constexpr char kMessageKeyBrowserVersion[] = "browserVersion"; +static constexpr char kMessageKeyConnType[] = "connType"; +static constexpr char kMessageKeyConnectionType[] = "connectionType"; +static constexpr char kMessageKeyUserAgent[] = "userAgent"; +static constexpr char kMessageKeyOrigin[] = "origin"; +static constexpr char kMessageKeyPlatform[] = "platform"; +static constexpr char kMessageKeySdkType[] = "skdType"; +static constexpr char kMessageKeySenderInfo[] = "senderInfo"; +static constexpr char kMessageKeyVersion[] = "version"; + // JSON message key strings specific to application control messages. static constexpr char kMessageKeyAvailability[] = "availability"; static constexpr char kMessageKeyAppParams[] = "appParams"; diff --git a/cast/common/channel/virtual_connection.h b/cast/common/channel/virtual_connection.h index 5e94c372..35b090cc 100644 --- a/cast/common/channel/virtual_connection.h +++ b/cast/common/channel/virtual_connection.h @@ -18,15 +18,15 @@ namespace cast { struct VirtualConnection { // Virtual connections can have slightly different semantics for a particular // endpoint based on its type. - enum class Type { + enum class Type : int8_t { // Normal connections. Receiver applications should not exit while they // still have strong connections open (e.g. active senders). - kStrong, + kStrong = 0, // Same as strong except if the connected endpoint is a receiver // application, it may stop if its only remaining open connections are all // weak. - kWeak, + kWeak = 1, // Receiver applications do not receive connected/disconnected notifications // about these connections. The following additional conditions apply: @@ -34,7 +34,7 @@ struct VirtualConnection { // messages over invisible connections. // - Receiver app can only send broadcast messages over an invisible // connection. - kInvisible, + kInvisible = 2, kMinValue = kStrong, kMaxValue = kInvisible, |