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;
mod remote_connector;
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;
}