Implement read and write

This commit is contained in:
Steven Fackler 2015-12-09 22:02:02 -08:00
parent 9ee6f1c578
commit 8f56897043
4 changed files with 148 additions and 96 deletions

View File

@ -55,6 +55,8 @@ extern {
pub fn BIO_set_retry_read(b: *mut BIO); pub fn BIO_set_retry_read(b: *mut BIO);
#[link_name = "BIO_set_retry_write_shim"] #[link_name = "BIO_set_retry_write_shim"]
pub fn BIO_set_retry_write(b: *mut BIO); pub fn BIO_set_retry_write(b: *mut BIO);
#[link_name = "BIO_flush"]
pub fn BIO_flush(b: *mut BIO) -> c_long;
pub fn SSL_CTX_set_options_shim(ctx: *mut SSL_CTX, options: c_long) -> c_long; pub fn SSL_CTX_set_options_shim(ctx: *mut SSL_CTX, options: c_long) -> c_long;
pub fn SSL_CTX_get_options_shim(ctx: *mut SSL_CTX) -> c_long; pub fn SSL_CTX_get_options_shim(ctx: *mut SSL_CTX) -> c_long;
pub fn SSL_CTX_clear_options_shim(ctx: *mut SSL_CTX, options: c_long) -> c_long; pub fn SSL_CTX_clear_options_shim(ctx: *mut SSL_CTX, options: c_long) -> c_long;

View File

@ -103,6 +103,10 @@ void BIO_set_retry_write_shim(BIO *b) {
BIO_set_retry_write(b); BIO_set_retry_write(b);
} }
long BIO_flush_shim(BIO *b) {
return BIO_flush(b);
}
long SSL_CTX_set_options_shim(SSL_CTX *ctx, long options) { long SSL_CTX_set_options_shim(SSL_CTX *ctx, long options) {
return SSL_CTX_set_options(ctx, options); return SSL_CTX_set_options(ctx, options);
} }

View File

@ -1350,102 +1350,6 @@ impl<S: Read+Write> Write for SslStream<S> {
} }
} }
pub struct SslStreamNg<S> {
ssl: Ssl,
_method: Box<ffi::BIO_METHOD>, // :(
_p: PhantomData<S>,
}
impl<S> Drop for SslStreamNg<S> {
fn drop(&mut self) {
unsafe {
let _ = bio::take_stream::<S>(self.ssl.get_raw_rbio());
}
}
}
impl<S: Read + Write> SslStreamNg<S> {
fn new_base(ssl: Ssl, stream: S) -> Result<Self, SslError> {
unsafe {
let (bio, method) = try!(bio::new(stream));
ffi::SSL_set_bio(ssl.ssl, bio, bio);
Ok(SslStreamNg {
ssl: ssl,
_method: method,
_p: PhantomData,
})
}
}
/// Creates an SSL/TLS client operating over the provided stream.
pub fn connect<T: IntoSsl>(ssl: T, stream: S) -> Result<Self, SslError> {
let ssl = try!(ssl.into_ssl());
let mut stream = try!(Self::new_base(ssl, stream));
let ret = stream.ssl.connect();
if ret > 0 {
Ok(stream)
} else {
Err(stream.make_error(ret))
}
}
/// Creates an SSL/TLS server operating over the provided stream.
pub fn accept<T: IntoSsl>(ssl: T, stream: S) -> Result<Self, SslError> {
let ssl = try!(ssl.into_ssl());
let mut stream = try!(Self::new_base(ssl, stream));
let ret = stream.ssl.accept();
if ret > 0 {
Ok(stream)
} else {
Err(stream.make_error(ret))
}
}
pub fn get_ref(&self) -> &S {
unsafe {
let bio = self.ssl.get_raw_rbio();
bio::get_ref(bio)
}
}
pub fn mut_ref(&mut self) -> &mut S {
unsafe {
let bio = self.ssl.get_raw_rbio();
bio::get_mut(bio)
}
}
fn make_error(&mut self, ret: c_int) -> SslError {
match self.ssl.get_error(ret) {
LibSslError::ErrorSsl => SslError::get(),
LibSslError::ErrorSyscall => {
let err = SslError::get();
let count = match err {
SslError::OpenSslErrors(ref v) => v.len(),
_ => unreachable!(),
};
if count == 0 {
if ret == 0 {
SslError::StreamError(io::Error::new(io::ErrorKind::ConnectionAborted,
"unexpected EOF observed"))
} else {
let error = unsafe { bio::take_error::<S>(self.ssl.get_raw_rbio()) };
SslError::StreamError(error.unwrap())
}
} else {
err
}
}
LibSslError::ErrorWantWrite | LibSslError::ErrorWantRead => {
let error = unsafe { bio::take_error::<S>(self.ssl.get_raw_rbio()) };
SslError::StreamError(error.unwrap())
}
err => panic!("unexpected error {:?} with ret {}", err, ret),
}
}
}
pub trait IntoSsl { pub trait IntoSsl {
fn into_ssl(self) -> Result<Ssl, SslError>; fn into_ssl(self) -> Result<Ssl, SslError>;
} }
@ -1756,3 +1660,133 @@ impl<S: Read+Write> NonblockingSslStream<S> {
} }
} }
} }
pub struct SslStreamNg<S> {
ssl: Ssl,
_method: Box<ffi::BIO_METHOD>, // :(
_p: PhantomData<S>,
}
impl<S> Drop for SslStreamNg<S> {
fn drop(&mut self) {
unsafe {
let _ = bio::take_stream::<S>(self.ssl.get_raw_rbio());
}
}
}
impl<S: Read + Write> SslStreamNg<S> {
fn new_base(ssl: Ssl, stream: S) -> Result<Self, SslError> {
unsafe {
let (bio, method) = try!(bio::new(stream));
ffi::SSL_set_bio(ssl.ssl, bio, bio);
Ok(SslStreamNg {
ssl: ssl,
_method: method,
_p: PhantomData,
})
}
}
/// Creates an SSL/TLS client operating over the provided stream.
pub fn connect<T: IntoSsl>(ssl: T, stream: S) -> Result<Self, SslError> {
let ssl = try!(ssl.into_ssl());
let mut stream = try!(Self::new_base(ssl, stream));
let ret = stream.ssl.connect();
if ret > 0 {
Ok(stream)
} else {
Err(stream.make_error(ret))
}
}
/// Creates an SSL/TLS server operating over the provided stream.
pub fn accept<T: IntoSsl>(ssl: T, stream: S) -> Result<Self, SslError> {
let ssl = try!(ssl.into_ssl());
let mut stream = try!(Self::new_base(ssl, stream));
let ret = stream.ssl.accept();
if ret > 0 {
Ok(stream)
} else {
Err(stream.make_error(ret))
}
}
}
impl<S> SslStreamNg<S> {
pub fn get_ref(&self) -> &S {
unsafe {
let bio = self.ssl.get_raw_rbio();
bio::get_ref(bio)
}
}
pub fn get_mut(&mut self) -> &mut S {
unsafe {
let bio = self.ssl.get_raw_rbio();
bio::get_mut(bio)
}
}
fn make_error(&mut self, ret: c_int) -> SslError {
match self.ssl.get_error(ret) {
LibSslError::ErrorSsl => SslError::get(),
LibSslError::ErrorSyscall => {
let err = SslError::get();
let count = match err {
SslError::OpenSslErrors(ref v) => v.len(),
_ => unreachable!(),
};
if count == 0 {
if ret == 0 {
SslError::StreamError(io::Error::new(io::ErrorKind::ConnectionAborted,
"unexpected EOF observed"))
} else {
let error = unsafe { bio::take_error::<S>(self.ssl.get_raw_rbio()) };
SslError::StreamError(error.unwrap())
}
} else {
err
}
}
LibSslError::ErrorWantWrite | LibSslError::ErrorWantRead => {
let error = unsafe { bio::take_error::<S>(self.ssl.get_raw_rbio()) };
SslError::StreamError(error.unwrap())
}
err => panic!("unexpected error {:?} with ret {}", err, ret),
}
}
}
impl<S: Read> Read for SslStreamNg<S> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let ret = self.ssl.read(buf);
if ret >= 0 {
return Ok(ret as usize);
}
match self.make_error(ret) {
SslError::StreamError(e) => Err(e),
e => Err(io::Error::new(io::ErrorKind::Other, e)),
}
}
}
impl<S: Write> Write for SslStreamNg<S> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let ret = self.ssl.write(buf);
if ret > 0 {
return Ok(ret as usize);
}
match self.make_error(ret) {
SslError::StreamError(e) => Err(e),
e => Err(io::Error::new(io::ErrorKind::Other, e)),
}
}
fn flush(&mut self) -> io::Result<()> {
self.get_mut().flush()
}
}

View File

@ -936,3 +936,15 @@ fn ng_connect() {
let ctx = SslContext::new(Sslv23).unwrap(); let ctx = SslContext::new(Sslv23).unwrap();
SslStreamNg::connect(&ctx, stream).unwrap(); SslStreamNg::connect(&ctx, stream).unwrap();
} }
#[test]
fn ng_get() {
let (_s, stream) = Server::new();
let ctx = SslContext::new(Sslv23).unwrap();
let mut stream = SslStreamNg::connect(&ctx, stream).unwrap();
stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
let mut resp = String::new();
stream.read_to_string(&mut resp).unwrap();
assert!(resp.starts_with("HTTP/1.0 200"));
assert!(resp.ends_with("</HTML>\r\n\r\n"));
}