Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+544
View File
@@ -0,0 +1,544 @@
use std::future::Future;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, RawSocket};
use std::pin::Pin;
#[cfg(feature = "early-data")]
use std::task::Waker;
use std::task::{Context, Poll};
use std::{
io::{self, BufRead as _},
sync::Arc,
};
use rustls::{pki_types::ServerName, ClientConfig, ClientConnection};
use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
use crate::common::{IoSession, MidHandshake, Stream, TlsState};
/// A wrapper around a `rustls::ClientConfig`, providing an async `connect` method.
#[derive(Clone)]
pub struct TlsConnector {
inner: Arc<ClientConfig>,
#[cfg(feature = "early-data")]
early_data: bool,
}
impl TlsConnector {
/// Enable 0-RTT.
///
/// If you want to use 0-RTT,
/// You must also set `ClientConfig.enable_early_data` to `true`.
#[cfg(feature = "early-data")]
pub fn early_data(mut self, flag: bool) -> Self {
self.early_data = flag;
self
}
#[inline]
pub fn connect<IO>(&self, domain: ServerName<'static>, stream: IO) -> Connect<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
self.connect_impl(domain, stream, None, |_| ())
}
#[inline]
pub fn connect_with<IO, F>(&self, domain: ServerName<'static>, stream: IO, f: F) -> Connect<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
F: FnOnce(&mut ClientConnection),
{
self.connect_impl(domain, stream, None, f)
}
fn connect_impl<IO, F>(
&self,
domain: ServerName<'static>,
stream: IO,
alpn_protocols: Option<Vec<Vec<u8>>>,
f: F,
) -> Connect<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
F: FnOnce(&mut ClientConnection),
{
let alpn = alpn_protocols.unwrap_or_else(|| self.inner.alpn_protocols.clone());
let mut session = match ClientConnection::new_with_alpn(self.inner.clone(), domain, alpn) {
Ok(session) => session,
Err(error) => {
return Connect(MidHandshake::Error {
io: stream,
// TODO(eliza): should this really return an `io::Error`?
// Probably not...
error: io::Error::new(io::ErrorKind::Other, error),
});
}
};
f(&mut session);
Connect(MidHandshake::Handshaking(TlsStream {
io: stream,
#[cfg(not(feature = "early-data"))]
state: TlsState::Stream,
#[cfg(feature = "early-data")]
state: if self.early_data && session.early_data().is_some() {
TlsState::EarlyData(0, Vec::new())
} else {
TlsState::Stream
},
need_flush: false,
#[cfg(feature = "early-data")]
early_waker: None,
session,
}))
}
pub fn with_alpn(&self, alpn_protocols: Vec<Vec<u8>>) -> TlsConnectorWithAlpn<'_> {
TlsConnectorWithAlpn {
inner: self,
alpn_protocols,
}
}
/// Get a read-only reference to underlying config
pub fn config(&self) -> &Arc<ClientConfig> {
&self.inner
}
}
impl From<Arc<ClientConfig>> for TlsConnector {
fn from(inner: Arc<ClientConfig>) -> Self {
Self {
inner,
#[cfg(feature = "early-data")]
early_data: false,
}
}
}
pub struct TlsConnectorWithAlpn<'c> {
inner: &'c TlsConnector,
alpn_protocols: Vec<Vec<u8>>,
}
impl TlsConnectorWithAlpn<'_> {
#[inline]
pub fn connect<IO>(self, domain: ServerName<'static>, stream: IO) -> Connect<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
self.inner
.connect_impl(domain, stream, Some(self.alpn_protocols), |_| ())
}
#[inline]
pub fn connect_with<IO, F>(self, domain: ServerName<'static>, stream: IO, f: F) -> Connect<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
F: FnOnce(&mut ClientConnection),
{
self.inner
.connect_impl(domain, stream, Some(self.alpn_protocols), f)
}
}
/// Future returned from `TlsConnector::connect` which will resolve
/// once the connection handshake has finished.
pub struct Connect<IO>(MidHandshake<TlsStream<IO>>);
impl<IO> Connect<IO> {
#[inline]
pub fn into_fallible(self) -> FallibleConnect<IO> {
FallibleConnect(self.0)
}
pub fn get_ref(&self) -> Option<&IO> {
match &self.0 {
MidHandshake::Handshaking(sess) => Some(sess.get_ref().0),
MidHandshake::SendAlert { io, .. } => Some(io),
MidHandshake::Error { io, .. } => Some(io),
MidHandshake::End => None,
}
}
pub fn get_mut(&mut self) -> Option<&mut IO> {
match &mut self.0 {
MidHandshake::Handshaking(sess) => Some(sess.get_mut().0),
MidHandshake::SendAlert { io, .. } => Some(io),
MidHandshake::Error { io, .. } => Some(io),
MidHandshake::End => None,
}
}
}
impl<IO: AsyncRead + AsyncWrite + Unpin> Future for Connect<IO> {
type Output = io::Result<TlsStream<IO>>;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0).poll(cx).map_err(|(err, _)| err)
}
}
impl<IO: AsyncRead + AsyncWrite + Unpin> Future for FallibleConnect<IO> {
type Output = Result<TlsStream<IO>, (io::Error, IO)>;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0).poll(cx)
}
}
/// Like [Connect], but returns `IO` on failure.
pub struct FallibleConnect<IO>(MidHandshake<TlsStream<IO>>);
/// A wrapper around an underlying raw stream which implements the TLS or SSL
/// protocol.
#[derive(Debug)]
pub struct TlsStream<IO> {
pub(crate) io: IO,
pub(crate) session: ClientConnection,
pub(crate) state: TlsState,
pub(crate) need_flush: bool,
#[cfg(feature = "early-data")]
pub(crate) early_waker: Option<Waker>,
}
impl<IO> TlsStream<IO> {
#[inline]
pub fn get_ref(&self) -> (&IO, &ClientConnection) {
(&self.io, &self.session)
}
#[inline]
pub fn get_mut(&mut self) -> (&mut IO, &mut ClientConnection) {
(&mut self.io, &mut self.session)
}
#[inline]
pub fn into_inner(self) -> (IO, ClientConnection) {
(self.io, self.session)
}
}
#[cfg(unix)]
impl<S> AsRawFd for TlsStream<S>
where
S: AsRawFd,
{
fn as_raw_fd(&self) -> RawFd {
self.get_ref().0.as_raw_fd()
}
}
#[cfg(windows)]
impl<S> AsRawSocket for TlsStream<S>
where
S: AsRawSocket,
{
fn as_raw_socket(&self) -> RawSocket {
self.get_ref().0.as_raw_socket()
}
}
impl<IO> IoSession for TlsStream<IO> {
type Io = IO;
type Session = ClientConnection;
#[inline]
fn skip_handshake(&self) -> bool {
self.state.is_early_data()
}
#[inline]
fn get_mut(&mut self) -> (&mut TlsState, &mut Self::Io, &mut Self::Session, &mut bool) {
(
&mut self.state,
&mut self.io,
&mut self.session,
&mut self.need_flush,
)
}
#[inline]
fn into_io(self) -> Self::Io {
self.io
}
}
#[cfg(feature = "early-data")]
impl<IO> TlsStream<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
fn poll_early_data(&mut self, cx: &mut Context<'_>) {
// In the EarlyData state, we have not really established a Tls connection.
// Before writing data through `AsyncWrite` and completing the tls handshake,
// we ignore read readiness and return to pending.
//
// In order to avoid event loss,
// we need to register a waker and wake it up after tls is connected.
if self
.early_waker
.as_ref()
.filter(|waker| cx.waker().will_wake(waker))
.is_none()
{
self.early_waker = Some(cx.waker().clone());
}
}
}
impl<IO> AsyncRead for TlsStream<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let data = ready!(self.as_mut().poll_fill_buf(cx))?;
let len = data.len().min(buf.remaining());
buf.put_slice(&data[..len]);
self.consume(len);
Poll::Ready(Ok(()))
}
}
impl<IO> AsyncBufRead for TlsStream<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
match self.state {
#[cfg(feature = "early-data")]
TlsState::EarlyData(..) => {
self.get_mut().poll_early_data(cx);
Poll::Pending
}
TlsState::Stream | TlsState::WriteShutdown => {
let this = self.get_mut();
let stream =
Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable());
match stream.poll_fill_buf(cx) {
Poll::Ready(Ok(buf)) => {
if buf.is_empty() {
this.state.shutdown_read();
}
Poll::Ready(Ok(buf))
}
Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::ConnectionAborted => {
this.state.shutdown_read();
Poll::Ready(Err(err))
}
output => output,
}
}
TlsState::ReadShutdown | TlsState::FullyShutdown => Poll::Ready(Ok(&[])),
}
}
fn consume(mut self: Pin<&mut Self>, amt: usize) {
self.session.reader().consume(amt);
}
}
impl<IO> AsyncWrite for TlsStream<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
/// Note: that it does not guarantee the final data to be sent.
/// To be cautious, you must manually call `flush`.
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let mut stream = Stream::new(&mut this.io, &mut this.session)
.set_eof(!this.state.readable())
.set_need_flush(this.need_flush);
#[cfg(feature = "early-data")]
{
let bufs = [io::IoSlice::new(buf)];
let written = poll_handle_early_data(
&mut this.state,
&mut stream,
&mut this.early_waker,
cx,
&bufs,
)?;
match written {
Poll::Ready(0) => {}
Poll::Ready(written) => return Poll::Ready(Ok(written)),
Poll::Pending => {
this.need_flush = stream.need_flush;
return Poll::Pending;
}
}
}
stream.as_mut_pin().poll_write(cx, buf)
}
/// Note: that it does not guarantee the final data to be sent.
/// To be cautious, you must manually call `flush`.
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let mut stream = Stream::new(&mut this.io, &mut this.session)
.set_eof(!this.state.readable())
.set_need_flush(this.need_flush);
#[cfg(feature = "early-data")]
{
let written = poll_handle_early_data(
&mut this.state,
&mut stream,
&mut this.early_waker,
cx,
bufs,
)?;
match written {
Poll::Ready(0) => {}
Poll::Ready(written) => return Poll::Ready(Ok(written)),
Poll::Pending => {
this.need_flush = stream.need_flush;
return Poll::Pending;
}
}
}
stream.as_mut_pin().poll_write_vectored(cx, bufs)
}
#[inline]
fn is_write_vectored(&self) -> bool {
true
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
let mut stream = Stream::new(&mut this.io, &mut this.session)
.set_eof(!this.state.readable())
.set_need_flush(this.need_flush);
#[cfg(feature = "early-data")]
{
let written = poll_handle_early_data(
&mut this.state,
&mut stream,
&mut this.early_waker,
cx,
&[],
)?;
if written.is_pending() {
this.need_flush = stream.need_flush;
return Poll::Pending;
}
}
stream.as_mut_pin().poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
#[cfg(feature = "early-data")]
{
// complete handshake
if matches!(self.state, TlsState::EarlyData(..)) {
ready!(self.as_mut().poll_flush(cx))?;
}
}
if self.state.writeable() {
self.session.send_close_notify();
self.state.shutdown_write();
}
let this = self.get_mut();
let mut stream =
Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable());
stream.as_mut_pin().poll_shutdown(cx)
}
}
#[cfg(feature = "early-data")]
fn poll_handle_early_data<IO>(
state: &mut TlsState,
stream: &mut Stream<IO, ClientConnection>,
early_waker: &mut Option<Waker>,
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<io::Result<usize>>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
if let TlsState::EarlyData(pos, data) = state {
use std::io::Write;
// write early data
if let Some(mut early_data) = stream.session.early_data() {
let mut written = 0;
for buf in bufs {
if buf.is_empty() {
continue;
}
let len = match early_data.write(buf) {
Ok(0) => break,
Ok(n) => n,
Err(err) => return Poll::Ready(Err(err)),
};
written += len;
data.extend_from_slice(&buf[..len]);
if len < buf.len() {
break;
}
}
if written != 0 {
return Poll::Ready(Ok(written));
}
}
// complete handshake
while stream.session.is_handshaking() {
ready!(stream.handshake(cx))?;
}
// write early data (fallback)
if !stream.session.is_early_data_accepted() {
while *pos < data.len() {
let len = ready!(stream.as_mut_pin().poll_write(cx, &data[*pos..]))?;
*pos += len;
}
}
// end
*state = TlsState::Stream;
if let Some(waker) = early_waker.take() {
waker.wake();
}
}
Poll::Ready(Ok(0))
}
@@ -0,0 +1,98 @@
use std::future::Future;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{io, mem};
use rustls::server::AcceptedAlert;
use rustls::{ConnectionCommon, SideData};
use tokio::io::{AsyncRead, AsyncWrite};
use crate::common::{Stream, SyncWriteAdapter, TlsState};
pub(crate) trait IoSession {
type Io;
type Session;
fn skip_handshake(&self) -> bool;
fn get_mut(&mut self) -> (&mut TlsState, &mut Self::Io, &mut Self::Session, &mut bool);
fn into_io(self) -> Self::Io;
}
pub(crate) enum MidHandshake<IS: IoSession> {
Handshaking(IS),
End,
SendAlert {
io: IS::Io,
alert: AcceptedAlert,
error: io::Error,
},
Error {
io: IS::Io,
error: io::Error,
},
}
impl<IS, SD> Future for MidHandshake<IS>
where
IS: IoSession + Unpin,
IS::Io: AsyncRead + AsyncWrite + Unpin,
IS::Session: DerefMut + Deref<Target = ConnectionCommon<SD>> + Unpin,
SD: SideData,
{
type Output = Result<IS, (io::Error, IS::Io)>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let mut stream = match mem::replace(this, Self::End) {
Self::Handshaking(stream) => stream,
Self::SendAlert {
mut io,
mut alert,
error,
} => loop {
match alert.write(&mut SyncWriteAdapter { io: &mut io, cx }) {
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
*this = Self::SendAlert { io, error, alert };
return Poll::Pending;
}
Err(_) | Ok(0) => return Poll::Ready(Err((error, io))),
Ok(_) => {}
};
},
// Starting the handshake returned an error; fail the future immediately.
Self::Error { io, error } => return Poll::Ready(Err((error, io))),
_ => panic!("unexpected polling after handshake"),
};
if !stream.skip_handshake() {
let (state, io, session, need_flush) = stream.get_mut();
let mut tls_stream = Stream::new(io, session)
.set_eof(!state.readable())
.set_need_flush(*need_flush);
macro_rules! try_poll {
( $e:expr ) => {
match $e {
Poll::Ready(Ok(x)) => x,
Poll::Ready(Err(err)) => return Poll::Ready(Err((err, stream.into_io()))),
Poll::Pending => {
*need_flush = tls_stream.need_flush;
*this = MidHandshake::Handshaking(stream);
return Poll::Pending;
}
}
};
}
while tls_stream.session.is_handshaking() {
try_poll!(tls_stream.handshake(cx));
}
try_poll!(Pin::new(&mut tls_stream).poll_flush(cx));
}
Poll::Ready(Ok(stream))
}
}
+438
View File
@@ -0,0 +1,438 @@
use std::io::{self, BufRead as _, IoSlice, Read, Write};
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::task::{Context, Poll};
use rustls::{ConnectionCommon, SideData};
use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
mod handshake;
pub(crate) use handshake::{IoSession, MidHandshake};
#[derive(Debug)]
pub(crate) enum TlsState {
#[cfg(feature = "early-data")]
EarlyData(usize, Vec<u8>),
Stream,
ReadShutdown,
WriteShutdown,
FullyShutdown,
}
impl TlsState {
#[inline]
pub(crate) fn shutdown_read(&mut self) {
match *self {
Self::WriteShutdown | Self::FullyShutdown => *self = Self::FullyShutdown,
_ => *self = Self::ReadShutdown,
}
}
#[inline]
pub(crate) fn shutdown_write(&mut self) {
match *self {
Self::ReadShutdown | Self::FullyShutdown => *self = Self::FullyShutdown,
_ => *self = Self::WriteShutdown,
}
}
#[inline]
pub(crate) fn writeable(&self) -> bool {
!matches!(*self, Self::WriteShutdown | Self::FullyShutdown)
}
#[inline]
pub(crate) fn readable(&self) -> bool {
!matches!(*self, Self::ReadShutdown | Self::FullyShutdown)
}
#[inline]
#[cfg(feature = "early-data")]
pub(crate) fn is_early_data(&self) -> bool {
matches!(self, Self::EarlyData(..))
}
#[inline]
#[cfg(not(feature = "early-data"))]
pub(crate) const fn is_early_data(&self) -> bool {
false
}
}
pub(crate) struct Stream<'a, IO, C> {
pub(crate) io: &'a mut IO,
pub(crate) session: &'a mut C,
pub(crate) eof: bool,
pub(crate) need_flush: bool,
}
impl<'a, IO: AsyncRead + AsyncWrite + Unpin, C, SD> Stream<'a, IO, C>
where
C: DerefMut + Deref<Target = ConnectionCommon<SD>>,
SD: SideData,
{
pub(crate) fn new(io: &'a mut IO, session: &'a mut C) -> Self {
Stream {
io,
session,
// The state so far is only used to detect EOF, so either Stream
// or EarlyData state should both be all right.
eof: false,
// Whether a previous flush returned pending, or a write occured without a flush.
need_flush: false,
}
}
pub(crate) fn set_eof(mut self, eof: bool) -> Self {
self.eof = eof;
self
}
pub(crate) fn set_need_flush(mut self, need_flush: bool) -> Self {
self.need_flush = need_flush;
self
}
pub(crate) fn as_mut_pin(&mut self) -> Pin<&mut Self> {
Pin::new(self)
}
pub(crate) fn read_io(&mut self, cx: &mut Context) -> Poll<io::Result<usize>> {
let mut reader = SyncReadAdapter { io: self.io, cx };
let n = match self.session.read_tls(&mut reader) {
Ok(n) => n,
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => return Poll::Pending,
Err(err) => return Poll::Ready(Err(err)),
};
self.session.process_new_packets().map_err(|err| {
// In case we have an alert to send describing this error,
// try a last-gasp write -- but don't predate the primary
// error.
let _ = self.write_io(cx);
io::Error::new(io::ErrorKind::InvalidData, err)
})?;
Poll::Ready(Ok(n))
}
pub(crate) fn write_io(&mut self, cx: &mut Context) -> Poll<io::Result<usize>> {
let mut writer = SyncWriteAdapter { io: self.io, cx };
match self.session.write_tls(&mut writer) {
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Poll::Pending,
result => Poll::Ready(result),
}
}
pub(crate) fn handshake(&mut self, cx: &mut Context) -> Poll<io::Result<(usize, usize)>> {
let mut wrlen = 0;
let mut rdlen = 0;
loop {
let mut write_would_block = false;
let mut read_would_block = false;
while self.session.wants_write() {
match self.write_io(cx) {
Poll::Ready(Ok(0)) => return Poll::Ready(Err(io::ErrorKind::WriteZero.into())),
Poll::Ready(Ok(n)) => {
wrlen += n;
self.need_flush = true;
}
Poll::Pending => {
write_would_block = true;
break;
}
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
}
}
if self.need_flush {
match Pin::new(&mut self.io).poll_flush(cx) {
Poll::Ready(Ok(())) => self.need_flush = false,
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
Poll::Pending => write_would_block = true,
}
}
while !self.eof && self.session.wants_read() {
match self.read_io(cx) {
Poll::Ready(Ok(0)) => self.eof = true,
Poll::Ready(Ok(n)) => rdlen += n,
Poll::Pending => {
read_would_block = true;
break;
}
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
}
}
return match (self.eof, self.session.is_handshaking()) {
(true, true) => {
let err = io::Error::new(io::ErrorKind::UnexpectedEof, "tls handshake eof");
Poll::Ready(Err(err))
}
(_, false) => Poll::Ready(Ok((rdlen, wrlen))),
(_, true) if write_would_block || read_would_block => {
if rdlen != 0 || wrlen != 0 {
Poll::Ready(Ok((rdlen, wrlen)))
} else {
Poll::Pending
}
}
(..) => continue,
};
}
}
pub(crate) fn poll_fill_buf(mut self, cx: &mut Context<'_>) -> Poll<io::Result<&'a [u8]>>
where
SD: 'a,
{
let mut io_pending = false;
// read a packet
while !self.eof && self.session.wants_read() {
match self.read_io(cx) {
Poll::Ready(Ok(0)) => {
break;
}
Poll::Ready(Ok(_)) => (),
Poll::Pending => {
io_pending = true;
break;
}
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
}
}
match self.session.reader().into_first_chunk() {
Ok(buf) => {
// Note that this could be empty (i.e. EOF) if a `CloseNotify` has been
// received and there is no more buffered data.
Poll::Ready(Ok(buf))
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
if !io_pending {
// If `wants_read()` is satisfied, rustls will not return `WouldBlock`.
// but if it does, we can try again.
//
// If the rustls state is abnormal, it may cause a cyclic wakeup.
// but tokio's cooperative budget will prevent infinite wakeup.
cx.waker().wake_by_ref();
}
Poll::Pending
}
Err(e) => Poll::Ready(Err(e)),
}
}
}
impl<'a, IO: AsyncRead + AsyncWrite + Unpin, C, SD> AsyncRead for Stream<'a, IO, C>
where
C: DerefMut + Deref<Target = ConnectionCommon<SD>>,
SD: SideData + 'a,
{
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let data = ready!(self.as_mut().poll_fill_buf(cx))?;
let amount = buf.remaining().min(data.len());
buf.put_slice(&data[..amount]);
self.session.reader().consume(amount);
Poll::Ready(Ok(()))
}
}
impl<'a, IO: AsyncRead + AsyncWrite + Unpin, C, SD> AsyncBufRead for Stream<'a, IO, C>
where
C: DerefMut + Deref<Target = ConnectionCommon<SD>>,
SD: SideData + 'a,
{
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
let this = self.get_mut();
Stream {
// reborrow
io: this.io,
session: this.session,
..*this
}
.poll_fill_buf(cx)
}
fn consume(mut self: Pin<&mut Self>, amt: usize) {
self.session.reader().consume(amt);
}
}
impl<IO: AsyncRead + AsyncWrite + Unpin, C, SD> AsyncWrite for Stream<'_, IO, C>
where
C: DerefMut + Deref<Target = ConnectionCommon<SD>>,
SD: SideData,
{
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let mut pos = 0;
while pos != buf.len() {
let mut would_block = false;
match self.session.writer().write(&buf[pos..]) {
Ok(n) => pos += n,
Err(err) => return Poll::Ready(Err(err)),
};
while self.session.wants_write() {
match self.write_io(cx) {
Poll::Ready(Ok(0)) | Poll::Pending => {
would_block = true;
break;
}
Poll::Ready(Ok(_)) => (),
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
}
}
return match (pos, would_block) {
(0, true) => Poll::Pending,
(n, true) => Poll::Ready(Ok(n)),
(_, false) => continue,
};
}
Poll::Ready(Ok(pos))
}
fn poll_write_vectored(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
if bufs.iter().all(|buf| buf.is_empty()) {
return Poll::Ready(Ok(0));
}
loop {
let mut would_block = false;
let written = self.session.writer().write_vectored(bufs)?;
while self.session.wants_write() {
match self.write_io(cx) {
Poll::Ready(Ok(0)) | Poll::Pending => {
would_block = true;
break;
}
Poll::Ready(Ok(_)) => (),
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
}
}
return match (written, would_block) {
(0, true) => Poll::Pending,
(0, false) => continue,
(n, _) => Poll::Ready(Ok(n)),
};
}
}
#[inline]
fn is_write_vectored(&self) -> bool {
true
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
self.session.writer().flush()?;
while self.session.wants_write() {
if ready!(self.write_io(cx))? == 0 {
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
}
}
Pin::new(&mut self.io).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
while self.session.wants_write() {
if ready!(self.write_io(cx))? == 0 {
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
}
}
Poll::Ready(match ready!(Pin::new(&mut self.io).poll_shutdown(cx)) {
Ok(()) => Ok(()),
// When trying to shutdown, not being connected seems fine
Err(err) if err.kind() == io::ErrorKind::NotConnected => Ok(()),
Err(err) => Err(err),
})
}
}
/// An adapter that implements a [`Read`] interface for [`AsyncRead`] types and an
/// associated [`Context`].
///
/// Turns `Poll::Pending` into `WouldBlock`.
pub(crate) struct SyncReadAdapter<'a, 'b, T> {
pub(crate) io: &'a mut T,
pub(crate) cx: &'a mut Context<'b>,
}
impl<T: AsyncRead + Unpin> Read for SyncReadAdapter<'_, '_, T> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut buf = ReadBuf::new(buf);
match Pin::new(&mut self.io).poll_read(self.cx, &mut buf) {
Poll::Ready(Ok(())) => Ok(buf.filled().len()),
Poll::Ready(Err(err)) => Err(err),
Poll::Pending => Err(io::ErrorKind::WouldBlock.into()),
}
}
}
/// An adapter that implements a [`Write`] interface for [`AsyncWrite`] types and an
/// associated [`Context`].
///
/// Turns `Poll::Pending` into `WouldBlock`.
pub(crate) struct SyncWriteAdapter<'a, 'b, T> {
pub(crate) io: &'a mut T,
pub(crate) cx: &'a mut Context<'b>,
}
impl<T: Unpin> SyncWriteAdapter<'_, '_, T> {
#[inline]
fn poll_with<U>(
&mut self,
f: impl FnOnce(Pin<&mut T>, &mut Context<'_>) -> Poll<io::Result<U>>,
) -> io::Result<U> {
match f(Pin::new(self.io), self.cx) {
Poll::Ready(result) => result,
Poll::Pending => Err(io::ErrorKind::WouldBlock.into()),
}
}
}
impl<T: AsyncWrite + Unpin> Write for SyncWriteAdapter<'_, '_, T> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.poll_with(|io, cx| io.poll_write(cx, buf))
}
#[inline]
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.poll_with(|io, cx| io.poll_write_vectored(cx, bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.poll_with(|io, cx| io.poll_flush(cx))
}
}
#[cfg(test)]
mod test_stream;
@@ -0,0 +1,399 @@
use std::io::{self, Cursor, Read, Write};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use futures_util::future::poll_fn;
use futures_util::task::noop_waker_ref;
use rustls::pki_types::ServerName;
use rustls::{ClientConnection, Connection, ServerConnection};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
use super::Stream;
struct Good<'a>(&'a mut Connection);
impl AsyncRead for Good<'_> {
fn poll_read(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let mut buf2 = buf.initialize_unfilled();
Poll::Ready(match self.0.write_tls(buf2.by_ref()) {
Ok(n) => {
buf.advance(n);
Ok(())
}
Err(err) => Err(err),
})
}
}
impl AsyncWrite for Good<'_> {
fn poll_write(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
mut buf: &[u8],
) -> Poll<io::Result<usize>> {
let len = self.0.read_tls(buf.by_ref())?;
self.0
.process_new_packets()
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
Poll::Ready(Ok(len))
}
fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.0
.process_new_packets()
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
Poll::Ready(Ok(()))
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.0.send_close_notify();
dbg!("sent close notify");
self.poll_flush(cx)
}
}
struct Pending;
impl AsyncRead for Pending {
fn poll_read(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
_: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Poll::Pending
}
}
impl AsyncWrite for Pending {
fn poll_write(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
_buf: &[u8],
) -> Poll<io::Result<usize>> {
Poll::Pending
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
struct Expected(Cursor<Vec<u8>>);
impl AsyncRead for Expected {
fn poll_read(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let this = self.get_mut();
let n = std::io::Read::read(&mut this.0, buf.initialize_unfilled())?;
buf.advance(n);
Poll::Ready(Ok(()))
}
}
impl AsyncWrite for Expected {
fn poll_write(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Poll::Ready(Ok(buf.len()))
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
struct Eof;
impl AsyncRead for Eof {
fn poll_read(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
_buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
impl AsyncWrite for Eof {
fn poll_write(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
_buf: &[u8],
) -> Poll<io::Result<usize>> {
Poll::Ready(Ok(0))
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
#[tokio::test]
async fn stream_good() -> io::Result<()> {
stream_good_impl(false, false).await
}
#[tokio::test]
async fn stream_good_vectored() -> io::Result<()> {
stream_good_impl(true, false).await
}
#[tokio::test]
async fn stream_good_bufread() -> io::Result<()> {
stream_good_impl(false, true).await
}
async fn stream_good_impl(vectored: bool, bufread: bool) -> io::Result<()> {
const FILE: &[u8] = include_bytes!("../../README.md");
let (server, mut client) = make_pair();
let mut server = Connection::from(server);
poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?;
io::copy(&mut Cursor::new(FILE), &mut server.writer())?;
server.send_close_notify();
{
let mut good = Good(&mut server);
let mut stream = Stream::new(&mut good, &mut client);
let mut buf = Vec::new();
if bufread {
dbg!(tokio::io::copy_buf(&mut stream, &mut buf).await)?;
} else {
dbg!(stream.read_to_end(&mut buf).await)?;
}
assert_eq!(buf, FILE);
dbg!(utils::write(&mut stream, b"Hello World!", vectored).await)?;
stream.session.send_close_notify();
dbg!(stream.shutdown().await)?;
}
let mut buf = String::new();
dbg!(server.process_new_packets()).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
dbg!(server.reader().read_to_string(&mut buf))?;
assert_eq!(buf, "Hello World!");
Ok(()) as io::Result<()>
}
#[tokio::test]
async fn stream_bad() -> io::Result<()> {
let (server, mut client) = make_pair();
let mut server = Connection::from(server);
poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?;
client.set_buffer_limit(Some(1024));
let mut bad = Pending;
let mut stream = Stream::new(&mut bad, &mut client);
assert_eq!(
poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x42; 8])).await?,
8
);
assert_eq!(
poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x42; 8])).await?,
8
);
let r = poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x00; 1024])).await?; // fill buffer
assert!(r < 1024);
let mut cx = Context::from_waker(noop_waker_ref());
let ret = stream.as_mut_pin().poll_write(&mut cx, &[0x01]);
assert!(ret.is_pending());
Ok(()) as io::Result<()>
}
#[tokio::test]
async fn stream_handshake() -> io::Result<()> {
let (server, mut client) = make_pair();
let mut server = Connection::from(server);
{
let mut good = Good(&mut server);
let mut stream = Stream::new(&mut good, &mut client);
let (r, w) = poll_fn(|cx| stream.handshake(cx)).await?;
assert!(r > 0);
assert!(w > 0);
poll_fn(|cx| stream.handshake(cx)).await?; // finish server handshake
}
assert!(!server.is_handshaking());
assert!(!client.is_handshaking());
Ok(()) as io::Result<()>
}
#[tokio::test]
async fn stream_buffered_handshake() -> io::Result<()> {
use tokio::io::BufWriter;
let (server, mut client) = make_pair();
let mut server = Connection::from(server);
{
let mut good = BufWriter::new(Good(&mut server));
let mut stream = Stream::new(&mut good, &mut client);
let (r, w) = poll_fn(|cx| stream.handshake(cx)).await?;
assert!(r > 0);
assert!(w > 0);
poll_fn(|cx| stream.handshake(cx)).await?; // finish server handshake
}
assert!(!server.is_handshaking());
assert!(!client.is_handshaking());
Ok(()) as io::Result<()>
}
#[tokio::test]
async fn stream_handshake_eof() -> io::Result<()> {
let (_, mut client) = make_pair();
let mut bad = Expected(Cursor::new(Vec::new()));
let mut stream = Stream::new(&mut bad, &mut client);
let mut cx = Context::from_waker(noop_waker_ref());
let r = stream.handshake(&mut cx);
assert_eq!(
r.map_err(|err| err.kind()),
Poll::Ready(Err(io::ErrorKind::UnexpectedEof))
);
Ok(()) as io::Result<()>
}
#[tokio::test]
async fn stream_handshake_write_eof() -> io::Result<()> {
let (_, mut client) = make_pair();
let mut io = Eof;
let mut stream = Stream::new(&mut io, &mut client);
let mut cx = Context::from_waker(noop_waker_ref());
let r = stream.handshake(&mut cx);
assert_eq!(
r.map_err(|err| err.kind()),
Poll::Ready(Err(io::ErrorKind::WriteZero))
);
Ok(()) as io::Result<()>
}
// see https://github.com/tokio-rs/tls/issues/77
#[tokio::test]
async fn stream_handshake_regression_issues_77() -> io::Result<()> {
let (_, mut client) = make_pair();
let mut bad = Expected(Cursor::new(b"\x15\x03\x01\x00\x02\x02\x00".to_vec()));
let mut stream = Stream::new(&mut bad, &mut client);
let mut cx = Context::from_waker(noop_waker_ref());
let r = stream.handshake(&mut cx);
assert_eq!(
r.map_err(|err| err.kind()),
Poll::Ready(Err(io::ErrorKind::InvalidData))
);
Ok(()) as io::Result<()>
}
#[tokio::test]
async fn stream_eof() -> io::Result<()> {
let (server, mut client) = make_pair();
let mut server = Connection::from(server);
poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?;
let mut bad = Expected(Cursor::new(Vec::new()));
let mut stream = Stream::new(&mut bad, &mut client);
let mut buf = Vec::new();
let result = stream.read_to_end(&mut buf).await;
assert_eq!(
result.err().map(|e| e.kind()),
Some(io::ErrorKind::UnexpectedEof)
);
Ok(()) as io::Result<()>
}
#[tokio::test]
async fn stream_write_zero() -> io::Result<()> {
let (server, mut client) = make_pair();
let mut server = Connection::from(server);
poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?;
let mut io = Eof;
let mut stream = Stream::new(&mut io, &mut client);
stream.write_all(b"1").await.unwrap();
let result = stream.flush().await;
assert_eq!(
result.err().map(|e| e.kind()),
Some(io::ErrorKind::WriteZero)
);
Ok(()) as io::Result<()>
}
fn make_pair() -> (ServerConnection, ClientConnection) {
let (sconfig, cconfig) = utils::make_configs();
let server = ServerConnection::new(Arc::new(sconfig)).unwrap();
let domain = ServerName::try_from("foobar.com").unwrap();
let client = ClientConnection::new(Arc::new(cconfig), domain).unwrap();
(server, client)
}
fn do_handshake(
client: &mut ClientConnection,
server: &mut Connection,
cx: &mut Context<'_>,
) -> Poll<io::Result<()>> {
let mut good = Good(server);
let mut stream = Stream::new(&mut good, client);
while stream.session.is_handshaking() {
ready!(stream.handshake(cx))?;
}
while stream.session.wants_write() {
ready!(stream.write_io(cx))?;
}
Poll::Ready(Ok(()))
}
// Share `utils` module with integration tests
include!("../../tests/utils.rs");
+231
View File
@@ -0,0 +1,231 @@
//! Asynchronous TLS/SSL streams for Tokio using [Rustls](https://github.com/rustls/rustls).
//!
//! # Why do I need to call `poll_flush`?
//!
//! Most TLS implementations will have an internal buffer to improve throughput,
//! and rustls is no exception.
//!
//! When we write data to `TlsStream`, we always write rustls buffer first,
//! then take out rustls encrypted data packet, and write it to data channel (like TcpStream).
//! When data channel is pending, some data may remain in rustls buffer.
//!
//! `tokio-rustls` To keep it simple and correct, [TlsStream] will behave like `BufWriter`.
//! For `TlsStream<TcpStream>`, this means that data written by `poll_write` is not guaranteed to be written to `TcpStream`.
//! You must call `poll_flush` to ensure that it is written to `TcpStream`.
//!
//! You should call `poll_flush` at the appropriate time,
//! such as when a period of `poll_write` write is complete and there is no more data to write.
//!
//! ## Why don't we write during `poll_read`?
//!
//! We did this in the early days of `tokio-rustls`, but it caused some bugs.
//! We can solve these bugs through some solutions, but this will cause performance degradation (reverse false wakeup).
//!
//! And reverse write will also prevent us implement full duplex in the future.
//!
//! see <https://github.com/tokio-rs/tls/issues/40>
//!
//! ## Why can't we handle it like `native-tls`?
//!
//! When data channel returns to pending, `native-tls` will falsely report the number of bytes it consumes.
//! This means that if data written by `poll_write` is not actually written to data channel, it will not return `Ready`.
//! Thus avoiding the call of `poll_flush`.
//!
//! but which does not conform to convention of `AsyncWrite` trait.
//! This means that if you give inconsistent data in two `poll_write`, it may cause unexpected behavior.
//!
//! see <https://github.com/tokio-rs/tls/issues/41>
#![warn(unreachable_pub, clippy::use_self)]
use std::io;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, RawSocket};
use std::pin::Pin;
use std::task::{Context, Poll};
pub use rustls;
use rustls::CommonState;
use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
macro_rules! ready {
( $e:expr ) => {
match $e {
std::task::Poll::Ready(t) => t,
std::task::Poll::Pending => return std::task::Poll::Pending,
}
};
}
pub mod client;
pub use client::{Connect, FallibleConnect, TlsConnector, TlsConnectorWithAlpn};
mod common;
pub mod server;
pub use server::{Accept, FallibleAccept, LazyConfigAcceptor, StartHandshake, TlsAcceptor};
/// Unified TLS stream type
///
/// This abstracts over the inner `client::TlsStream` and `server::TlsStream`, so you can use
/// a single type to keep both client- and server-initiated TLS-encrypted connections.
#[allow(clippy::large_enum_variant)] // https://github.com/rust-lang/rust-clippy/issues/9798
#[derive(Debug)]
pub enum TlsStream<T> {
Client(client::TlsStream<T>),
Server(server::TlsStream<T>),
}
impl<T> TlsStream<T> {
pub fn get_ref(&self) -> (&T, &CommonState) {
use TlsStream::*;
match self {
Client(io) => {
let (io, session) = io.get_ref();
(io, session)
}
Server(io) => {
let (io, session) = io.get_ref();
(io, session)
}
}
}
pub fn get_mut(&mut self) -> (&mut T, &mut CommonState) {
use TlsStream::*;
match self {
Client(io) => {
let (io, session) = io.get_mut();
(io, &mut *session)
}
Server(io) => {
let (io, session) = io.get_mut();
(io, &mut *session)
}
}
}
}
impl<T> From<client::TlsStream<T>> for TlsStream<T> {
fn from(s: client::TlsStream<T>) -> Self {
Self::Client(s)
}
}
impl<T> From<server::TlsStream<T>> for TlsStream<T> {
fn from(s: server::TlsStream<T>) -> Self {
Self::Server(s)
}
}
#[cfg(unix)]
impl<S> AsRawFd for TlsStream<S>
where
S: AsRawFd,
{
fn as_raw_fd(&self) -> RawFd {
self.get_ref().0.as_raw_fd()
}
}
#[cfg(windows)]
impl<S> AsRawSocket for TlsStream<S>
where
S: AsRawSocket,
{
fn as_raw_socket(&self) -> RawSocket {
self.get_ref().0.as_raw_socket()
}
}
impl<T> AsyncRead for TlsStream<T>
where
T: AsyncRead + AsyncWrite + Unpin,
{
#[inline]
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
match self.get_mut() {
Self::Client(x) => Pin::new(x).poll_read(cx, buf),
Self::Server(x) => Pin::new(x).poll_read(cx, buf),
}
}
}
impl<T> AsyncBufRead for TlsStream<T>
where
T: AsyncRead + AsyncWrite + Unpin,
{
#[inline]
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
match self.get_mut() {
Self::Client(x) => Pin::new(x).poll_fill_buf(cx),
Self::Server(x) => Pin::new(x).poll_fill_buf(cx),
}
}
#[inline]
fn consume(self: Pin<&mut Self>, amt: usize) {
match self.get_mut() {
Self::Client(x) => Pin::new(x).consume(amt),
Self::Server(x) => Pin::new(x).consume(amt),
}
}
}
impl<T> AsyncWrite for TlsStream<T>
where
T: AsyncRead + AsyncWrite + Unpin,
{
#[inline]
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
match self.get_mut() {
Self::Client(x) => Pin::new(x).poll_write(cx, buf),
Self::Server(x) => Pin::new(x).poll_write(cx, buf),
}
}
#[inline]
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<io::Result<usize>> {
match self.get_mut() {
Self::Client(x) => Pin::new(x).poll_write_vectored(cx, bufs),
Self::Server(x) => Pin::new(x).poll_write_vectored(cx, bufs),
}
}
#[inline]
fn is_write_vectored(&self) -> bool {
match self {
Self::Client(x) => x.is_write_vectored(),
Self::Server(x) => x.is_write_vectored(),
}
}
#[inline]
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match self.get_mut() {
Self::Client(x) => Pin::new(x).poll_flush(cx),
Self::Server(x) => Pin::new(x).poll_flush(cx),
}
}
#[inline]
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match self.get_mut() {
Self::Client(x) => Pin::new(x).poll_shutdown(cx),
Self::Server(x) => Pin::new(x).poll_shutdown(cx),
}
}
}
+481
View File
@@ -0,0 +1,481 @@
use std::future::Future;
use std::io::{self, BufRead as _};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, RawSocket};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use rustls::server::AcceptedAlert;
use rustls::{ServerConfig, ServerConnection};
use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
use crate::common::{IoSession, MidHandshake, Stream, SyncReadAdapter, SyncWriteAdapter, TlsState};
/// A wrapper around a `rustls::ServerConfig`, providing an async `accept` method.
#[derive(Clone)]
pub struct TlsAcceptor {
inner: Arc<ServerConfig>,
}
impl From<Arc<ServerConfig>> for TlsAcceptor {
fn from(inner: Arc<ServerConfig>) -> Self {
Self { inner }
}
}
impl TlsAcceptor {
#[inline]
pub fn accept<IO>(&self, stream: IO) -> Accept<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
self.accept_with(stream, |_| ())
}
pub fn accept_with<IO, F>(&self, stream: IO, f: F) -> Accept<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
F: FnOnce(&mut ServerConnection),
{
let mut session = match ServerConnection::new(self.inner.clone()) {
Ok(session) => session,
Err(error) => {
return Accept(MidHandshake::Error {
io: stream,
// TODO(eliza): should this really return an `io::Error`?
// Probably not...
error: io::Error::new(io::ErrorKind::Other, error),
});
}
};
f(&mut session);
Accept(MidHandshake::Handshaking(TlsStream {
session,
io: stream,
state: TlsState::Stream,
need_flush: false,
}))
}
/// Get a read-only reference to underlying config
pub fn config(&self) -> &Arc<ServerConfig> {
&self.inner
}
}
pub struct LazyConfigAcceptor<IO> {
acceptor: rustls::server::Acceptor,
io: Option<IO>,
alert: Option<(rustls::Error, AcceptedAlert)>,
}
impl<IO> LazyConfigAcceptor<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
#[inline]
pub fn new(acceptor: rustls::server::Acceptor, io: IO) -> Self {
Self {
acceptor,
io: Some(io),
alert: None,
}
}
/// Takes back the client connection. Will return `None` if called more than once or if the
/// connection has been accepted.
///
/// # Example
///
/// ```no_run
/// # fn choose_server_config(
/// # _: rustls::server::ClientHello,
/// # ) -> std::sync::Arc<rustls::ServerConfig> {
/// # unimplemented!();
/// # }
/// # #[allow(unused_variables)]
/// # async fn listen() {
/// use tokio::io::AsyncWriteExt;
/// let listener = tokio::net::TcpListener::bind("127.0.0.1:4443").await.unwrap();
/// let (stream, _) = listener.accept().await.unwrap();
///
/// let acceptor = tokio_rustls::LazyConfigAcceptor::new(rustls::server::Acceptor::default(), stream);
/// tokio::pin!(acceptor);
///
/// match acceptor.as_mut().await {
/// Ok(start) => {
/// let clientHello = start.client_hello();
/// let config = choose_server_config(clientHello);
/// let stream = start.into_stream(config).await.unwrap();
/// // Proceed with handling the ServerConnection...
/// }
/// Err(err) => {
/// if let Some(mut stream) = acceptor.take_io() {
/// stream
/// .write_all(
/// format!("HTTP/1.1 400 Invalid Input\r\n\r\n\r\n{:?}\n", err)
/// .as_bytes()
/// )
/// .await
/// .unwrap();
/// }
/// }
/// }
/// # }
/// ```
pub fn take_io(&mut self) -> Option<IO> {
self.io.take()
}
}
impl<IO> Future for LazyConfigAcceptor<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
type Output = Result<StartHandshake<IO>, io::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
loop {
let io = match this.io.as_mut() {
Some(io) => io,
None => {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::Other,
"acceptor cannot be polled after acceptance",
)))
}
};
if let Some((err, mut alert)) = this.alert.take() {
match alert.write(&mut SyncWriteAdapter { io, cx }) {
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
this.alert = Some((err, alert));
return Poll::Pending;
}
Ok(0) | Err(_) => {
return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidData, err)))
}
Ok(_) => {
this.alert = Some((err, alert));
continue;
}
};
}
let mut reader = SyncReadAdapter { io, cx };
match this.acceptor.read_tls(&mut reader) {
Ok(0) => return Err(io::ErrorKind::UnexpectedEof.into()).into(),
Ok(_) => {}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => return Poll::Pending,
Err(e) => return Err(e).into(),
}
match this.acceptor.accept() {
Ok(Some(accepted)) => {
let io = this.io.take().unwrap();
return Poll::Ready(Ok(StartHandshake { accepted, io }));
}
Ok(None) => {}
Err((err, alert)) => {
this.alert = Some((err, alert));
}
}
}
}
}
/// An incoming connection received through [`LazyConfigAcceptor`].
///
/// This contains the generic `IO` asynchronous transport,
/// [`ClientHello`](rustls::server::ClientHello) data,
/// and all the state required to continue the TLS handshake (e.g. via
/// [`StartHandshake::into_stream`]).
#[non_exhaustive]
#[derive(Debug)]
pub struct StartHandshake<IO> {
pub accepted: rustls::server::Accepted,
pub io: IO,
}
impl<IO> StartHandshake<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
/// Create a new object from an `IO` transport and prior TLS metadata.
pub fn from_parts(accepted: rustls::server::Accepted, transport: IO) -> Self {
Self {
accepted,
io: transport,
}
}
pub fn client_hello(&self) -> rustls::server::ClientHello<'_> {
self.accepted.client_hello()
}
pub fn into_stream(self, config: Arc<ServerConfig>) -> Accept<IO> {
self.into_stream_with(config, |_| ())
}
pub fn into_stream_with<F>(self, config: Arc<ServerConfig>, f: F) -> Accept<IO>
where
F: FnOnce(&mut ServerConnection),
{
let mut conn = match self.accepted.into_connection(config) {
Ok(conn) => conn,
Err((error, alert)) => {
return Accept(MidHandshake::SendAlert {
io: self.io,
alert,
// TODO(eliza): should this really return an `io::Error`?
// Probably not...
error: io::Error::new(io::ErrorKind::InvalidData, error),
});
}
};
f(&mut conn);
Accept(MidHandshake::Handshaking(TlsStream {
session: conn,
io: self.io,
state: TlsState::Stream,
need_flush: false,
}))
}
}
/// Future returned from `TlsAcceptor::accept` which will resolve
/// once the accept handshake has finished.
pub struct Accept<IO>(MidHandshake<TlsStream<IO>>);
impl<IO> Accept<IO> {
#[inline]
pub fn into_fallible(self) -> FallibleAccept<IO> {
FallibleAccept(self.0)
}
pub fn get_ref(&self) -> Option<&IO> {
match &self.0 {
MidHandshake::Handshaking(sess) => Some(sess.get_ref().0),
MidHandshake::SendAlert { io, .. } => Some(io),
MidHandshake::Error { io, .. } => Some(io),
MidHandshake::End => None,
}
}
pub fn get_mut(&mut self) -> Option<&mut IO> {
match &mut self.0 {
MidHandshake::Handshaking(sess) => Some(sess.get_mut().0),
MidHandshake::SendAlert { io, .. } => Some(io),
MidHandshake::Error { io, .. } => Some(io),
MidHandshake::End => None,
}
}
}
impl<IO: AsyncRead + AsyncWrite + Unpin> Future for Accept<IO> {
type Output = io::Result<TlsStream<IO>>;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0).poll(cx).map_err(|(err, _)| err)
}
}
/// Like [Accept], but returns `IO` on failure.
pub struct FallibleAccept<IO>(MidHandshake<TlsStream<IO>>);
impl<IO: AsyncRead + AsyncWrite + Unpin> Future for FallibleAccept<IO> {
type Output = Result<TlsStream<IO>, (io::Error, IO)>;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0).poll(cx)
}
}
/// A wrapper around an underlying raw stream which implements the TLS or SSL
/// protocol.
#[derive(Debug)]
pub struct TlsStream<IO> {
pub(crate) io: IO,
pub(crate) session: ServerConnection,
pub(crate) state: TlsState,
pub(crate) need_flush: bool,
}
impl<IO> TlsStream<IO> {
#[inline]
pub fn get_ref(&self) -> (&IO, &ServerConnection) {
(&self.io, &self.session)
}
#[inline]
pub fn get_mut(&mut self) -> (&mut IO, &mut ServerConnection) {
(&mut self.io, &mut self.session)
}
#[inline]
pub fn into_inner(self) -> (IO, ServerConnection) {
(self.io, self.session)
}
}
impl<IO> IoSession for TlsStream<IO> {
type Io = IO;
type Session = ServerConnection;
#[inline]
fn skip_handshake(&self) -> bool {
false
}
#[inline]
fn get_mut(&mut self) -> (&mut TlsState, &mut Self::Io, &mut Self::Session, &mut bool) {
(
&mut self.state,
&mut self.io,
&mut self.session,
&mut self.need_flush,
)
}
#[inline]
fn into_io(self) -> Self::Io {
self.io
}
}
impl<IO> AsyncRead for TlsStream<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let data = ready!(self.as_mut().poll_fill_buf(cx))?;
let len = data.len().min(buf.remaining());
buf.put_slice(&data[..len]);
self.consume(len);
Poll::Ready(Ok(()))
}
}
impl<IO> AsyncBufRead for TlsStream<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
match self.state {
TlsState::Stream | TlsState::WriteShutdown => {
let this = self.get_mut();
let stream =
Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable());
match stream.poll_fill_buf(cx) {
Poll::Ready(Ok(buf)) => {
if buf.is_empty() {
this.state.shutdown_read();
}
Poll::Ready(Ok(buf))
}
Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::ConnectionAborted => {
this.state.shutdown_read();
Poll::Ready(Err(err))
}
output => output,
}
}
TlsState::ReadShutdown | TlsState::FullyShutdown => Poll::Ready(Ok(&[])),
#[cfg(feature = "early-data")]
ref s => unreachable!("server TLS can not hit this state: {:?}", s),
}
}
fn consume(mut self: Pin<&mut Self>, amt: usize) {
self.session.reader().consume(amt);
}
}
impl<IO> AsyncWrite for TlsStream<IO>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
/// Note: that it does not guarantee the final data to be sent.
/// To be cautious, you must manually call `flush`.
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let mut stream =
Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable());
stream.as_mut_pin().poll_write(cx, buf)
}
/// Note: that it does not guarantee the final data to be sent.
/// To be cautious, you must manually call `flush`.
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let mut stream =
Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable());
stream.as_mut_pin().poll_write_vectored(cx, bufs)
}
#[inline]
fn is_write_vectored(&self) -> bool {
true
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
let mut stream =
Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable());
stream.as_mut_pin().poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
if self.state.writeable() {
self.session.send_close_notify();
self.state.shutdown_write();
}
let this = self.get_mut();
let mut stream =
Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable());
stream.as_mut_pin().poll_shutdown(cx)
}
}
#[cfg(unix)]
impl<IO> AsRawFd for TlsStream<IO>
where
IO: AsRawFd,
{
fn as_raw_fd(&self) -> RawFd {
self.get_ref().0.as_raw_fd()
}
}
#[cfg(windows)]
impl<IO> AsRawSocket for TlsStream<IO>
where
IO: AsRawSocket,
{
fn as_raw_socket(&self) -> RawSocket {
self.get_ref().0.as_raw_socket()
}
}