Skip to content

Commit c55c73d

Browse files
committed
fix: BIO_set_retry_write when BIO_CTRL_FLUSH to allow writer returns WouldBlock on flush
1 parent 7cfe206 commit c55c73d

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

boring/src/ssl/bio.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,13 @@ unsafe extern "C" fn ctrl<S: Write>(
166166
let state = state::<S>(bio);
167167

168168
if cmd == BIO_CTRL_FLUSH {
169+
BIO_clear_retry_flags(bio);
169170
match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) {
170171
Ok(Ok(())) => 1,
171172
Ok(Err(err)) => {
173+
if retriable_error(&err) {
174+
BIO_set_retry_write(bio);
175+
}
172176
state.error = Some(err);
173177
0
174178
}

boring/src/ssl/test/mod.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,68 @@ fn test_select_cert_alpn_extension() {
563563
);
564564
}
565565

566+
#[test]
567+
fn test_io_retry() {
568+
#[derive(Debug)]
569+
struct RetryStream {
570+
inner: TcpStream,
571+
first_read: bool,
572+
first_write: bool,
573+
first_flush: bool,
574+
}
575+
576+
impl Read for RetryStream {
577+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
578+
if mem::replace(&mut self.first_read, false) {
579+
Err(io::Error::new(io::ErrorKind::WouldBlock, "first read"))
580+
} else {
581+
self.inner.read(buf)
582+
}
583+
}
584+
}
585+
586+
impl Write for RetryStream {
587+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
588+
if mem::replace(&mut self.first_write, false) {
589+
Err(io::Error::new(io::ErrorKind::WouldBlock, "first write"))
590+
} else {
591+
self.inner.write(buf)
592+
}
593+
}
594+
595+
fn flush(&mut self) -> io::Result<()> {
596+
if mem::replace(&mut self.first_flush, false) {
597+
Err(io::Error::new(io::ErrorKind::WouldBlock, "first flush"))
598+
} else {
599+
self.inner.flush()
600+
}
601+
}
602+
}
603+
604+
let server = Server::builder().build();
605+
606+
let stream = RetryStream {
607+
inner: server.connect_tcp(),
608+
first_read: true,
609+
first_write: true,
610+
first_flush: true,
611+
};
612+
613+
let ctx = SslContext::builder(SslMethod::tls()).unwrap();
614+
let mut s = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
615+
Ok(mut s) => return s.read_exact(&mut [0]).unwrap(),
616+
Err(HandshakeError::WouldBlock(s)) => s,
617+
Err(_) => panic!("should not fail on setup"),
618+
};
619+
loop {
620+
match s.handshake() {
621+
Ok(mut s) => return s.read_exact(&mut [0]).unwrap(),
622+
Err(HandshakeError::WouldBlock(mid_s)) => s = mid_s,
623+
Err(_) => panic!("should not fail on handshake"),
624+
}
625+
}
626+
}
627+
566628
#[test]
567629
#[should_panic(expected = "blammo")]
568630
fn write_panic() {

0 commit comments

Comments
 (0)