parent
5613f9e47d
commit
c2ff77b446
@ -0,0 +1,31 @@ |
|||||||
|
use async_tungstenite::{async_std::connect_async, tungstenite::Message}; |
||||||
|
use futures::prelude::*; |
||||||
|
|
||||||
|
use async_std::task; |
||||||
|
|
||||||
|
async fn run() -> Result<(), Box<dyn std::error::Error>> { |
||||||
|
#[cfg(any(feature = "async-tls", feature = "async-native-tls"))] |
||||||
|
let url = url::Url::parse("wss://echo.websocket.org").unwrap(); |
||||||
|
#[cfg(not(any(feature = "async-tls", feature = "async-native-tls")))] |
||||||
|
let url = url::Url::parse("ws://echo.websocket.org").unwrap(); |
||||||
|
|
||||||
|
let (mut ws_stream, _) = connect_async(url).await?; |
||||||
|
|
||||||
|
let text = "Hello, World!"; |
||||||
|
|
||||||
|
println!("Sending: \"{}\"", text); |
||||||
|
ws_stream.send(Message::text(text)).await?; |
||||||
|
|
||||||
|
let msg = ws_stream |
||||||
|
.next() |
||||||
|
.await |
||||||
|
.ok_or_else(|| "didn't receive anything")??; |
||||||
|
|
||||||
|
println!("Received: {:?}", msg); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> { |
||||||
|
task::block_on(run()) |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
use async_tungstenite::{gio::connect_async, tungstenite::Message}; |
||||||
|
use futures::prelude::*; |
||||||
|
|
||||||
|
async fn run() -> Result<(), Box<dyn std::error::Error>> { |
||||||
|
let url = url::Url::parse("wss://echo.websocket.org").unwrap(); |
||||||
|
|
||||||
|
let (mut ws_stream, _) = connect_async(url).await?; |
||||||
|
|
||||||
|
let text = "Hello, World!"; |
||||||
|
|
||||||
|
println!("Sending: \"{}\"", text); |
||||||
|
ws_stream.send(Message::text(text)).await?; |
||||||
|
|
||||||
|
let msg = ws_stream |
||||||
|
.next() |
||||||
|
.await |
||||||
|
.ok_or_else(|| "didn't receive anything")??; |
||||||
|
|
||||||
|
println!("Received: {:?}", msg); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> { |
||||||
|
// Get the default main context and run our async function on it
|
||||||
|
let main_context = glib::MainContext::default(); |
||||||
|
main_context.block_on(run()) |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
use async_tungstenite::{tokio::connect_async, tungstenite::Message}; |
||||||
|
use futures::prelude::*; |
||||||
|
|
||||||
|
async fn run() -> Result<(), Box<dyn std::error::Error>> { |
||||||
|
#[cfg(any(feature = "async-tls", feature = "tokio-tls"))] |
||||||
|
let url = url::Url::parse("wss://echo.websocket.org").unwrap(); |
||||||
|
#[cfg(not(any(feature = "async-tls", feature = "tokio-tls")))] |
||||||
|
let url = url::Url::parse("ws://echo.websocket.org").unwrap(); |
||||||
|
|
||||||
|
let (mut ws_stream, _) = connect_async(url).await?; |
||||||
|
|
||||||
|
let text = "Hello, World!"; |
||||||
|
|
||||||
|
println!("Sending: \"{}\"", text); |
||||||
|
ws_stream.send(Message::text(text)).await?; |
||||||
|
|
||||||
|
let msg = ws_stream |
||||||
|
.next() |
||||||
|
.await |
||||||
|
.ok_or_else(|| "didn't receive anything")??; |
||||||
|
|
||||||
|
println!("Received: {:?}", msg); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> { |
||||||
|
let mut rt = tokio::runtime::Runtime::new()?; |
||||||
|
rt.block_on(run()) |
||||||
|
} |
@ -0,0 +1,268 @@ |
|||||||
|
//! `async-std` integration.
|
||||||
|
use tungstenite::handshake::client::Response; |
||||||
|
use tungstenite::protocol::WebSocketConfig; |
||||||
|
use tungstenite::Error; |
||||||
|
|
||||||
|
use async_std::net::TcpStream; |
||||||
|
|
||||||
|
use super::{domain, Request, WebSocketStream}; |
||||||
|
|
||||||
|
#[cfg(feature = "async-native-tls")] |
||||||
|
use futures::io::{AsyncRead, AsyncWrite}; |
||||||
|
|
||||||
|
#[cfg(feature = "async-native-tls")] |
||||||
|
pub(crate) mod async_native_tls { |
||||||
|
use async_native_tls::TlsConnector as AsyncTlsConnector; |
||||||
|
use async_native_tls::TlsStream; |
||||||
|
use real_async_native_tls as async_native_tls; |
||||||
|
|
||||||
|
use tungstenite::client::url_mode; |
||||||
|
use tungstenite::stream::Mode; |
||||||
|
use tungstenite::Error; |
||||||
|
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite}; |
||||||
|
|
||||||
|
use crate::stream::Stream as StreamSwitcher; |
||||||
|
use crate::{ |
||||||
|
client_async_with_config, domain, Request, Response, WebSocketConfig, WebSocketStream, |
||||||
|
}; |
||||||
|
|
||||||
|
/// A stream that might be protected with TLS.
|
||||||
|
pub type MaybeTlsStream<S> = StreamSwitcher<S, TlsStream<S>>; |
||||||
|
|
||||||
|
pub type AutoStream<S> = MaybeTlsStream<S>; |
||||||
|
|
||||||
|
pub type Connector = AsyncTlsConnector; |
||||||
|
|
||||||
|
async fn wrap_stream<S>( |
||||||
|
socket: S, |
||||||
|
domain: String, |
||||||
|
connector: Option<Connector>, |
||||||
|
mode: Mode, |
||||||
|
) -> Result<AutoStream<S>, Error> |
||||||
|
where |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
{ |
||||||
|
match mode { |
||||||
|
Mode::Plain => Ok(StreamSwitcher::Plain(socket)), |
||||||
|
Mode::Tls => { |
||||||
|
let stream = { |
||||||
|
let connector = if let Some(connector) = connector { |
||||||
|
connector |
||||||
|
} else { |
||||||
|
AsyncTlsConnector::new() |
||||||
|
}; |
||||||
|
connector |
||||||
|
.connect(&domain, socket) |
||||||
|
.await |
||||||
|
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))? |
||||||
|
}; |
||||||
|
Ok(StreamSwitcher::Tls(stream)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required and using the given
|
||||||
|
/// connector and WebSocket configuration.
|
||||||
|
pub async fn client_async_tls_with_connector_and_config<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
connector: Option<AsyncTlsConnector>, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
let request: Request = request.into(); |
||||||
|
|
||||||
|
let domain = domain(&request)?; |
||||||
|
|
||||||
|
// Make sure we check domain and mode first. URL must be valid.
|
||||||
|
let mode = url_mode(&request.url)?; |
||||||
|
|
||||||
|
let stream = wrap_stream(stream, domain, connector, mode).await?; |
||||||
|
client_async_with_config(request, stream, config).await |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(not(any(feature = "async-tls", feature = "async-native-tls")))] |
||||||
|
pub(crate) mod dummy_tls { |
||||||
|
use futures::io::{AsyncRead, AsyncWrite}; |
||||||
|
|
||||||
|
use tungstenite::client::url_mode; |
||||||
|
use tungstenite::stream::Mode; |
||||||
|
use tungstenite::Error; |
||||||
|
|
||||||
|
use crate::{ |
||||||
|
client_async_with_config, domain, Request, Response, WebSocketConfig, WebSocketStream, |
||||||
|
}; |
||||||
|
|
||||||
|
pub type AutoStream<S> = S; |
||||||
|
type Connector = (); |
||||||
|
|
||||||
|
async fn wrap_stream<S>( |
||||||
|
socket: S, |
||||||
|
_domain: String, |
||||||
|
_connector: Option<()>, |
||||||
|
mode: Mode, |
||||||
|
) -> Result<AutoStream<S>, Error> |
||||||
|
where |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
{ |
||||||
|
match mode { |
||||||
|
Mode::Plain => Ok(socket), |
||||||
|
Mode::Tls => Err(Error::Url("TLS support not compiled in.".into())), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) async fn client_async_tls_with_connector_and_config<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
connector: Option<Connector>, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
let request: Request = request.into(); |
||||||
|
|
||||||
|
let domain = domain(&request)?; |
||||||
|
|
||||||
|
// Make sure we check domain and mode first. URL must be valid.
|
||||||
|
let mode = url_mode(&request.url)?; |
||||||
|
|
||||||
|
let stream = wrap_stream(stream, domain, connector, mode).await?; |
||||||
|
client_async_with_config(request, stream, config).await |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(not(any(feature = "async-tls", feature = "async-native-tls")))] |
||||||
|
use self::dummy_tls::{client_async_tls_with_connector_and_config, AutoStream}; |
||||||
|
|
||||||
|
#[cfg(all(feature = "async-tls", not(feature = "async-native-tls")))] |
||||||
|
use crate::async_tls::{client_async_tls_with_connector_and_config, AutoStream}; |
||||||
|
#[cfg(all(feature = "async-tls", not(feature = "async-native-tls")))] |
||||||
|
type Connector = real_async_tls::TlsConnector; |
||||||
|
|
||||||
|
#[cfg(feature = "async-native-tls")] |
||||||
|
use self::async_native_tls::{client_async_tls_with_connector_and_config, AutoStream, Connector}; |
||||||
|
|
||||||
|
#[cfg(feature = "async-native-tls")] |
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required.
|
||||||
|
pub async fn client_async_tls<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
client_async_tls_with_connector_and_config(request, stream, None, None).await |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(feature = "async-native-tls")] |
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required and using the given
|
||||||
|
/// WebSocket configuration.
|
||||||
|
pub async fn client_async_tls_with_config<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
client_async_tls_with_connector_and_config(request, stream, None, config).await |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(feature = "async-native-tls")] |
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required and using the given
|
||||||
|
/// connector.
|
||||||
|
pub async fn client_async_tls_with_connector<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
connector: Option<Connector>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
client_async_tls_with_connector_and_config(request, stream, connector, None).await |
||||||
|
} |
||||||
|
|
||||||
|
/// Connect to a given URL.
|
||||||
|
pub async fn connect_async<R>( |
||||||
|
request: R, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
{ |
||||||
|
connect_async_with_config(request, None).await |
||||||
|
} |
||||||
|
|
||||||
|
/// Connect to a given URL with a given WebSocket configuration.
|
||||||
|
pub async fn connect_async_with_config<R>( |
||||||
|
request: R, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
{ |
||||||
|
let request: Request = request.into(); |
||||||
|
|
||||||
|
let domain = domain(&request)?; |
||||||
|
let port = request |
||||||
|
.url |
||||||
|
.port_or_known_default() |
||||||
|
.expect("Bug: port unknown"); |
||||||
|
|
||||||
|
let try_socket = TcpStream::connect((domain.as_str(), port)).await; |
||||||
|
let socket = try_socket.map_err(Error::Io)?; |
||||||
|
client_async_tls_with_connector_and_config(request, socket, None, config).await |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(any(feature = "async-tls", feature = "async-native-tls"))] |
||||||
|
/// Connect to a given URL using the provided TLS connector.
|
||||||
|
pub async fn connect_async_with_tls_connector<R>( |
||||||
|
request: R, |
||||||
|
connector: Option<Connector>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
{ |
||||||
|
connect_async_with_tls_connector_and_config(request, connector, None).await |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(any(feature = "async-tls", feature = "async-native-tls"))] |
||||||
|
/// Connect to a given URL using the provided TLS connector.
|
||||||
|
pub async fn connect_async_with_tls_connector_and_config<R>( |
||||||
|
request: R, |
||||||
|
connector: Option<Connector>, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
{ |
||||||
|
let request: Request = request.into(); |
||||||
|
|
||||||
|
let domain = domain(&request)?; |
||||||
|
let port = request |
||||||
|
.url |
||||||
|
.port_or_known_default() |
||||||
|
.expect("Bug: port unknown"); |
||||||
|
|
||||||
|
let try_socket = TcpStream::connect((domain.as_str(), port)).await; |
||||||
|
let socket = try_socket.map_err(Error::Io)?; |
||||||
|
client_async_tls_with_connector_and_config(request, socket, connector, config).await |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
//! `async-tls` integration.
|
||||||
|
use tungstenite::client::url_mode; |
||||||
|
use tungstenite::handshake::client::Response; |
||||||
|
use tungstenite::protocol::WebSocketConfig; |
||||||
|
use tungstenite::Error; |
||||||
|
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite}; |
||||||
|
|
||||||
|
use super::{client_async_with_config, Request, WebSocketStream}; |
||||||
|
|
||||||
|
use async_tls::client::TlsStream; |
||||||
|
use async_tls::TlsConnector as AsyncTlsConnector; |
||||||
|
use real_async_tls as async_tls; |
||||||
|
|
||||||
|
use tungstenite::stream::Mode; |
||||||
|
|
||||||
|
use crate::domain; |
||||||
|
use crate::stream::Stream as StreamSwitcher; |
||||||
|
|
||||||
|
type MaybeTlsStream<S> = StreamSwitcher<S, TlsStream<S>>; |
||||||
|
|
||||||
|
pub(crate) type AutoStream<S> = MaybeTlsStream<S>; |
||||||
|
|
||||||
|
async fn wrap_stream<S>( |
||||||
|
socket: S, |
||||||
|
domain: String, |
||||||
|
connector: Option<AsyncTlsConnector>, |
||||||
|
mode: Mode, |
||||||
|
) -> Result<AutoStream<S>, Error> |
||||||
|
where |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
{ |
||||||
|
match mode { |
||||||
|
Mode::Plain => Ok(StreamSwitcher::Plain(socket)), |
||||||
|
Mode::Tls => { |
||||||
|
let stream = { |
||||||
|
let connector = connector.unwrap_or_else(AsyncTlsConnector::new); |
||||||
|
connector.connect(&domain, socket)?.await? |
||||||
|
}; |
||||||
|
Ok(StreamSwitcher::Tls(stream)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required.
|
||||||
|
pub async fn client_async_tls<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
client_async_tls_with_connector_and_config(request, stream, None, None).await |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required and using the given
|
||||||
|
/// WebSocket configuration.
|
||||||
|
pub async fn client_async_tls_with_config<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
client_async_tls_with_connector_and_config(request, stream, None, config).await |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required and using the given
|
||||||
|
/// connector.
|
||||||
|
pub async fn client_async_tls_with_connector<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
connector: Option<AsyncTlsConnector>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
client_async_tls_with_connector_and_config(request, stream, connector, None).await |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required and using the given
|
||||||
|
/// connector and WebSocket configuration.
|
||||||
|
pub async fn client_async_tls_with_connector_and_config<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
connector: Option<AsyncTlsConnector>, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
let request: Request = request.into(); |
||||||
|
|
||||||
|
let domain = domain(&request)?; |
||||||
|
|
||||||
|
// Make sure we check domain and mode first. URL must be valid.
|
||||||
|
let mode = url_mode(&request.url)?; |
||||||
|
|
||||||
|
let stream = wrap_stream(stream, domain, connector, mode).await?; |
||||||
|
client_async_with_config(request, stream, config).await |
||||||
|
} |
@ -1,263 +0,0 @@ |
|||||||
//! Connection helper.
|
|
||||||
use tungstenite::client::url_mode; |
|
||||||
use tungstenite::handshake::client::Response; |
|
||||||
use tungstenite::protocol::WebSocketConfig; |
|
||||||
use tungstenite::Error; |
|
||||||
|
|
||||||
use futures::io::{AsyncRead, AsyncWrite}; |
|
||||||
|
|
||||||
use super::{client_async_with_config, Request, WebSocketStream}; |
|
||||||
|
|
||||||
#[cfg(feature = "tls-base")] |
|
||||||
pub(crate) mod encryption { |
|
||||||
#[cfg(feature = "tls")] |
|
||||||
use async_tls::client::TlsStream; |
|
||||||
#[cfg(feature = "tls")] |
|
||||||
use async_tls::TlsConnector as AsyncTlsConnector; |
|
||||||
|
|
||||||
#[cfg(feature = "native-tls")] |
|
||||||
use async_native_tls::TlsConnector as AsyncTlsConnector; |
|
||||||
#[cfg(feature = "native-tls")] |
|
||||||
use async_native_tls::TlsStream; |
|
||||||
|
|
||||||
use tungstenite::stream::Mode; |
|
||||||
use tungstenite::Error; |
|
||||||
|
|
||||||
use futures::io::{AsyncRead, AsyncWrite}; |
|
||||||
|
|
||||||
use crate::stream::Stream as StreamSwitcher; |
|
||||||
|
|
||||||
/// A stream that might be protected with TLS.
|
|
||||||
pub type MaybeTlsStream<S> = StreamSwitcher<S, TlsStream<S>>; |
|
||||||
|
|
||||||
pub type AutoStream<S> = MaybeTlsStream<S>; |
|
||||||
#[cfg(feature = "tls")] |
|
||||||
pub type Connector = async_tls::TlsConnector; |
|
||||||
#[cfg(feature = "native-tls")] |
|
||||||
pub type Connector = real_native_tls::TlsConnector; |
|
||||||
|
|
||||||
pub async fn wrap_stream<S>( |
|
||||||
socket: S, |
|
||||||
domain: String, |
|
||||||
connector: Option<Connector>, |
|
||||||
mode: Mode, |
|
||||||
) -> Result<AutoStream<S>, Error> |
|
||||||
where |
|
||||||
S: 'static + AsyncRead + AsyncWrite + Send + Unpin, |
|
||||||
{ |
|
||||||
match mode { |
|
||||||
Mode::Plain => Ok(StreamSwitcher::Plain(socket)), |
|
||||||
Mode::Tls => { |
|
||||||
#[cfg(feature = "tls")] |
|
||||||
let stream = { |
|
||||||
let connector = connector.unwrap_or_else(AsyncTlsConnector::new); |
|
||||||
connector.connect(&domain, socket)?.await? |
|
||||||
}; |
|
||||||
#[cfg(feature = "native-tls")] |
|
||||||
let stream = { |
|
||||||
let connector = if let Some(connector) = connector { |
|
||||||
connector |
|
||||||
} else { |
|
||||||
let builder = real_native_tls::TlsConnector::builder(); |
|
||||||
builder.build()? |
|
||||||
}; |
|
||||||
let connector = AsyncTlsConnector::from(connector); |
|
||||||
connector.connect(&domain, socket).await? |
|
||||||
}; |
|
||||||
Ok(StreamSwitcher::Tls(stream)) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
#[cfg(feature = "tls-base")] |
|
||||||
pub use self::encryption::MaybeTlsStream; |
|
||||||
|
|
||||||
#[cfg(not(feature = "tls-base"))] |
|
||||||
pub(crate) mod encryption { |
|
||||||
use futures::io::{AsyncRead, AsyncWrite}; |
|
||||||
|
|
||||||
use tungstenite::stream::Mode; |
|
||||||
use tungstenite::Error; |
|
||||||
|
|
||||||
pub type AutoStream<S> = S; |
|
||||||
pub type Connector = (); |
|
||||||
|
|
||||||
pub(crate) async fn wrap_stream<S>( |
|
||||||
socket: S, |
|
||||||
_domain: String, |
|
||||||
_connector: Option<()>, |
|
||||||
mode: Mode, |
|
||||||
) -> Result<AutoStream<S>, Error> |
|
||||||
where |
|
||||||
S: 'static + AsyncRead + AsyncWrite + Send + Unpin, |
|
||||||
{ |
|
||||||
match mode { |
|
||||||
Mode::Plain => Ok(socket), |
|
||||||
Mode::Tls => Err(Error::Url("TLS support not compiled in.".into())), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
use self::encryption::AutoStream; |
|
||||||
|
|
||||||
/// Get a domain from an URL.
|
|
||||||
#[inline] |
|
||||||
fn domain(request: &Request) -> Result<String, Error> { |
|
||||||
match request.url.host_str() { |
|
||||||
Some(d) => Ok(d.to_string()), |
|
||||||
None => Err(Error::Url("no host name in the url".into())), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// Creates a WebSocket handshake from a request and a stream,
|
|
||||||
/// upgrading the stream to TLS if required.
|
|
||||||
pub async fn client_async_tls<R, S>( |
|
||||||
request: R, |
|
||||||
stream: S, |
|
||||||
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
|
||||||
where |
|
||||||
R: Into<Request<'static>> + Unpin, |
|
||||||
S: 'static + AsyncRead + AsyncWrite + Send + Unpin, |
|
||||||
AutoStream<S>: Unpin, |
|
||||||
{ |
|
||||||
client_async_tls_with_connector_and_config(request, stream, None, None).await |
|
||||||
} |
|
||||||
|
|
||||||
/// Creates a WebSocket handshake from a request and a stream,
|
|
||||||
/// upgrading the stream to TLS if required and using the given
|
|
||||||
/// WebSocket configuration.
|
|
||||||
pub async fn client_async_tls_with_config<R, S>( |
|
||||||
request: R, |
|
||||||
stream: S, |
|
||||||
config: Option<WebSocketConfig>, |
|
||||||
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
|
||||||
where |
|
||||||
R: Into<Request<'static>> + Unpin, |
|
||||||
S: 'static + AsyncRead + AsyncWrite + Send + Unpin, |
|
||||||
AutoStream<S>: Unpin, |
|
||||||
{ |
|
||||||
client_async_tls_with_connector_and_config(request, stream, None, config).await |
|
||||||
} |
|
||||||
|
|
||||||
/// Creates a WebSocket handshake from a request and a stream,
|
|
||||||
/// upgrading the stream to TLS if required and using the given
|
|
||||||
/// connector.
|
|
||||||
pub async fn client_async_tls_with_connector<R, S>( |
|
||||||
request: R, |
|
||||||
stream: S, |
|
||||||
connector: Option<self::encryption::Connector>, |
|
||||||
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
|
||||||
where |
|
||||||
R: Into<Request<'static>> + Unpin, |
|
||||||
S: 'static + AsyncRead + AsyncWrite + Send + Unpin, |
|
||||||
AutoStream<S>: Unpin, |
|
||||||
{ |
|
||||||
client_async_tls_with_connector_and_config(request, stream, connector, None).await |
|
||||||
} |
|
||||||
|
|
||||||
/// Creates a WebSocket handshake from a request and a stream,
|
|
||||||
/// upgrading the stream to TLS if required and using the given
|
|
||||||
/// connector and WebSocket configuration.
|
|
||||||
pub async fn client_async_tls_with_connector_and_config<R, S>( |
|
||||||
request: R, |
|
||||||
stream: S, |
|
||||||
connector: Option<self::encryption::Connector>, |
|
||||||
config: Option<WebSocketConfig>, |
|
||||||
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
|
||||||
where |
|
||||||
R: Into<Request<'static>> + Unpin, |
|
||||||
S: 'static + AsyncRead + AsyncWrite + Send + Unpin, |
|
||||||
AutoStream<S>: Unpin, |
|
||||||
{ |
|
||||||
let request: Request = request.into(); |
|
||||||
|
|
||||||
let domain = domain(&request)?; |
|
||||||
|
|
||||||
// Make sure we check domain and mode first. URL must be valid.
|
|
||||||
let mode = url_mode(&request.url)?; |
|
||||||
|
|
||||||
let stream = self::encryption::wrap_stream(stream, domain, connector, mode).await?; |
|
||||||
client_async_with_config(request, stream, config).await |
|
||||||
} |
|
||||||
|
|
||||||
#[cfg(feature = "async_std_runtime")] |
|
||||||
pub(crate) mod async_std_runtime { |
|
||||||
use super::*; |
|
||||||
use async_std::net::TcpStream; |
|
||||||
|
|
||||||
/// Connect to a given URL.
|
|
||||||
pub async fn connect_async<R>( |
|
||||||
request: R, |
|
||||||
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error> |
|
||||||
where |
|
||||||
R: Into<Request<'static>> + Unpin, |
|
||||||
{ |
|
||||||
connect_async_with_config(request, None).await |
|
||||||
} |
|
||||||
|
|
||||||
/// Connect to a given URL with a given WebSocket configuration.
|
|
||||||
pub async fn connect_async_with_config<R>( |
|
||||||
request: R, |
|
||||||
config: Option<WebSocketConfig>, |
|
||||||
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error> |
|
||||||
where |
|
||||||
R: Into<Request<'static>> + Unpin, |
|
||||||
{ |
|
||||||
let request: Request = request.into(); |
|
||||||
|
|
||||||
let domain = domain(&request)?; |
|
||||||
let port = request |
|
||||||
.url |
|
||||||
.port_or_known_default() |
|
||||||
.expect("Bug: port unknown"); |
|
||||||
|
|
||||||
let try_socket = TcpStream::connect((domain.as_str(), port)).await; |
|
||||||
let socket = try_socket.map_err(Error::Io)?; |
|
||||||
client_async_tls_with_config(request, socket, config).await |
|
||||||
} |
|
||||||
|
|
||||||
#[cfg(any(feature = "tls", feature = "native-tls"))] |
|
||||||
/// Connect to a given URL using the provided TLS connector.
|
|
||||||
pub async fn connect_async_with_tls_connector<R>( |
|
||||||
request: R, |
|
||||||
connector: Option<super::encryption::Connector>, |
|
||||||
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error> |
|
||||||
where |
|
||||||
R: Into<Request<'static>> + Unpin, |
|
||||||
{ |
|
||||||
connect_async_with_tls_connector_and_config(request, connector, None).await |
|
||||||
} |
|
||||||
|
|
||||||
#[cfg(any(feature = "tls", feature = "native-tls"))] |
|
||||||
/// Connect to a given URL using the provided TLS connector.
|
|
||||||
pub async fn connect_async_with_tls_connector_and_config<R>( |
|
||||||
request: R, |
|
||||||
connector: Option<super::encryption::Connector>, |
|
||||||
config: Option<WebSocketConfig>, |
|
||||||
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error> |
|
||||||
where |
|
||||||
R: Into<Request<'static>> + Unpin, |
|
||||||
{ |
|
||||||
let request: Request = request.into(); |
|
||||||
|
|
||||||
let domain = domain(&request)?; |
|
||||||
let port = request |
|
||||||
.url |
|
||||||
.port_or_known_default() |
|
||||||
.expect("Bug: port unknown"); |
|
||||||
|
|
||||||
let try_socket = TcpStream::connect((domain.as_str(), port)).await; |
|
||||||
let socket = try_socket.map_err(Error::Io)?; |
|
||||||
client_async_tls_with_connector_and_config(request, socket, connector, config).await |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[cfg(feature = "async_std_runtime")] |
|
||||||
pub use async_std_runtime::{connect_async, connect_async_with_config}; |
|
||||||
#[cfg(all(
|
|
||||||
feature = "async_std_runtime", |
|
||||||
any(feature = "tls", feature = "native-tls") |
|
||||||
))] |
|
||||||
pub use async_std_runtime::{ |
|
||||||
connect_async_with_tls_connector, connect_async_with_tls_connector_and_config, |
|
||||||
}; |
|
@ -0,0 +1,137 @@ |
|||||||
|
//! `gio` integration.
|
||||||
|
use tungstenite::Error; |
||||||
|
|
||||||
|
use std::io; |
||||||
|
|
||||||
|
use gio::prelude::*; |
||||||
|
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite}; |
||||||
|
|
||||||
|
use tungstenite::client::url_mode; |
||||||
|
use tungstenite::stream::Mode; |
||||||
|
|
||||||
|
use crate::{ |
||||||
|
client_async_with_config, domain, Request, Response, WebSocketConfig, WebSocketStream, |
||||||
|
}; |
||||||
|
|
||||||
|
type MaybeTlsStream = IOStreamAsyncReadWrite<gio::SocketConnection>; |
||||||
|
|
||||||
|
/// Connect to a given URL.
|
||||||
|
pub async fn connect_async<R>( |
||||||
|
request: R, |
||||||
|
) -> Result<(WebSocketStream<MaybeTlsStream>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
{ |
||||||
|
connect_async_with_config(request, None).await |
||||||
|
} |
||||||
|
|
||||||
|
/// Connect to a given URL with a given WebSocket configuration.
|
||||||
|
pub async fn connect_async_with_config<R>( |
||||||
|
request: R, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<MaybeTlsStream>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
{ |
||||||
|
let request: Request = request.into(); |
||||||
|
|
||||||
|
let domain = domain(&request)?; |
||||||
|
let port = request |
||||||
|
.url |
||||||
|
.port_or_known_default() |
||||||
|
.expect("Bug: port unknown"); |
||||||
|
|
||||||
|
let client = gio::SocketClient::new(); |
||||||
|
|
||||||
|
// Make sure we check domain and mode first. URL must be valid.
|
||||||
|
let mode = url_mode(&request.url)?; |
||||||
|
if let Mode::Tls = mode { |
||||||
|
client.set_tls(true); |
||||||
|
} else { |
||||||
|
client.set_tls(false); |
||||||
|
} |
||||||
|
|
||||||
|
let connectable = gio::NetworkAddress::new(domain.as_str(), port); |
||||||
|
|
||||||
|
let socket = client |
||||||
|
.connect_async_future(&connectable) |
||||||
|
.await |
||||||
|
.map_err(|err| to_std_io_error(err))?; |
||||||
|
let socket = IOStreamAsyncReadWrite::new(socket) |
||||||
|
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Unsupported gio::IOStream"))?; |
||||||
|
|
||||||
|
client_async_with_config(request, socket, config).await |
||||||
|
} |
||||||
|
|
||||||
|
/// Adapter for `gio::IOStream` to provide `AsyncRead` and `AsyncWrite`.
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct IOStreamAsyncReadWrite<T: IsA<gio::IOStream>> { |
||||||
|
io_stream: T, |
||||||
|
read: gio::InputStreamAsyncRead<gio::PollableInputStream>, |
||||||
|
write: gio::OutputStreamAsyncWrite<gio::PollableOutputStream>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: IsA<gio::IOStream>> IOStreamAsyncReadWrite<T> { |
||||||
|
/// Create a new `gio::IOStream` adapter
|
||||||
|
pub fn new(stream: T) -> Result<IOStreamAsyncReadWrite<T>, T> { |
||||||
|
let write = stream |
||||||
|
.get_output_stream() |
||||||
|
.and_then(|s| s.dynamic_cast::<gio::PollableOutputStream>().ok()) |
||||||
|
.and_then(|s| s.into_async_write().ok()); |
||||||
|
|
||||||
|
let read = stream |
||||||
|
.get_input_stream() |
||||||
|
.and_then(|s| s.dynamic_cast::<gio::PollableInputStream>().ok()) |
||||||
|
.and_then(|s| s.into_async_read().ok()); |
||||||
|
|
||||||
|
let (read, write) = match (read, write) { |
||||||
|
(Some(read), Some(write)) => (read, write), |
||||||
|
_ => return Err(stream), |
||||||
|
}; |
||||||
|
|
||||||
|
Ok(IOStreamAsyncReadWrite { |
||||||
|
io_stream: stream, |
||||||
|
read, |
||||||
|
write, |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
use std::pin::Pin; |
||||||
|
use std::task::{Context, Poll}; |
||||||
|
|
||||||
|
impl<T: IsA<gio::IOStream> + Unpin> AsyncRead for IOStreamAsyncReadWrite<T> { |
||||||
|
fn poll_read( |
||||||
|
self: Pin<&mut Self>, |
||||||
|
cx: &mut Context<'_>, |
||||||
|
buf: &mut [u8], |
||||||
|
) -> Poll<Result<usize, io::Error>> { |
||||||
|
Pin::new(&mut Pin::get_mut(self).read).poll_read(cx, buf) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: IsA<gio::IOStream> + Unpin> AsyncWrite for IOStreamAsyncReadWrite<T> { |
||||||
|
fn poll_write( |
||||||
|
self: Pin<&mut Self>, |
||||||
|
cx: &mut Context<'_>, |
||||||
|
buf: &[u8], |
||||||
|
) -> Poll<Result<usize, io::Error>> { |
||||||
|
Pin::new(&mut Pin::get_mut(self).write).poll_write(cx, buf) |
||||||
|
} |
||||||
|
|
||||||
|
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> { |
||||||
|
Pin::new(&mut Pin::get_mut(self).write).poll_close(cx) |
||||||
|
} |
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> { |
||||||
|
Pin::new(&mut Pin::get_mut(self).write).poll_flush(cx) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn to_std_io_error(error: glib::Error) -> io::Error { |
||||||
|
match error.kind::<gio::IOErrorEnum>() { |
||||||
|
Some(io_error_enum) => io::Error::new(io_error_enum.into(), error), |
||||||
|
None => io::Error::new(io::ErrorKind::Other, error), |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,363 @@ |
|||||||
|
//! `tokio` integration.
|
||||||
|
use tungstenite::handshake::client::Response; |
||||||
|
use tungstenite::protocol::WebSocketConfig; |
||||||
|
use tungstenite::Error; |
||||||
|
|
||||||
|
use tokio::net::TcpStream; |
||||||
|
|
||||||
|
use super::{domain, Request, WebSocketStream}; |
||||||
|
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite}; |
||||||
|
|
||||||
|
#[cfg(feature = "tokio-tls")] |
||||||
|
pub(crate) mod tokio_tls { |
||||||
|
use real_tokio_tls::TlsConnector as AsyncTlsConnector; |
||||||
|
use real_tokio_tls::TlsStream; |
||||||
|
|
||||||
|
use tungstenite::client::url_mode; |
||||||
|
use tungstenite::stream::Mode; |
||||||
|
use tungstenite::Error; |
||||||
|
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite}; |
||||||
|
|
||||||
|
use crate::stream::Stream as StreamSwitcher; |
||||||
|
use crate::{ |
||||||
|
client_async_with_config, domain, Request, Response, WebSocketConfig, WebSocketStream, |
||||||
|
}; |
||||||
|
|
||||||
|
use super::TokioAdapter; |
||||||
|
|
||||||
|
/// A stream that might be protected with TLS.
|
||||||
|
pub type MaybeTlsStream<S> = StreamSwitcher<S, TokioAdapter<TlsStream<TokioAdapter<S>>>>; |
||||||
|
|
||||||
|
pub type AutoStream<S> = MaybeTlsStream<S>; |
||||||
|
|
||||||
|
pub type Connector = AsyncTlsConnector; |
||||||
|
|
||||||
|
async fn wrap_stream<S>( |
||||||
|
socket: S, |
||||||
|
domain: String, |
||||||
|
connector: Option<Connector>, |
||||||
|
mode: Mode, |
||||||
|
) -> Result<AutoStream<S>, Error> |
||||||
|
where |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
{ |
||||||
|
match mode { |
||||||
|
Mode::Plain => Ok(StreamSwitcher::Plain(socket)), |
||||||
|
Mode::Tls => { |
||||||
|
let stream = { |
||||||
|
let connector = if let Some(connector) = connector { |
||||||
|
connector |
||||||
|
} else { |
||||||
|
let connector = real_native_tls::TlsConnector::builder().build()?; |
||||||
|
AsyncTlsConnector::from(connector) |
||||||
|
}; |
||||||
|
connector |
||||||
|
.connect(&domain, TokioAdapter(socket)) |
||||||
|
.await |
||||||
|
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))? |
||||||
|
}; |
||||||
|
Ok(StreamSwitcher::Tls(TokioAdapter(stream))) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required and using the given
|
||||||
|
/// connector and WebSocket configuration.
|
||||||
|
pub async fn client_async_tls_with_connector_and_config<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
connector: Option<AsyncTlsConnector>, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
let request: Request = request.into(); |
||||||
|
|
||||||
|
let domain = domain(&request)?; |
||||||
|
|
||||||
|
// Make sure we check domain and mode first. URL must be valid.
|
||||||
|
let mode = url_mode(&request.url)?; |
||||||
|
|
||||||
|
let stream = wrap_stream(stream, domain, connector, mode).await?; |
||||||
|
client_async_with_config(request, stream, config).await |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(not(any(feature = "async-tls", feature = "tokio-tls")))] |
||||||
|
pub(crate) mod dummy_tls { |
||||||
|
use futures::io::{AsyncRead, AsyncWrite}; |
||||||
|
|
||||||
|
use tungstenite::client::url_mode; |
||||||
|
use tungstenite::stream::Mode; |
||||||
|
use tungstenite::Error; |
||||||
|
|
||||||
|
use crate::{ |
||||||
|
client_async_with_config, domain, Request, Response, WebSocketConfig, WebSocketStream, |
||||||
|
}; |
||||||
|
|
||||||
|
pub type AutoStream<S> = S; |
||||||
|
type Connector = (); |
||||||
|
|
||||||
|
async fn wrap_stream<S>( |
||||||
|
socket: S, |
||||||
|
_domain: String, |
||||||
|
_connector: Option<()>, |
||||||
|
mode: Mode, |
||||||
|
) -> Result<AutoStream<S>, Error> |
||||||
|
where |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
{ |
||||||
|
match mode { |
||||||
|
Mode::Plain => Ok(socket), |
||||||
|
Mode::Tls => Err(Error::Url("TLS support not compiled in.".into())), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) async fn client_async_tls_with_connector_and_config<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
connector: Option<Connector>, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
let request: Request = request.into(); |
||||||
|
|
||||||
|
let domain = domain(&request)?; |
||||||
|
|
||||||
|
// Make sure we check domain and mode first. URL must be valid.
|
||||||
|
let mode = url_mode(&request.url)?; |
||||||
|
|
||||||
|
let stream = wrap_stream(stream, domain, connector, mode).await?; |
||||||
|
client_async_with_config(request, stream, config).await |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(not(any(feature = "async-tls", feature = "tokio-tls")))] |
||||||
|
use self::dummy_tls::{client_async_tls_with_connector_and_config, AutoStream}; |
||||||
|
|
||||||
|
#[cfg(all(feature = "async-tls", not(feature = "tokio-tls")))] |
||||||
|
use crate::async_tls::{client_async_tls_with_connector_and_config, AutoStream}; |
||||||
|
#[cfg(all(feature = "async-tls", not(feature = "tokio-tls")))] |
||||||
|
type Connector = real_async_tls::TlsConnector; |
||||||
|
|
||||||
|
#[cfg(feature = "tokio-tls")] |
||||||
|
use self::tokio_tls::{client_async_tls_with_connector_and_config, AutoStream, Connector}; |
||||||
|
|
||||||
|
#[cfg(feature = "tokio-tls")] |
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required.
|
||||||
|
pub async fn client_async_tls<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
client_async_tls_with_connector_and_config(request, stream, None, None).await |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(feature = "tokio-tls")] |
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required and using the given
|
||||||
|
/// WebSocket configuration.
|
||||||
|
pub async fn client_async_tls_with_config<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
client_async_tls_with_connector_and_config(request, stream, None, config).await |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(feature = "tokio-tls")] |
||||||
|
/// Creates a WebSocket handshake from a request and a stream,
|
||||||
|
/// upgrading the stream to TLS if required and using the given
|
||||||
|
/// connector.
|
||||||
|
pub async fn client_async_tls_with_connector<R, S>( |
||||||
|
request: R, |
||||||
|
stream: S, |
||||||
|
connector: Option<Connector>, |
||||||
|
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
S: 'static + AsyncRead + AsyncWrite + Unpin, |
||||||
|
AutoStream<S>: Unpin, |
||||||
|
{ |
||||||
|
client_async_tls_with_connector_and_config(request, stream, connector, None).await |
||||||
|
} |
||||||
|
|
||||||
|
/// Connect to a given URL.
|
||||||
|
pub async fn connect_async<R>( |
||||||
|
request: R, |
||||||
|
) -> Result< |
||||||
|
( |
||||||
|
WebSocketStream<AutoStream<TokioAdapter<TcpStream>>>, |
||||||
|
Response, |
||||||
|
), |
||||||
|
Error, |
||||||
|
> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
{ |
||||||
|
connect_async_with_config(request, None).await |
||||||
|
} |
||||||
|
|
||||||
|
/// Connect to a given URL with a given WebSocket configuration.
|
||||||
|
pub async fn connect_async_with_config<R>( |
||||||
|
request: R, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result< |
||||||
|
( |
||||||
|
WebSocketStream<AutoStream<TokioAdapter<TcpStream>>>, |
||||||
|
Response, |
||||||
|
), |
||||||
|
Error, |
||||||
|
> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
{ |
||||||
|
let request: Request = request.into(); |
||||||
|
|
||||||
|
let domain = domain(&request)?; |
||||||
|
let port = request |
||||||
|
.url |
||||||
|
.port_or_known_default() |
||||||
|
.expect("Bug: port unknown"); |
||||||
|
|
||||||
|
let try_socket = TcpStream::connect((domain.as_str(), port)).await; |
||||||
|
let socket = try_socket.map_err(Error::Io)?; |
||||||
|
client_async_tls_with_connector_and_config(request, TokioAdapter(socket), None, config).await |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(any(feature = "async-tls", feature = "tokio-tls"))] |
||||||
|
/// Connect to a given URL using the provided TLS connector.
|
||||||
|
pub async fn connect_async_with_tls_connector<R>( |
||||||
|
request: R, |
||||||
|
connector: Option<Connector>, |
||||||
|
) -> Result< |
||||||
|
( |
||||||
|
WebSocketStream<AutoStream<TokioAdapter<TcpStream>>>, |
||||||
|
Response, |
||||||
|
), |
||||||
|
Error, |
||||||
|
> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
{ |
||||||
|
connect_async_with_tls_connector_and_config(request, connector, None).await |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(any(feature = "async-tls", feature = "tokio-tls"))] |
||||||
|
/// Connect to a given URL using the provided TLS connector.
|
||||||
|
pub async fn connect_async_with_tls_connector_and_config<R>( |
||||||
|
request: R, |
||||||
|
connector: Option<Connector>, |
||||||
|
config: Option<WebSocketConfig>, |
||||||
|
) -> Result< |
||||||
|
( |
||||||
|
WebSocketStream<AutoStream<TokioAdapter<TcpStream>>>, |
||||||
|
Response, |
||||||
|
), |
||||||
|
Error, |
||||||
|
> |
||||||
|
where |
||||||
|
R: Into<Request<'static>> + Unpin, |
||||||
|
{ |
||||||
|
let request: Request = request.into(); |
||||||
|
|
||||||
|
let domain = domain(&request)?; |
||||||
|
let port = request |
||||||
|
.url |
||||||
|
.port_or_known_default() |
||||||
|
.expect("Bug: port unknown"); |
||||||
|
|
||||||
|
let try_socket = TcpStream::connect((domain.as_str(), port)).await; |
||||||
|
let socket = try_socket.map_err(Error::Io)?; |
||||||
|
client_async_tls_with_connector_and_config(request, TokioAdapter(socket), connector, config) |
||||||
|
.await |
||||||
|
} |
||||||
|
|
||||||
|
use pin_project::pin_project; |
||||||
|
use std::pin::Pin; |
||||||
|
use std::task::{Context, Poll}; |
||||||
|
|
||||||
|
/// Adapter for `tokio::io::AsyncRead` and `tokio::io::AsyncWrite` to provide
|
||||||
|
/// the variants from the `futures` crate and the other way around.
|
||||||
|
#[pin_project] |
||||||
|
#[derive(Debug, Clone)] |
||||||
|
pub struct TokioAdapter<T>(#[pin] pub T); |
||||||
|
|
||||||
|
impl<T: tokio::io::AsyncRead> AsyncRead for TokioAdapter<T> { |
||||||
|
fn poll_read( |
||||||
|
self: Pin<&mut Self>, |
||||||
|
cx: &mut Context<'_>, |
||||||
|
buf: &mut [u8], |
||||||
|
) -> Poll<std::io::Result<usize>> { |
||||||
|
self.project().0.poll_read(cx, buf) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: tokio::io::AsyncWrite> AsyncWrite for TokioAdapter<T> { |
||||||
|
fn poll_write( |
||||||
|
self: Pin<&mut Self>, |
||||||
|
cx: &mut Context<'_>, |
||||||
|
buf: &[u8], |
||||||
|
) -> Poll<Result<usize, std::io::Error>> { |
||||||
|
self.project().0.poll_write(cx, buf) |
||||||
|
} |
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> { |
||||||
|
self.project().0.poll_flush(cx) |
||||||
|
} |
||||||
|
|
||||||
|
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> { |
||||||
|
self.project().0.poll_shutdown(cx) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: AsyncRead> tokio::io::AsyncRead for TokioAdapter<T> { |
||||||
|
fn poll_read( |
||||||
|
self: Pin<&mut Self>, |
||||||
|
cx: &mut Context<'_>, |
||||||
|
buf: &mut [u8], |
||||||
|
) -> Poll<std::io::Result<usize>> { |
||||||
|
self.project().0.poll_read(cx, buf) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: AsyncWrite> tokio::io::AsyncWrite for TokioAdapter<T> { |
||||||
|
fn poll_write( |
||||||
|
self: Pin<&mut Self>, |
||||||
|
cx: &mut Context<'_>, |
||||||
|
buf: &[u8], |
||||||
|
) -> Poll<Result<usize, std::io::Error>> { |
||||||
|
self.project().0.poll_write(cx, buf) |
||||||
|
} |
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> { |
||||||
|
self.project().0.poll_flush(cx) |
||||||
|
} |
||||||
|
|
||||||
|
fn poll_shutdown( |
||||||
|
self: Pin<&mut Self>, |
||||||
|
cx: &mut Context<'_>, |
||||||
|
) -> Poll<Result<(), std::io::Error>> { |
||||||
|
self.project().0.poll_close(cx) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue