Skip to content

Commit ed76885

Browse files
ihciahkornelski
authored andcommitted
fix: BIO_set_retry_write when BIO_CTRL_FLUSH to allow writer returns WouldBlock on flush
1 parent b65a064 commit ed76885

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
@@ -163,9 +163,13 @@ unsafe extern "C" fn ctrl<S: Write>(
163163
let state = state::<S>(bio);
164164

165165
if cmd == BIO_CTRL_FLUSH {
166+
BIO_clear_retry_flags(bio);
166167
match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) {
167168
Ok(Ok(())) => 1,
168169
Ok(Err(err)) => {
170+
if retriable_error(&err) {
171+
BIO_set_retry_write(bio);
172+
}
169173
state.error = Some(err);
170174
0
171175
}

boring/src/ssl/test/mod.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,68 @@ fn test_select_cert_alpn_extension() {
417417
);
418418
}
419419

420+
#[test]
421+
fn test_io_retry() {
422+
#[derive(Debug)]
423+
struct RetryStream {
424+
inner: TcpStream,
425+
first_read: bool,
426+
first_write: bool,
427+
first_flush: bool,
428+
}
429+
430+
impl Read for RetryStream {
431+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
432+
if mem::replace(&mut self.first_read, false) {
433+
Err(io::Error::new(io::ErrorKind::WouldBlock, "first read"))
434+
} else {
435+
self.inner.read(buf)
436+
}
437+
}
438+
}
439+
440+
impl Write for RetryStream {
441+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
442+
if mem::replace(&mut self.first_write, false) {
443+
Err(io::Error::new(io::ErrorKind::WouldBlock, "first write"))
444+
} else {
445+
self.inner.write(buf)
446+
}
447+
}
448+
449+
fn flush(&mut self) -> io::Result<()> {
450+
if mem::replace(&mut self.first_flush, false) {
451+
Err(io::Error::new(io::ErrorKind::WouldBlock, "first flush"))
452+
} else {
453+
self.inner.flush()
454+
}
455+
}
456+
}
457+
458+
let server = Server::builder().build();
459+
460+
let stream = RetryStream {
461+
inner: server.connect_tcp(),
462+
first_read: true,
463+
first_write: true,
464+
first_flush: true,
465+
};
466+
467+
let ctx = SslContext::builder(SslMethod::tls()).unwrap();
468+
let mut s = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
469+
Ok(mut s) => return s.read_exact(&mut [0]).unwrap(),
470+
Err(HandshakeError::WouldBlock(s)) => s,
471+
Err(_) => panic!("should not fail on setup"),
472+
};
473+
loop {
474+
match s.handshake() {
475+
Ok(mut s) => return s.read_exact(&mut [0]).unwrap(),
476+
Err(HandshakeError::WouldBlock(mid_s)) => s = mid_s,
477+
Err(_) => panic!("should not fail on handshake"),
478+
}
479+
}
480+
}
481+
420482
#[test]
421483
#[should_panic(expected = "blammo")]
422484
fn write_panic() {

0 commit comments

Comments
 (0)