close: implement complete close handshake

pull/7/head
Alexey Galakhov 8 years ago
parent caba37c41e
commit 4714773370
  1. 4
      src/error.rs
  2. 29
      src/protocol/mod.rs

@ -16,6 +16,8 @@ pub type Result<T> = result::Result<T, Error>;
/// Possible WebSocket errors /// Possible WebSocket errors
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// WebSocket connection closed (normally)
ConnectionClosed,
/// Input-output error /// Input-output error
Io(io::Error), Io(io::Error),
/// Buffer capacity exhausted /// Buffer capacity exhausted
@ -33,6 +35,7 @@ pub enum Error {
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
Error::ConnectionClosed => write!(f, "Connection closed"),
Error::Io(ref err) => write!(f, "IO error: {}", err), Error::Io(ref err) => write!(f, "IO error: {}", err),
Error::Capacity(ref msg) => write!(f, "Space limit exceeded: {}", msg), Error::Capacity(ref msg) => write!(f, "Space limit exceeded: {}", msg),
Error::Protocol(ref msg) => write!(f, "WebSocket protocol error: {}", msg), Error::Protocol(ref msg) => write!(f, "WebSocket protocol error: {}", msg),
@ -46,6 +49,7 @@ impl fmt::Display for Error {
impl ErrorTrait for Error { impl ErrorTrait for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
Error::ConnectionClosed => "",
Error::Io(ref err) => err.description(), Error::Io(ref err) => err.description(),
Error::Capacity(ref msg) => msg.borrow(), Error::Capacity(ref msg) => msg.borrow(),
Error::Protocol(ref msg) => msg.borrow(), Error::Protocol(ref msg) => msg.borrow(),

@ -82,7 +82,8 @@ impl<Stream> WebSocket<Stream>
match self.state { match self.state {
WebSocketState::Active => { WebSocketState::Active => {
self.state = WebSocketState::ClosedByUs; self.state = WebSocketState::ClosedByUs;
// TODO let frame = Frame::close(None);
self.send_queue.push_back(frame);
} }
_ => { _ => {
// already closed, nothing to do // already closed, nothing to do
@ -220,7 +221,6 @@ impl<Stream> WebSocket<Stream>
} // match opcode } // match opcode
} else { } else {
//Ok(None) // TODO handle EOF?
Err(Error::Protocol("Connection reset without closing handshake".into())) Err(Error::Protocol("Connection reset without closing handshake".into()))
} }
} }
@ -240,25 +240,26 @@ impl<Stream> WebSocket<Stream>
Frame::close(None) Frame::close(None)
}; };
self.send_queue.push_back(reply); self.send_queue.push_back(reply);
Ok(())
} }
WebSocketState::ClosedByPeer => { WebSocketState::ClosedByPeer => {
// It is already closed, just ignore. // It is already closed, just ignore.
Ok(())
} }
WebSocketState::ClosedByUs => { WebSocketState::ClosedByUs => {
// We received a reply. // We received a reply.
match self.role { match self.role {
Role::Client => { Role::Client => {
// Client waits for the server to close the connection. // Client waits for the server to close the connection.
Ok(())
} }
Role::Server => { Role::Server => {
// Server closes the connection. // Server closes the connection.
// TODO Err(Error::ConnectionClosed)
} }
} }
} }
} }
//unimplemented!()
Ok(())
} }
/// Received a ping frame. /// Received a ping frame.
@ -294,7 +295,23 @@ impl<Stream> WebSocket<Stream>
while let Some(data) = self.send_queue.pop_front() { while let Some(data) = self.send_queue.pop_front() {
self.send_one_frame(data)?; self.send_one_frame(data)?;
} }
Ok(())
// If we're closing and there is nothing to send anymore, we should close the connection.
match self.state {
WebSocketState::ClosedByPeer if self.send_queue.is_empty() => {
// The underlying TCP connection, in most normal cases, SHOULD be closed
// first by the server, so that it holds the TIME_WAIT state and not the
// client (as this would prevent it from re-opening the connection for 2
// maximum segment lifetimes (2MSL), while there is no corresponding
// server impact as a TIME_WAIT connection is immediately reopened upon
// a new SYN with a higher seq number). (RFC 6455)
match self.role {
Role::Client => Ok(()),
Role::Server => Err(Error::ConnectionClosed),
}
}
_ => Ok(()),
}
} }
/// Send a single pending frame. /// Send a single pending frame.

Loading…
Cancel
Save