diff --git a/src/error.rs b/src/error.rs index ec86b0d3..8629efcc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -25,30 +25,39 @@ 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. 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 169b1ff1..2765d369 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) }