Skip to content

Commit 8764318

Browse files
committed
Added even more docs and examples.
1 parent 23aa593 commit 8764318

File tree

10 files changed

+723
-18
lines changed

10 files changed

+723
-18
lines changed

src/client/async.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,49 @@
1+
//! Contains the asynchronous websocket client.
2+
//!
3+
//! The async client is simply a `Stream + Sink` of `OwnedMessage` structs.
4+
//! This definition means that you don't have to learn any new APIs other than
5+
//! futures-rs.
6+
//! The client simply wraps around an `AsyncRead + AsyncWrite` stream and uses
7+
//! a `MessageCodec` to chop up the bytes into websocket messages.
8+
//! See the `codec` module for all the cool codecs this crate has.
9+
//!
10+
//! Since this is all asynchronous, you will not create a client from `ClientBuilder`
11+
//! but instead you will create a `ClientNew` struct, which is a Future that
12+
//! will eventually evaluate to a `Client`.
13+
//!
14+
//! # Example with Type Annotations
15+
//!
16+
//! ```rust,no_run
17+
//! # extern crate tokio_core;
18+
//! # extern crate futures;
19+
//! # extern crate websocket;
20+
//! use websocket::ClientBuilder;
21+
//! use websocket::async::client::{Client, ClientNew};
22+
//! use websocket::async::TcpStream;
23+
//! use websocket::futures::{Future, Stream, Sink};
24+
//! use websocket::Message;
25+
//! use tokio_core::reactor::Core;
26+
//! # fn main() {
27+
//!
28+
//! let mut core = Core::new().unwrap();
29+
//!
30+
//! // create a Future of a client
31+
//! let client_future: ClientNew<TcpStream> =
32+
//! ClientBuilder::new("ws://echo.websocket.org").unwrap()
33+
//! .async_connect_insecure(&core.handle());
34+
//!
35+
//! // send a message
36+
//! let send_future = client_future
37+
//! .and_then(|(client, headers)| {
38+
//! // just to make it clear what type this is
39+
//! let client: Client<TcpStream> = client;
40+
//! client.send(Message::text("hallo").into())
41+
//! });
42+
//!
43+
//! core.run(send_future).unwrap();
44+
//! # }
45+
//! ```
46+
147
pub use tokio_core::reactor::Handle;
248
pub use tokio_io::codec::Framed;
349
pub use tokio_core::net::TcpStream;
@@ -11,6 +57,19 @@ use message::OwnedMessage;
1157
#[cfg(feature="async-ssl")]
1258
pub use tokio_tls::TlsStream;
1359

60+
/// An asynchronous websocket client.
61+
///
62+
/// This is simply a `Stream` and `Sink` of `OwnedMessage`s.
63+
/// See the docs for `Stream` and `Sink` to learn more about how to use
64+
/// these futures.
1465
pub type Client<S> = Framed<S, MessageCodec<OwnedMessage>>;
1566

67+
/// A future which will evaluate to a `Client` and a set of hyper `Headers`.
68+
///
69+
/// The `Client` can send and receive websocket messages, and the Headers are
70+
/// the headers that came back from the server handshake.
71+
/// If the user used a protocol or attached some other headers check these response
72+
/// headers to see if the server accepted the protocol or other custom header.
73+
/// This crate will not automatically close the connection if the server refused
74+
/// to use the user protocols given to it, you must check that the server accepted.
1675
pub type ClientNew<S> = Box<Future<Item = (Client<S>, Headers), Error = WebSocketError>>;

src/client/builder.rs

Lines changed: 156 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ impl<'u> ClientBuilder<'u> {
398398
}
399399

400400
/// Create an insecure (plain TCP) connection to the client.
401-
/// In this case no `Box` will be used you will just get a TcpStream,
401+
/// In this case no `Box` will be used, you will just get a TcpStream,
402402
/// giving you the ability to split the stream into a reader and writer
403403
/// (since SSL streams cannot be cloned).
404404
///
@@ -419,9 +419,9 @@ impl<'u> ClientBuilder<'u> {
419419
}
420420

