aboutsummaryrefslogtreecommitdiff
path: root/cast/common
diff options
context:
space:
mode:
authorYuri Wiitala <miu@chromium.org>2020-12-08 17:23:26 -0800
committerCommit Bot <commit-bot@chromium.org>2020-12-09 02:03:05 +0000
commit66f231597428bb8843d1e11efd6e324f6934ef19 (patch)
tree0066583915df6e6f177bf701e18333d79860fe4d /cast/common
parent963b0a6bd9156b612efc8c0d8142bba7b5f4e1dc (diff)
downloadopenscreen-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.cc114
-rw-r--r--cast/common/channel/connection_namespace_handler.h44
-rw-r--r--cast/common/channel/message_util.cc87
-rw-r--r--cast/common/channel/message_util.h14
-rw-r--r--cast/common/channel/virtual_connection.h8
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,