From f818322a25b38ba6d4e2b1bcc8b20e1a530486c7 Mon Sep 17 00:00:00 2001 From: Naja Melan Date: Sat, 14 Sep 2019 16:30:42 +0200 Subject: [PATCH 1/2] Add some documentation to WebSocket and Error. --- src/error.rs | 36 +++++++++++++++++---------- src/protocol/mod.rs | 59 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/error.rs b/src/error.rs index ec86b0d..c3f8c91 100644 --- a/src/error.rs +++ b/src/error.rs @@ -25,30 +25,40 @@ pub type Result = result::Result; /// Possible WebSocket errors #[derive(Debug)] pub enum Error { - /// WebSocket connection closed normally + /// WebSocket connection closed normally. This informs you of the close. + /// It's not an error as such and nothing wrong happened. /// - /// Upon receiving this, the server must drop the WebSocket object as soon as possible - /// to close the connection. - /// The client gets this error if the connection is already closed at the server side. + /// This is returned as soon as the close handshake is finished (we have both sent and + /// received a close frame) on the server end and as soon as the server has closed the + /// underlying connection if this endpoint is a client. /// - /// Receiving this error means that the WebSocket object is not usable anymore and the only - /// meaningful action with it is dropping it. + /// Thus when you receive this, it is safe to drop the underlying connection. + /// + /// Receiving this error means that the WebSocket object is not usable anymore and the + /// only meaningful action with it is dropping it. ConnectionClosed, - /// Trying to work with already closed connection + /// Trying to work with already closed connection. + /// + /// Trying to read or write after receiving `ConnectionClosed` causes this. /// - /// Trying to write after receiving `Message::Close` or trying to read after receiving - /// `Error::ConnectionClosed` causes this. + /// As opposed to `ConnectionClosed`, this indicates your code tries to operate on the + /// connection when it really shouldn't anymore, so this really indicates a programmer + /// error on your part. AlreadyClosed, - /// Input-output error + /// Input-output error. Appart from WouldBlock, these are generally errors with the + /// underlying connection and you should probably consider them fatal. Io(io::Error), #[cfg(feature = "tls")] /// TLS error Tls(tls::Error), - /// Buffer capacity exhausted + /// - When reading: buffer capacity exhausted. + /// - When writing: your message is bigger than the configured max message size + /// (64MB by default). Capacity(Cow<'static, str>), - /// Protocol violation + /// Protocol violation. Only returned from reads, if the remote caused a protocol + /// violation. Messages you send are currently not checked for protocol validity. Protocol(Cow<'static, str>), - /// Message send queue full + /// Message send queue full. SendQueueFull(Message), /// UTF coding error Utf8, diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 169b1ff..2765d36 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -115,8 +115,18 @@ impl WebSocket { impl WebSocket { /// Read a message from stream, if possible. /// - /// This function sends pong and close responses automatically. - /// However, it never blocks on write. + /// This will queue responses to ping and close messages to be sent. It will call + /// `write_pending` before trying to read in order to make sure that those responses + /// make progress even if you never call `write_pending`. That does mean that they + /// get sent out earliest on the next call to `read_message`, `write_message` or `write_pending`. + /// + /// ## Closing the connection + /// When the remote endpoint decides to close the connection this will return + /// the close message with an optional close frame. + /// + /// You should continue calling `read_message`, `write_message` or `write_pending` to drive + /// the reply to the close frame until [Error::ConnectionClosed] is returned. Once that happens + /// it is safe to drop the underlying connection. pub fn read_message(&mut self) -> Result { self.context.read_message(&mut self.socket) } @@ -124,11 +134,30 @@ impl WebSocket { /// Send a message to stream, if possible. /// /// WebSocket will buffer a configurable number of messages at a time, except to reply to Ping - /// and Close requests. If the WebSocket's send queue is full, `SendQueueFull` will be returned - /// along with the passed message. Otherwise, the message is queued and Ok(()) is returned. + /// requests. A Pong reply will jump the queue because the + /// [websocket RFC](https://tools.ietf.org/html/rfc6455#section-5.5.2) specifies it should be sent + /// as soon as is practical. /// - /// Note that only the last pong frame is stored to be sent, and only the - /// most recent pong frame is sent if multiple pong frames are queued. + /// Note that upon receiving a ping message, tungstenite cues a pong reply automatically. + /// When you call either `read_message`, `write_message` or `write_pending` next it will try to send + /// that pong out if the underlying connection can take more data. This means you should not + /// respond to ping frames manually. + /// + /// You can however send pong frames manually in order to indicate a unidirectional heartbeat + /// as described in [RFC 6455](https://tools.ietf.org/html/rfc6455#section-5.5.3). Note that + /// if `read_message` returns a ping, you should call `write_pending` until it doesn't return + /// WouldBlock before passing a pong to `write_message`, otherwise the response to the + /// ping will not be sent, but rather replaced by your custom pong message. + /// + /// ## Errors + /// - If the WebSocket's send queue is full, `SendQueueFull` will be returned + /// along with the passed message. Otherwise, the message is queued and Ok(()) is returned. + /// - If the connection is closed and should be dropped, this will return [Error::ConnectionClosed]. + /// - If you try again after [Error::ConnectionClosed] was returned either from here or from `read_message`, + /// [Error::AlreadyClosed] will be returned. This indicates a program error on your part. + /// - [Error::Io] is returned if the underlying connection returns an error + /// (consider these fatal except for WouldBlock). + /// - [Error::Capacity] if your message size is bigger than the configured max message size. pub fn write_message(&mut self, message: Message) -> Result<()> { self.context.write_message(&mut self.socket, message) } @@ -142,7 +171,23 @@ impl WebSocket { /// /// This function guarantees that the close frame will be queued. /// There is no need to call it again. Calling this function is - /// the same as calling `write(Message::Close(..))`. + /// the same as calling `write_message(Message::Close(..))`. + /// + /// After queing the close frame you should continue calling `read_message` or + /// `write_pending` to drive the close handshake to completion. + /// + /// The websocket RFC defines that the underlying connection should be closed + /// by the server. Tungstenite takes care of this asymmetry for you. + /// + /// When the close handshake is finished (we have both sent and received + /// a close message), `read_message` or `write_pending` will return + /// [Error::ConnectionClosed] if this endpoint is the server. + /// + /// If this endpoint is a client, [Error::ConnectionClosed] will only be + /// returned after the server has closed the underlying connection. + /// + /// It is thus safe to drop the underlying connection as soon as [Error::ConnectionClosed] + /// is returned from `read_message` or `write_pending`. pub fn close(&mut self, code: Option) -> Result<()> { self.context.close(&mut self.socket, code) } From b807f76ed17eb31440b697f0998ca00fac0ecc87 Mon Sep 17 00:00:00 2001 From: Daniel Abramov Date: Fri, 20 Sep 2019 12:52:08 +0200 Subject: [PATCH 2/2] Update error description Remove the statement which is not valid anymore. --- src/error.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index c3f8c91..8629efc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -55,8 +55,7 @@ pub enum Error { /// - When writing: your message is bigger than the configured max message size /// (64MB by default). Capacity(Cow<'static, str>), - /// Protocol violation. Only returned from reads, if the remote caused a protocol - /// violation. Messages you send are currently not checked for protocol validity. + /// Protocol violation. Protocol(Cow<'static, str>), /// Message send queue full. SendQueueFull(Message),