421421
/// Create an SSL connection to the sever.
422-
/// This will only use an `SslStream`, this is useful
422+
/// This will only use an `TlsStream`, this is useful
423423
/// when you want to be sure to connect over SSL or when you want access
424-
/// to the `SslStream` functions (without having to go through a `Box`).
424+
/// to the `TlsStream` functions (without having to go through a `Box`).
425425
#[cfg(feature="sync-ssl")]
426426
pub fn connect_secure(
427427
&mut self,
@@ -434,7 +434,6 @@ impl<'u> ClientBuilder<'u> {
434434
self.connect_on(ssl_stream)
435435
}
436436

437-
// TODO: similar ability for server?
438437
/// Connects to a websocket server on any stream you would like.
439438
/// Possible streams:
440439
/// - Unix Sockets
@@ -483,6 +482,50 @@ impl<'u> ClientBuilder<'u> {
483482
Ok(Client::unchecked(reader, response.headers, true, false))
484483
}
485484

485+
/// Connect to a websocket server asynchronously.
486+
///
487+
/// This will use a `Box<AsyncRead + AsyncWrite + Send>` to represent either
488+
/// an SSL connection or a normal TCP connection, what to use will be decided
489+
/// using the protocol of the URL passed in (e.g. `ws://` or `wss://`)
490+
///
491+
/// If you have non-default SSL circumstances, you can use the `ssl_config`
492+
/// parameter to configure those.
493+
///
494+
///# Example
495+
///
496+
/// ```rust,no_run
497+
/// # extern crate rand;
498+
/// # extern crate tokio_core;
499+
/// # extern crate futures;
500+
/// # extern crate websocket;
501+
/// use websocket::ClientBuilder;
502+
/// use websocket::futures::{Future, Stream, Sink};
503+
/// use websocket::Message;
504+
/// use tokio_core::reactor::Core;
505+
/// # use rand::Rng;
506+
///
507+
/// # fn main() {
508+
/// let mut core = Core::new().unwrap();
509+
///
510+
/// // let's randomly do either SSL or plaintext
511+
/// let url = if rand::thread_rng().gen() {
512+
/// "ws://echo.websocket.org"
513+
/// } else {
514+
/// "wss://echo.websocket.org"
515+
/// };
516+
///
517+
/// // send a message and hear it come back
518+
/// let echo_future = ClientBuilder::new(url).unwrap()
519+
/// .async_connect(None, &core.handle())
520+
/// .and_then(|(s, _)| s.send(Message::text("hallo").into()))
521+
/// .and_then(|s| s.into_future().map_err(|e| e.0))
522+
/// .map(|(m, _)| {
523+
/// assert_eq!(m, Some(Message::text("hallo").into()))
524+
/// });
525+
///
526+
/// core.run(echo_future).unwrap();
527+
/// # }
528+
/// ```
486529
#[cfg(feature="async-ssl")]
487530
pub fn async_connect(
488531
self,
@@ -536,6 +579,41 @@ impl<'u> ClientBuilder<'u> {
536579
}
537580
}
538581

582+
/// Asynchronously create an SSL connection to a websocket sever.
583+
///
584+
/// This method will only try to connect over SSL and fail otherwise, useful
585+
/// when you want to be sure to connect over SSL or when you want access
586+
/// to the `TlsStream` functions (without having to go through a `Box`).
587+
///
588+
/// If you have non-default SSL circumstances, you can use the `ssl_config`
589+
/// parameter to configure those.
590+
///
591+
///# Example
592+
///
593+
/// ```rust
594+
/// # extern crate tokio_core;
595+
/// # extern crate futures;
596+
/// # extern crate websocket;
597+
/// use websocket::ClientBuilder;
598+
/// use websocket::futures::{Future, Stream, Sink};
599+
/// use websocket::Message;
600+
/// use tokio_core::reactor::Core;
601+
/// # fn main() {
602+
///
603+
/// let mut core = Core::new().unwrap();
604+
///
605+
/// // send a message and hear it come back
606+
/// let echo_future = ClientBuilder::new("wss://echo.websocket.org").unwrap()
607+
/// .async_connect_secure(None, &core.handle())
608+
/// .and_then(|(s, _)| s.send(Message::text("hallo").into()))
609+
/// .and_then(|s| s.into_future().map_err(|e| e.0))
610+
/// .map(|(m, _)| {
611+
/// assert_eq!(m, Some(Message::text("hallo").into()))
612+
/// });
613+
///
614+
/// core.run(echo_future).unwrap();
615+
/// # }
616+
/// ```
539617
#[cfg(feature="async-ssl")]
540618
pub fn async_connect_secure(
541619
self,
@@ -578,6 +656,38 @@ impl<'u> ClientBuilder<'u> {
578656

579657
// TODO: add timeout option for connecting
580658
// TODO: add conveniences like .response_to_pings, .send_close, etc.
659+
/// Asynchronously create an insecure (plain TCP) connection to the client.
660+
///
661+
/// In this case no `Box` will be used, you will just get a `TcpStream`,
662+
/// giving you less allocations on the heap and direct access to `TcpStream`
663+
/// functions.
664+
///
665+
///# Example
666+
///
667+
/// ```rust,no_run
668+
/// # extern crate tokio_core;
669+
/// # extern crate futures;
670+
/// # extern crate websocket;
671+
/// use websocket::ClientBuilder;
672+
/// use websocket::futures::{Future, Stream, Sink};
673+
/// use websocket::Message;
674+
/// use tokio_core::reactor::Core;
675+
/// # fn main() {
676+
///
677+
/// let mut core = Core::new().unwrap();
678+
///
679+
/// // send a message and hear it come back
680+
/// let echo_future = ClientBuilder::new("ws://echo.websocket.org").unwrap()
681+
/// .async_connect_insecure(&core.handle())
682+
/// .and_then(|(s, _)| s.send(Message::text("hallo").into()))
683+
/// .and_then(|s| s.into_future().map_err(|e| e.0))
684+
/// .map(|(m, _)| {
685+
/// assert_eq!(m, Some(Message::text("hallo").into()))
686+
/// });
687+
///
688+
/// core.run(echo_future).unwrap();
689+
/// # }
690+
/// ```
581691
#[cfg(feature="async")]
582692
pub fn async_connect_insecure(self, handle: &Handle) -> async::ClientNew<async::TcpStream> {
583693
let tcp_stream = match self.async_tcpstream(Some(false), handle) {
@@ -600,6 +710,48 @@ impl<'u> ClientBuilder<'u> {
600710
Box::new(future)
601711
}
602712

713+
/// Asynchronously connects to a websocket server on any stream you would like.
714+
/// Possible streams:
715+
/// - Unix Sockets
716+
/// - Bluetooth
717+
/// - Logging Middle-ware
718+
/// - SSH
719+
///
720+
/// The stream must be `AsyncRead + AsyncWrite + Send + 'static`.
721+
///
722+
/// # Example
723+
///
724+
/// ```rust
725+
/// use websocket::header::WebSocketProtocol;
726+
/// use websocket::ClientBuilder;
727+
/// use websocket::sync::stream::ReadWritePair;
728+
/// use websocket::futures::Future;
729+
/// use websocket::async::Core;
730+
/// # use std::io::Cursor;
731+
///
732+
/// let mut core = Core::new().unwrap();
733+
///
734+
/// let accept = b"\
735+
/// HTTP/1.1 101 Switching Protocols\r\n\
736+
/// Upgrade: websocket\r\n\
737+
/// Sec-WebSocket-Protocol: proto-metheus\r\n\
738+
/// Connection: Upgrade\r\n\
739+
/// Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\
740+
/// \r\n";
741+
///
742+
/// let input = Cursor::new(&accept[..]);
743+
/// let output = Cursor::new(Vec::new());
744+
///
745+
/// let client = ClientBuilder::new("wss://test.ws").unwrap()
746+
/// .key(b"the sample nonce".clone())
747+
/// .async_connect_on(ReadWritePair(input, output))
748+
/// .map(|(_, headers)| {
749+
/// let proto: &WebSocketProtocol = headers.get().unwrap();
750+
/// assert_eq!(proto.0.first().unwrap(), "proto-metheus")
751+
/// });
752+
///
753+
/// core.run(client).unwrap();
754+
/// ```
603755
#[cfg(feature="async")]
604756
pub fn async_connect_on<S>(self, stream: S) -> async::ClientNew<S>
605757
where S: stream::async::Stream + Send + 'static

src/client/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
//! Build and use asynchronously or synchronous websocket clients.
2+
//!
3+
//! This crate is split up into a synchronous and asynchronous half.
4+
//! These can be turned on and off by switching the `sync` and `async` features
5+
//! on and off (plus `sync-ssl` and `async-ssl` for SSL connections).
6+
//!
7+
//! In general pick a style you would like to write in and use `ClientBuilder`
8+
//! to create your websocket connections. Use the `.async_connect` functions to create
9+
//! async connections, and the normal `.connect` functions for synchronous clients.
10+
//! The `ClientBuilder` creates both async and sync connections, the actual sync and
11+
//! async clients live in the `client::sync` and `client::async` modules, respectively.
12+
//!
13+
//! Many of the useful things from this module will be hoisted and re-exported under the
14+
//! `websocket::{sync, async}::client` module which will have all sync or all async things.
15+
116
pub mod builder;
217
pub use self::builder::{ClientBuilder, Url, ParseError};
318

0 commit comments

Comments
 (0)