Skip to content

Commit 2cd512d

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 b71a7c4 commit 2cd512d

2 files changed

Lines changed: 133 additions & 107 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: 47 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ use std::pin::Pin;
2828
use std::task::{Context, Poll};
2929
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
3030

31+
mod bridge;
32+
33+
use self::bridge::AsyncStreamBridge;
34+
3135
/// Asynchronously performs a client-side TLS handshake over the provided stream.
3236
pub fn connect<S>(
3337
config: ConnectConfiguration,
@@ -49,91 +53,15 @@ where
4953
}
5054

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

13967
fn cvt<T>(r: io::Result<T>) -> Poll<io::Result<T>> {
@@ -152,7 +80,7 @@ fn cvt<T>(r: io::Result<T>) -> Poll<io::Result<T>> {
15280
/// data. Bytes read from a `SslStream` are decrypted from `S` and bytes written
15381
/// to a `SslStream` are encrypted when passing through to `S`.
15482
#[derive(Debug)]
155-
pub struct SslStream<S>(ssl::SslStream<StreamWrapper<S>>);
83+
pub struct SslStream<S>(ssl::SslStream<AsyncStreamBridge<S>>);
15684

15785
impl<S> SslStream<S> {
15886
/// Returns a shared reference to the `Ssl` object associated with this stream.
@@ -170,14 +98,21 @@ impl<S> SslStream<S> {
17098
&mut self.0.get_mut().stream
17199
}
172100

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

@@ -193,8 +128,10 @@ where
193128
///
194129
/// The caller must ensure the pointer is valid.
195130
pub unsafe fn from_raw_parts(ssl: *mut ffi::SSL, stream: S) -> Self {
196-
let stream = StreamWrapper { stream, context: 0 };
197-
SslStream(ssl::SslStream::from_raw_parts(ssl, stream))
131+
Self(ssl::SslStream::from_raw_parts(
132+
ssl,
133+
AsyncStreamBridge::new(stream),
134+
))
198135
}
199136
}
200137

@@ -207,7 +144,7 @@ where
207144
ctx: &mut Context<'_>,
208145
buf: &mut ReadBuf,
209146
) -> Poll<io::Result<()>> {
210-
self.with_context(ctx, |s| {
147+
self.run_in_context(ctx, |s| {
211148
// This isn't really "proper", but rust-openssl doesn't currently expose a suitable interface even though
212149
// OpenSSL itself doesn't require the buffer to be initialized. So this is good enough for now.
213150
let slice = unsafe {
@@ -237,15 +174,15 @@ where
237174
ctx: &mut Context,
238175
buf: &[u8],
239176
) -> Poll<io::Result<usize>> {
240-
self.with_context(ctx, |s| cvt(s.write(buf)))
177+
self.run_in_context(ctx, |s| cvt(s.write(buf)))
241178
}
242179

243180
fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<io::Result<()>> {
244-
self.with_context(ctx, |s| cvt(s.flush()))
181+
self.run_in_context(ctx, |s| cvt(s.flush()))
245182
}
246183

247184
fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<io::Result<()>> {
248-
match self.with_context(ctx, |s| s.shutdown()) {
185+
match self.run_in_context(ctx, |s| s.shutdown()) {
249186
Ok(ShutdownResult::Sent) | Ok(ShutdownResult::Received) => {}
250187
Err(ref e) if e.code() == ErrorCode::ZERO_RETURN => {}
251188
Err(ref e) if e.code() == ErrorCode::WANT_READ || e.code() == ErrorCode::WANT_WRITE => {
@@ -263,7 +200,7 @@ where
263200
}
264201

265202
/// The error type returned after a failed handshake.
266-
pub struct HandshakeError<S>(MidHandshakeSslStream<StreamWrapper<S>>);
203+
pub struct HandshakeError<S>(MidHandshakeSslStream<AsyncStreamBridge<S>>);
267204

268205
impl<S> HandshakeError<S> {
269206
/// Returns a shared reference to the `Ssl` object associated with this error.
@@ -332,33 +269,36 @@ where
332269
/// Future for an ongoing TLS handshake.
333270
///
334271
/// See [`connect`] and [`accept`].
335-
pub struct HandshakeFuture<S>(Option<MidHandshakeSslStream<StreamWrapper<S>>>);
272+
pub struct HandshakeFuture<S>(Option<MidHandshakeSslStream<AsyncStreamBridge<S>>>);
336273

337274
impl<S> Future for HandshakeFuture<S>
338275
where
339276
S: AsyncRead + AsyncWrite + Unpin,
340277
{
341278
type Output = Result<SslStream<S>, HandshakeError<S>>;
342279

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

346-
s.get_mut().context = cx as *mut _ as usize;
283+
mid_handshake.get_mut().set_waker(Some(ctx));
347284

348-
match s.handshake() {
349-
Ok(mut s) => {
350-
s.get_mut().context = 0;
285+
match mid_handshake.handshake() {
286+
Ok(mut stream) => {
287+
stream.get_mut().set_waker(None);
351288

352-
Poll::Ready(Ok(SslStream(s)))
289+
Poll::Ready(Ok(SslStream(stream)))
353290
}
354-
Err(mut handshake) if handshake.error().would_block() => {
355-
handshake.get_mut().context = 0;
291+
Err(mut mid_handshake) => {
292+
mid_handshake.get_mut().set_waker(None);
356293

357-
self.0 = Some(handshake);
294+
if mid_handshake.error().would_block() {
295+
self.0 = Some(mid_handshake);
358296

359-
Poll::Pending
297+
Poll::Pending
298+
} else {
299+
Poll::Ready(Err(HandshakeError(mid_handshake)))
300+
}
360301
}
361-
Err(e) => Poll::Ready(Err(HandshakeError(e))),
362302
}
363303
}
364304
}

0 commit comments

Comments
 (0)