Skip to content

Commit c1d690d

Browse files
committed
Introduce AsyncStreamBridge
This encapsulates a bit better the unsafety of task context management to invoke async code from inside boring.
1 parent 2b71560 commit c1d690d

2 files changed

Lines changed: 141 additions & 108 deletions

File tree

tokio-boring/src/bridge.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//! Bridge between sync IO traits and async tokio IO traits.
2+
3+
use std::fmt;
4+
use std::io;
5+
use std::pin::Pin;
6+
use std::task::{Context, Poll, Waker};
7+
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
8+
9+
pub(crate) struct AsyncStreamBridge<S> {
10+
pub(crate) stream: S,
11+
waker: Option<Waker>,
12+
}
13+
14+
impl<S> AsyncStreamBridge<S> {
15+
pub(crate) fn new(stream: S) -> Self
16+
where
17+
S: AsyncRead + AsyncWrite + Unpin,
18+
{
19+
Self {
20+
stream,
21+
waker: None,
22+
}
23+
}
24+
25+
pub(crate) fn set_waker(&mut self, ctx: Option<&mut Context<'_>>) {
26+
self.waker = ctx.map(|ctx| ctx.waker().clone())
27+
}
28+
29+
/// # Panics
30+
///
31+
/// Panics if the bridge has no waker.
32+
pub(crate) fn with_context<F, R>(&mut self, f: F) -> R
33+
where
34+
S: Unpin,
35+
F: FnOnce(&mut Context<'_>, Pin<&mut S>) -> R,
36+
{
37+
let mut ctx =
38+
Context::from_waker(self.waker.as_ref().expect("missing task context pointer"));
39+
40+
f(&mut ctx, Pin::new(&mut self.stream))
41+
}
42+
}
43+
44+
impl<S> io::Read for AsyncStreamBridge<S>
45+
where
46+
S: AsyncRead + Unpin,
47+
{
48+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
49+
self.with_context(|ctx, stream| {
50+
let mut buf = ReadBuf::new(buf);
51+
52+
match stream.poll_read(ctx, &mut buf)? {
53+
Poll::Ready(()) => Ok(buf.filled().len()),
54+
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
55+
}
56+
})
57+
}
58+
}
59+
60+
impl<S> io::Write for AsyncStreamBridge<S>
61+
where
62+
S: AsyncWrite + Unpin,
63+
{
64+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
65+
match self.with_context(|ctx, stream| stream.poll_write(ctx, buf)) {
66+
Poll::Ready(r) => r,
67+
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
68+
}
69+
}
70+
71+
fn flush(&mut self) -> io::Result<()> {
72+
match self.with_context(|ctx, stream| stream.poll_flush(ctx)) {
73+
Poll::Ready(r) => r,
74+
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
75+
}
76+
}
77+
}
78+
79+
impl<S> fmt::Debug for AsyncStreamBridge<S>
80+
where
81+
S: fmt::Debug,
82+
{
83+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
84+
fmt::Debug::fmt(&self.stream, fmt)
85+
}
86+
}

tokio-boring/src/lib.rs

Lines changed: 55 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ use std::pin::Pin;
2727
use std::task::{Context, Poll};
2828
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
2929

30+
mod bridge;
31+
32+
use self::bridge::AsyncStreamBridge;
33+
3034
/// Asynchronously performs a client-side TLS handshake over the provided stream.
3135
pub async fn connect<S>(
3236
config: ConnectConfiguration,
@@ -48,94 +52,18 @@ where
4852
}
4953

