Merge pull request #490 from sfackler/shutdown

Add a shutdown method
This commit is contained in:
Steven Fackler 2016-10-25 20:53:43 -07:00 committed by GitHub
commit 44010d7f19
3 changed files with 64 additions and 1 deletions

View File

@ -1556,6 +1556,7 @@ extern {
#[cfg(not(ossl101))]
pub fn SSL_get0_param(ssl: *mut SSL) -> *mut X509_VERIFY_PARAM;
pub fn SSL_get_verify_result(ssl: *const SSL) -> c_long;
pub fn SSL_shutdown(ssl: *mut SSL) -> c_int;
#[cfg(not(osslconf = "OPENSSL_NO_COMP"))]
pub fn SSL_COMP_get_name(comp: *const COMP_METHOD) -> *const c_char;

View File

@ -1278,6 +1278,26 @@ impl<S: Read + Write> SslStream<S> {
Err(self.make_error(ret))
}
}
/// Shuts down the session.
///
/// The shutdown process consists of two steps. The first step sends a
/// close notify message to the peer, after which `ShutdownResult::Sent`
/// is returned. The second step awaits the receipt of a close notify
/// message from the peer, after which `ShutdownResult::Received` is
/// returned.
///
/// While the connection may be closed after the first step, it is
/// recommended to fully shut the session down. In particular, it must
/// be fully shut down if the connection is to be used for further
/// communication in the future.
pub fn shutdown(&mut self) -> Result<ShutdownResult, Error> {
match unsafe { ffi::SSL_shutdown(self.ssl.as_ptr()) } {
0 => Ok(ShutdownResult::Sent),
1 => Ok(ShutdownResult::Received),
n => Err(self.make_error(n)),
}
}
}
impl<S> SslStream<S> {
@ -1383,6 +1403,16 @@ impl<S: Read + Write> Write for SslStream<S> {
}
}
/// The result of a shutdown request.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ShutdownResult {
/// A close notify message has been sent to the peer.
Sent,
/// A close notify response message has been received from the peer.
Received,
}
#[cfg(ossl110)]
mod compat {
use std::ptr;

View File

@ -19,7 +19,7 @@ use ssl;
use ssl::SSL_VERIFY_PEER;
use ssl::{SslMethod, HandshakeError};
use ssl::error::Error;
use ssl::{SslContext, SslStream, Ssl};
use ssl::{SslContext, SslStream, Ssl, ShutdownResult};
use x509::X509StoreContextRef;
use x509::X509FileType;
use x509::X509;
@ -1084,6 +1084,38 @@ fn invalid_hostname() {
assert!(ssl.connect(s).is_err());
}
#[test]
fn shutdown() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let port = listener.local_addr().unwrap().port();
thread::spawn(move || {
let stream = listener.accept().unwrap().0;
let mut ctx = SslContext::new(SslMethod::tls()).unwrap();
ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM).unwrap();
ctx.set_private_key_file(&Path::new("test/key.pem"), X509FileType::PEM).unwrap();
let ssl = Ssl::new(&ctx).unwrap();
let mut stream = ssl.accept(stream).unwrap();
stream.write_all(b"hello").unwrap();
let mut buf = [0; 1];
assert_eq!(stream.read(&mut buf).unwrap(), 0);
assert_eq!(stream.shutdown().unwrap(), ShutdownResult::Received);
});
let stream = TcpStream::connect(("127.0.0.1", port)).unwrap();
let ctx = SslContext::new(SslMethod::tls()).unwrap();
let ssl = Ssl::new(&ctx).unwrap();
let mut stream = ssl.connect(stream).unwrap();
let mut buf = [0; 5];
stream.read_exact(&mut buf).unwrap();
assert_eq!(b"hello", &buf);
assert_eq!(stream.shutdown().unwrap(), ShutdownResult::Sent);
assert_eq!(stream.shutdown().unwrap(), ShutdownResult::Received);
}
fn _check_kinds() {
fn is_send<T: Send>() {}
fn is_sync<T: Sync>() {}