1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
// Buttplug Rust Source Code File - See https://buttplug.io for more info. // // Copyright 2016-2020 Nonpolynomial Labs LLC. All rights reserved. // // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. //! Methods for establishing connections between Buttplug Clients and Servers //! //! Buttplug is made to work in many different circumstances. The //! [ButtplugClient] and [ButtplugServer] may be in the same process, in //! different process communicating over some sort of IPC, or on different //! machines using a network connection. Connectors are what make these setups //! possible. //! //! # How Buttplug Clients and Servers Use Connectors //! //! A Buttplug Client uses a connector to communicate with a server, be it in //! the same process or on another machine. The client's connector handles //! establishing the connection to the server, as well as sending ([possibly //! serialized][crate::core::messages::serializer]) messages to the server and //! matching replies from the server to waiting futures. //! //! Buttplug servers use connectors to receive info from clients. They usually //! have less to do than client connectors, because they don't have to keep //! track of messages waiting for replies (since Buttplug messages that require //! responses are only client -> server, the server will never send anything to //! a client that expects a response.) //! //! # In-Process and Remote Connectors //! //! There are two types of connectors: In-Process and Remote. All connectors //! have the same API (since they all follow the [ButtplugClientConnector] and //! [ButtplugServerConnector] traits), but will varying in latency, message //! passing techniques, etc... //! //! There is only 1 in-process connector, the //! [ButtplugInProcessClientConnector]. This is used when the client and server //! live in the same process, which is useful for multiple reasons (see //! [ButtplugInProcessClientConnector] documentation for more info). As //! in-process connectors can just send message objects back and forth, there is //! no need for message serialization. //! //! Remote connectors refer to any connector that connects to something outside //! of the current process, be it still on the same machine (IPC) or somewhere //! else (network). //! //! # Remote Transports //! //! Remote Transports //! //! # Buttplug Client/Server Does Not Necessarily Mean Transport Client/Server //! //! Here's an odd but valid situation: *You can have a Buttplug Client that uses //! a Websocket Server connector!* //! //! There are times where this is actually useful. For instance, let's say a //! user of Buttplug wants to use a Windows 7 desktop machine to control a //! Bluetooth LE toy. This normally wouldn't work because Windows 7 doesn't have //! a Bluetooth LE API we can easily access. However, they also have an android //! phone. They could run a Buttplug Server in Chrome on their Android phone, //! have the Client on the desktop run a websocket server, then have (and stick //! with me here) the Buttplug Server in the Android Chrome instance use a //! Websocket Client to connect to the Websocket Server on the desktop. This //! allows the desktop machine to proxy Bluetooth to the WebBluetooth API built //! into Android Chrome. //! //! Is this ridiculous? *Absolutely*. //! //! Will people do it? Remember, this is a library about sex, so the answer is //! also *Absolutely*. //! //! There are slightly more useful situations like device forwarders where this //! work comes in also, but that Windows 7/Android example is where the idea //! originally came from. #[cfg(all(feature = "server", feature = "client"))] mod in_process_connector; pub mod remote_connector; pub mod transport; #[cfg(all(feature = "server", feature = "client"))] pub use in_process_connector::ButtplugInProcessClientConnector; pub use remote_connector::{ ButtplugRemoteClientConnector, ButtplugRemoteConnector, ButtplugRemoteServerConnector, }; #[cfg(feature = "websockets")] pub use transport::ButtplugWebsocketClientTransport; #[cfg(all(feature = "websockets", feature = "async-std-runtime"))] pub use transport::{ButtplugWebsocketServerTransport, ButtplugWebsocketServerTransportOptions}; use crate::{ core::{ errors::ButtplugServerError, messages::{serializer::ButtplugSerializedMessage, ButtplugMessage}, }, util::future::{ButtplugFuture, ButtplugFutureStateShared}, }; use async_channel::Receiver; use displaydoc::Display; use futures::future::{self, BoxFuture}; use thiserror::Error; pub type ButtplugConnectorResult = Result<(), ButtplugConnectorError>; pub type ButtplugConnectorStateShared = ButtplugFutureStateShared<Result<(), ButtplugConnectorError>>; pub type ButtplugConnectorFuture = ButtplugFuture<Result<(), ButtplugConnectorError>>; pub type ButtplugConnectorResultFuture = BoxFuture<'static, ButtplugConnectorResult>; /// Errors specific to client connector structs. /// /// Errors that relate to the communication method of the client connector. Can /// include network/IPC protocol specific errors. #[derive(Debug, Error, Display)] pub enum ButtplugConnectorError { /// Connector is not currently connected to a remote. ConnectorNotConnected, /// Connector channel has closed, meaning disconnection is likely. ConnectorChannelClosed, /// Connector already connected, cannot be connected twice. ConnectorAlreadyConnected, /// Connector error: {0} ConnectorGenericError(String), /// Specific error for connector type: {0}. TransportSpecificError(transport::ButtplugConnectorTransportSpecificError), } impl<T> From<ButtplugConnectorError> for BoxFuture<'static, Result<T, ButtplugConnectorError>> where T: Send + 'static, { fn from(err: ButtplugConnectorError) -> BoxFuture<'static, Result<T, ButtplugConnectorError>> { Box::pin(future::ready(Err(err))) } } /// Trait for client connectors. /// /// Connectors are how Buttplug Clients and servers talk to each other. Whether /// embedded, meaning the client and server exist in the same process space, or /// remote, where the client and server are separated by some boundary, the /// connector trait makes it so that these components always look local. /// /// The `O` type specifies the outbound message type. This will usually be a /// message enum. For instance, in a client connector, this would usually be /// [ButtplugClientOutMessage][crate::core::messages::ButtplugClientOutMessage]. /// /// The `I` type specifies the inbound message type. This will usually be a /// message enum. For instance, in a client connector, this would usually be /// [ButtplugClientInMessage][crate::core::messages::ButtplugClientInMessage]. pub trait ButtplugConnector<OutboundMessageType, InboundMessageType>: Send + Sync where OutboundMessageType: ButtplugMessage + 'static, InboundMessageType: ButtplugMessage + 'static, { /// Connects the client to the server. /// /// Tries to connect to another connector, returning an event stream of /// incoming messages (of type `I`) on successful connect. /// /// # Errors /// /// Returns a [ButtplugClientConnectorError] if there is a problem with the /// connection process. It is assumed that all information needed to create /// the connection will be passed as part of the Trait implementors creation /// methods. /// /// # Async /// /// As connection may involve blocking operations like establishing network /// connections, this trait method is marked async. fn connect( &mut self, ) -> BoxFuture< 'static, Result<Receiver<Result<InboundMessageType, ButtplugServerError>>, ButtplugConnectorError>, >; /// Disconnects the client from the server. /// /// Returns a [ButtplugClientConnectorError] if there is a problem with the /// disconnection process. /// /// # Async /// /// As disconnection may involve blocking operations like network closing and /// cleanup, this trait method is marked async. fn disconnect(&self) -> ButtplugConnectorResultFuture; /// Sends a message of outbound message type `O` to the other connector. /// /// # Errors /// /// If the connector is not currently connected, or an error happens during /// the send operation, this will return a [ButtplugConnectorError] fn send(&self, msg: OutboundMessageType) -> ButtplugConnectorResultFuture; }