5054
async fn handshake<S>(
51-
f: impl FnOnce(StreamWrapper<S>) -> Result<MidHandshakeSslStream<StreamWrapper<S>>, ErrorStack>,
55+
f: impl FnOnce(
56+
AsyncStreamBridge<S>,
57+
) -> Result<MidHandshakeSslStream<AsyncStreamBridge<S>>, ErrorStack>,
5258
stream: S,
5359
) -> Result<SslStream<S>, HandshakeError<S>>
5460
where
5561
S: AsyncRead + AsyncWrite + Unpin,
5662
{
57-
let ongoing_handshake = Some(
58-
f(StreamWrapper { stream, context: 0 })
59-
.map_err(|err| HandshakeError(ssl::HandshakeError::SetupFailure(err)))?,
60-
);
61-
62-
HandshakeFuture(ongoing_handshake).await
63-
}
64-
65-
struct StreamWrapper<S> {
66-
stream: S,
67-
context: usize,
68-
}
69-
70-
impl<S> StreamWrapper<S> {
71-
/// # Safety
72-
///
73-
/// Must be called with `context` set to a valid pointer to a live `Context` object, and the
74-
/// wrapper must be pinned in memory.
75-
unsafe fn parts(&mut self) -> (Pin<&mut S>, &mut Context<'_>) {
76-
debug_assert_ne!(self.context, 0);
77-
let stream = Pin::new_unchecked(&mut self.stream);
78-
let context = &mut *(self.context as *mut _);
79-
(stream, context)
80-
}
81-
}
82-
83-
impl<S> fmt::Debug for StreamWrapper<S>
84-
where
85-
S: fmt::Debug,
86-
{
87-
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
88-
fmt::Debug::fmt(&self.stream, fmt)
89-
}
90-
}
91-
92-
impl<S> StreamWrapper<S>
93-
where
94-
S: Unpin,
95-
{
96-
fn with_context<F, R>(&mut self, f: F) -> R
97-
where
98-
F: FnOnce(&mut Context<'_>, Pin<&mut S>) -> R,
99-
{
100-
unsafe {
101-
assert_ne!(self.context, 0);
102-
let waker = &mut *(self.context as *mut _);
103-
f(waker, Pin::new(&mut self.stream))
104-
}
105-
}
106-
}
107-
108-
impl<S> Read for StreamWrapper<S>
109-
where
110-
S: AsyncRead + Unpin,
111-
{
112-
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
113-
let (stream, cx) = unsafe { self.parts() };
114-
let mut buf = ReadBuf::new(buf);
115-
match stream.poll_read(cx, &mut buf)? {
116-
Poll::Ready(()) => Ok(buf.filled().len()),
117-
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
118-
}
119-
}
120-
}
63+
let mid_handshake = f(AsyncStreamBridge::new(stream))
64+
.map_err(|err| HandshakeError(ssl::HandshakeError::SetupFailure(err)))?;
12165

122-
impl<S> Write for StreamWrapper<S>
123-
where
124-
S: AsyncWrite + Unpin,
125-
{
126-
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
127-
match self.with_context(|ctx, stream| stream.poll_write(ctx, buf)) {
128-
Poll::Ready(r) => r,
129-
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
130-
}
131-
}
132-
133-
fn flush(&mut self) -> io::Result<()> {
134-
match self.with_context(|ctx, stream| stream.poll_flush(ctx)) {
135-
Poll::Ready(r) => r,
136-
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
137-
}
138-
}
66+
HandshakeFuture(Some(mid_handshake)).await
13967
}
14068

