diff --git a/src/connection.rs b/src/connection.rs index 3458cacd2..6ad4cd07e 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -2123,6 +2123,59 @@ impl Connection { buf.into() } + /// Close a connection immediately + /// + /// This does not ensure delivery of outstanding data. It is the application's responsibility to call this only when + /// all important communications have been completed. + pub fn close( + &mut self, + ctx: &mut Context, + now: u64, + conn: ConnectionHandle, + error_code: u16, + reason: Bytes, + ) { + if let State::Established(ref mut state) = *self.state.as_mut().unwrap() { + // Inform OpenSSL that the connection is being closed gracefully. This ensures that a resumable session is + // not erased from the anti-replay cache as it otherwise might be. + state.tls.shutdown().unwrap(); + } + + let was_closed = self.state.as_ref().unwrap().is_closed(); + let reason = + state::CloseReason::Application(frame::ApplicationClose { error_code, reason }); + if !was_closed { + self.close_common(ctx, now, conn); + ctx.io.push_back(Io::Transmit { + destination: self.remote, + packet: self.make_close(&reason), + }); + self.reset_idle_timeout(&ctx.config, now); + ctx.dirty_conns.insert(conn); + } + self.state = Some(match self.state.take().unwrap() { + State::Handshake(_) => State::HandshakeFailed(state::HandshakeFailed { + reason, + alert: None, + app_closed: true, + }), + State::HandshakeFailed(x) => State::HandshakeFailed(state::HandshakeFailed { + app_closed: true, + ..x + }), + State::Established(_) => State::Closed(state::Closed { + reason, + app_closed: true, + }), + State::Closed(x) => State::Closed(state::Closed { + app_closed: true, + ..x + }), + State::Draining(_) => State::Draining(state::Draining { app_closed: true }), + State::Drained => unreachable!(), + }); + } + pub fn close_common(&mut self, ctx: &mut Context, now: u64, conn: ConnectionHandle) { trace!(ctx.log, "connection closed"); self.set_loss_detection = Some(None); diff --git a/src/endpoint.rs b/src/endpoint.rs index 53e18e901..d5f9be594 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -1028,48 +1028,7 @@ impl Endpoint { self.forget(conn); return; } - - if let State::Established(ref mut state) = *self.connections[conn.0].state.as_mut().unwrap() - { - // Inform OpenSSL that the connection is being closed gracefully. This ensures that a resumable session is - // not erased from the anti-replay cache as it otherwise might be. - state.tls.shutdown().unwrap(); - } - - let was_closed = self.connections[conn.0].state.as_ref().unwrap().is_closed(); - let reason = - state::CloseReason::Application(frame::ApplicationClose { error_code, reason }); - if !was_closed { - self.connections[conn.0].close_common(&mut self.ctx, now, conn); - self.ctx.io.push_back(Io::Transmit { - destination: self.connections[conn.0].remote, - packet: self.connections[conn.0].make_close(&reason), - }); - self.connections[conn.0].reset_idle_timeout(&self.ctx.config, now); - self.ctx.dirty_conns.insert(conn); - } - self.connections[conn.0].state = - Some(match self.connections[conn.0].state.take().unwrap() { - State::Handshake(_) => State::HandshakeFailed(state::HandshakeFailed { - reason, - alert: None, - app_closed: true, - }), - State::HandshakeFailed(x) => State::HandshakeFailed(state::HandshakeFailed { - app_closed: true, - ..x - }), - State::Established(_) => State::Closed(state::Closed { - reason, - app_closed: true, - }), - State::Closed(x) => State::Closed(state::Closed { - app_closed: true, - ..x - }), - State::Draining(_) => State::Draining(state::Draining { app_closed: true }), - State::Drained => unreachable!(), - }); + self.connections[conn.0].close(&mut self.ctx, now, conn, error_code, reason); } /// Look up whether we're the client or server of `conn`.