-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from Congyuwang/compat-layer
Make it possible to opt out tokio
- Loading branch information
Showing
7 changed files
with
375 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
//! Compat layer for `futures-io` types. | ||
//! | ||
//! This module provides a compatibility layer for using `futures-io` types with | ||
//! `async-socks5`. AsyncSocket is implemented for Compat<S> where S is an | ||
//! AsyncRead + AsyncWrite + Unpin type from `futures-io`. | ||
use super::AsyncSocket; | ||
use futures_io::{AsyncRead, AsyncWrite}; | ||
use std::{ | ||
io::Result as IoResult, | ||
ops::{Deref, DerefMut}, | ||
pin::Pin, | ||
task::{Context, Poll}, | ||
}; | ||
|
||
/// A compatibility layer for using `futures-io` types with `async-socks5`. | ||
/// | ||
/// See `FuturesIoCompatExt` trait for details. | ||
pub struct Compat<S>(S); | ||
|
||
impl<S> Compat<S> { | ||
pub(crate) fn new(inner: S) -> Self { | ||
Compat(inner) | ||
} | ||
|
||
/// Unwraps this Compat, returning the inner value. | ||
pub fn into_inner(self) -> S { | ||
self.0 | ||
} | ||
} | ||
|
||
impl<S> Deref for Compat<S> { | ||
type Target = S; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl<S> DerefMut for Compat<S> { | ||
fn deref_mut(&mut self) -> &mut Self::Target { | ||
&mut self.0 | ||
} | ||
} | ||
|
||
/// Import this trait to use socks with `futures-io` compatible runtime. | ||
/// | ||
/// Example: | ||
/// ```no_run | ||
/// use async_std::os::unix::net::UnixStream; | ||
/// use tokio_socks::{io::FuturesIoCompatExt as _, tcp::Socks5Stream}; | ||
/// | ||
/// let socket = UnixStream::connect(proxy_addr) | ||
/// .await | ||
/// .map_err(Error::Io)? | ||
/// .compat(); // Compat<UnixStream> | ||
/// let conn = | ||
/// Socks5Stream::connect_with_password_and_socket(socket, target, username, pswd).await?; | ||
/// // Socks5Stream has implemented futures-io AsyncRead + AsyncWrite. | ||
/// ``` | ||
pub trait FuturesIoCompatExt { | ||
fn compat(self) -> Compat<Self> | ||
where | ||
Self: Sized; | ||
} | ||
|
||
impl<S> FuturesIoCompatExt for S | ||
where | ||
S: AsyncRead + AsyncWrite + Unpin, | ||
{ | ||
fn compat(self) -> Compat<Self> { | ||
Compat::new(self) | ||
} | ||
} | ||
|
||
impl<S> AsyncSocket for Compat<S> | ||
where | ||
S: AsyncRead + AsyncWrite + Unpin, | ||
{ | ||
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<IoResult<usize>> { | ||
AsyncRead::poll_read(Pin::new(self.get_mut().deref_mut()), cx, buf) | ||
} | ||
|
||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<IoResult<usize>> { | ||
AsyncWrite::poll_write(Pin::new(self.get_mut().deref_mut()), cx, buf) | ||
} | ||
} | ||
|
||
impl<S> AsyncRead for Compat<S> | ||
where | ||
S: AsyncRead + Unpin, | ||
{ | ||
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<IoResult<usize>> { | ||
AsyncRead::poll_read(Pin::new(self.get_mut().deref_mut()), cx, buf) | ||
} | ||
} | ||
|
||
impl<S> AsyncWrite for Compat<S> | ||
where | ||
S: AsyncWrite + Unpin, | ||
{ | ||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<IoResult<usize>> { | ||
AsyncWrite::poll_write(Pin::new(self.get_mut().deref_mut()), cx, buf) | ||
} | ||
|
||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<IoResult<()>> { | ||
AsyncWrite::poll_flush(Pin::new(self.get_mut().deref_mut()), cx) | ||
} | ||
|
||
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<IoResult<()>> { | ||
AsyncWrite::poll_close(Pin::new(self.get_mut().deref_mut()), cx) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
//! Asynchronous I/O abstractions for sockets. | ||
#[cfg(feature = "futures-io")] | ||
mod futures; | ||
#[cfg(feature = "tokio")] | ||
mod tokio; | ||
|
||
use futures_util::ready; | ||
use std::{ | ||
future::Future, | ||
io::{Error, ErrorKind}, | ||
mem, | ||
pin::Pin, | ||
task::{Context, Poll}, | ||
}; | ||
|
||
#[cfg(feature = "futures-io")] | ||
pub use futures::{Compat, FuturesIoCompatExt}; | ||
|
||
/// A trait for asynchronous socket I/O. | ||
/// | ||
/// Any type that implements tokio's `AsyncRead` and `AsyncWrite` traits | ||
/// has implemented `AsyncSocket` trait. | ||
/// | ||
/// Use `FuturesIoCompatExt` to wrap `futures-io` types as `AsyncSocket` types. | ||
pub trait AsyncSocket { | ||
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize, Error>>; | ||
|
||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize, Error>>; | ||
} | ||
|
||
pub(crate) trait AsyncSocketExt { | ||
fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self> | ||
where | ||
Self: Sized; | ||
|
||
fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> | ||
where | ||
Self: Sized; | ||
} | ||
|
||
impl<S: AsyncSocket> AsyncSocketExt for S { | ||
fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self> | ||
where | ||
Self: Sized, | ||
{ | ||
let capacity = buf.len(); | ||
ReadExact { | ||
reader: self, | ||
buf, | ||
capacity, | ||
} | ||
} | ||
|
||
fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> | ||
where | ||
Self: Sized, | ||
{ | ||
WriteAll { writer: self, buf } | ||
} | ||
} | ||
|
||
pub(crate) struct ReadExact<'a, R> { | ||
reader: &'a mut R, | ||
buf: &'a mut [u8], | ||
capacity: usize, | ||
} | ||
|
||
impl<R: AsyncSocket + Unpin> Future for ReadExact<'_, R> { | ||
type Output = Result<usize, Error>; | ||
|
||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
let this = &mut *self; | ||
while !this.buf.is_empty() { | ||
let n = ready!(Pin::new(&mut *this.reader).poll_read(cx, this.buf))?; | ||
{ | ||
let (_, rest) = mem::take(&mut this.buf).split_at_mut(n); | ||
this.buf = rest; | ||
} | ||
if n == 0 { | ||
return Poll::Ready(Err(ErrorKind::UnexpectedEof.into())); | ||
} | ||
} | ||
Poll::Ready(Ok(this.capacity)) | ||
} | ||
} | ||
|
||
pub(crate) struct WriteAll<'a, W> { | ||
writer: &'a mut W, | ||
buf: &'a [u8], | ||
} | ||
|
||
impl<W: AsyncSocket + Unpin> Future for WriteAll<'_, W> { | ||
type Output = Result<(), Error>; | ||
|
||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
let this = &mut *self; | ||
while !this.buf.is_empty() { | ||
let n = ready!(Pin::new(&mut *this.writer).poll_write(cx, this.buf))?; | ||
{ | ||
let (_, rest) = mem::take(&mut this.buf).split_at(n); | ||
this.buf = rest; | ||
} | ||
if n == 0 { | ||
return Poll::Ready(Err(ErrorKind::WriteZero.into())); | ||
} | ||
} | ||
|
||
Poll::Ready(Ok(())) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
//! AsyncSocket trait implementation for tokio's AsyncRead + AsyncWrite | ||
//! traits. | ||
use super::AsyncSocket; | ||
use futures_util::ready; | ||
use std::{ | ||
io::Result as IoResult, | ||
pin::Pin, | ||
task::{Context, Poll}, | ||
}; | ||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; | ||
|
||
impl<S> AsyncSocket for S | ||
where | ||
S: AsyncRead + AsyncWrite, | ||
{ | ||
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<IoResult<usize>> { | ||
let mut buf = ReadBuf::new(buf); | ||
ready!(AsyncRead::poll_read(self, cx, &mut buf))?; | ||
Poll::Ready(Ok(buf.filled().len())) | ||
} | ||
|
||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<IoResult<usize>> { | ||
AsyncWrite::poll_write(self, cx, buf) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.