14169
fn cvt<T>(r: io::Result<T>) -> Poll<io::Result<T>> {
@@ -154,7 +82,7 @@ fn cvt<T>(r: io::Result<T>) -> Poll<io::Result<T>> {
15482
/// data. Bytes read from a `SslStream` are decrypted from `S` and bytes written
15583
/// to a `SslStream` are encrypted when passing through to `S`.
15684
#[derive(Debug)]
157-
pub struct SslStream<S>(ssl::SslStream<StreamWrapper<S>>);
85+
pub struct SslStream<S>(ssl::SslStream<AsyncStreamBridge<S>>);
15886

15987
impl<S> SslStream<S> {
16088
/// Returns a shared reference to the `Ssl` object associated with this stream.
@@ -172,14 +100,21 @@ impl<S> SslStream<S> {
172100
&mut self.0.get_mut().stream
173101
}
174102

175-
fn with_context<F, R>(&mut self, ctx: &mut Context<'_>, f: F) -> R
103+
fn run_in_context<F, R>(&mut self, ctx: &mut Context<'_>, f: F) -> R
176104
where
177-
F: FnOnce(&mut ssl::SslStream<StreamWrapper<S>>) -> R,
105+
F: FnOnce(&mut ssl::SslStream<AsyncStreamBridge<S>>) -> R,
178106
{
179-
self.0.get_mut().context = ctx as *mut _ as usize;
180-
let r = f(&mut self.0);
181-
self.0.get_mut().context = 0;
182-
r
107+
self.0.get_mut().set_waker(Some(ctx));
108+
109+
let result = f(&mut self.0);
110+
111+
// NOTE(nox): This should also be executed when `f` panics,
112+
// but it's not that important as boring segfaults on panics
113+
// and we always set the context prior to doing anything with
114+
// the inner async stream.
115+
self.0.get_mut().set_waker(None);
116+
117+
result
183118
}
184119
}
185120

@@ -195,8 +130,10 @@ where
195130
///
196131
/// The caller must ensure the pointer is valid.
197132
pub unsafe fn from_raw_parts(ssl: *mut ffi::SSL, stream: S) -> Self {
198-
let stream = StreamWrapper { stream, context: 0 };
199-
SslStream(ssl::SslStream::from_raw_parts(ssl, stream))
133+
Self(ssl::SslStream::from_raw_parts(
134+
ssl,
135+
AsyncStreamBridge::new(stream),
136+
))
200137
}
201138
}
202139

@@ -209,7 +146,7 @@ where
209146
ctx: &mut Context<'_>,
210147
buf: &mut ReadBuf,
211148
) -> Poll<io::Result<()>> {
212-
self.with_context(ctx, |s| {
149+
self.run_in_context(ctx, |s| {
213150
// This isn't really "proper", but rust-openssl doesn't currently expose a suitable interface even though
214151
// OpenSSL itself doesn't require the buffer to be initialized. So this is good enough for now.
215152
let slice = unsafe {
@@ -239,15 +176,15 @@ where
239176
ctx: &mut Context,
240177
buf: &[u8],
241178
) -> Poll<io::Result<usize>> {
242-
self.with_context(ctx, |s| cvt(s.write(buf)))
179+
self.run_in_context(ctx, |s| cvt(s.write(buf)))
243180
}
244181

245182
fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<io::Result<()>> {
246-
self.with_context(ctx, |s| cvt(s.flush()))
183+
self.run_in_context(ctx, |s| cvt(s.flush()))
247184
}
248185

249186
fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<io::Result<()>> {
250-
match self.with_context(ctx, |s| s.shutdown()) {
187+
match self.run_in_context(ctx, |s| s.shutdown()) {
251188
Ok(ShutdownResult::Sent) | Ok(ShutdownResult::Received) => {}
252189
Err(ref e) if e.code() == ErrorCode::ZERO_RETURN => {}
253190
Err(ref e) if e.code() == ErrorCode::WANT_READ || e.code() == ErrorCode::WANT_WRITE => {
@@ -265,7 +202,7 @@ where
265202
}
266203

267204
/// The error type returned after a failed handshake.
268-
pub struct HandshakeError<S>(ssl::HandshakeError<StreamWrapper<S>>);
205+
pub struct HandshakeError<S>(ssl::HandshakeError<AsyncStreamBridge<S>>);
269206

270207
impl<S> HandshakeError<S> {
271208
/// Returns a shared reference to the `Ssl` object associated with this error.
@@ -333,33 +270,43 @@ where
333270
}
334271
}
335272

336-
struct HandshakeFuture<S>(Option<MidHandshakeSslStream<StreamWrapper<S>>>);
273+
/// Future for an ongoing TLS handshake.
274+
///
275+
/// See [`connect`] and [`accept`].
276+
pub struct HandshakeFuture<S>(Option<MidHandshakeSslStream<AsyncStreamBridge<S>>>);
337277

338278
impl<S> Future for HandshakeFuture<S>
339279
where
340280
S: AsyncRead + AsyncWrite + Unpin,
341281
{
342282
type Output = Result<SslStream<S>, HandshakeError<S>>;
343283

344-
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
345-
let mut s = self.0.take().expect("future polled after completion");
284+
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
285+
let mut mid_handshake = self.0.take().expect("future polled after completion");
346286

347-
s.get_mut().context = cx as *mut _ as usize;
287+
mid_handshake.get_mut().set_waker(Some(ctx));
348288

349-
match s.handshake() {
350-
Ok(mut s) => {
351-
s.get_mut().context = 0;
289+
match mid_handshake.handshake() {
290+
Ok(mut stream) => {
291+
stream.get_mut().set_waker(None);
352292

353-
Poll::Ready(Ok(SslStream(s)))
293+
Poll::Ready(Ok(SslStream(stream)))
354294
}
355-
Err(ssl::HandshakeError::WouldBlock(mut s)) => {
356-
s.get_mut().context = 0;
295+
Err(ssl::HandshakeError::WouldBlock(mut mid_handshake)) => {
296+
mid_handshake.get_mut().set_waker(None);
357297

358-
self.0 = Some(s);
298+
self.0 = Some(mid_handshake);
359299

360300
Poll::Pending
361301
}
362-
Err(e) => Poll::Ready(Err(HandshakeError(e))),
302+
Err(ssl::HandshakeError::Failure(mut mid_handshake)) => {
303+
mid_handshake.get_mut().set_waker(None);
304+
305+
Poll::Ready(Err(HandshakeError(ssl::HandshakeError::Failure(
306+
mid_handshake,
307+
))))
308+
}
309+
Err(err @ ssl::HandshakeError::SetupFailure(_)) => Poll::Ready(Err(HandshakeError(err))),
363310
}
364311
}
365312
}

0 commit comments

Comments
 (0)