New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add generic trait to combine UnixListener and TcpListener #4385
Changes from 10 commits
4305804
69b0379
cd1ddf9
9bda024
d0dba7a
c2ec467
8afdc3e
75dcc76
4a46da1
fe231ec
812dfa5
a538f0b
e194407
b639d98
27912b7
316f015
61bd0fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,7 @@ cfg_codec! { | |
|
||
cfg_net! { | ||
pub mod udp; | ||
pub mod net; | ||
} | ||
|
||
cfg_compat! { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
//! TCP/UDP/Unix helpers for tokio. | ||
|
||
use crate::either::Either; | ||
use std::future::Future; | ||
use std::io::Result; | ||
use std::pin::Pin; | ||
use std::task::{Context, Poll}; | ||
|
||
#[cfg(unix)] | ||
pub mod unix; | ||
|
||
/// A trait for a listener: `TcpListener` and `UnixListener`. | ||
pub trait Listener { | ||
/// The stream's type of this listener. | ||
type Io: tokio::io::AsyncRead + tokio::io::AsyncWrite; | ||
/// The socket address type of this listener. | ||
type Addr; | ||
|
||
/// Polls to accept a new incoming connection to this listener. | ||
fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll<Result<(Self::Io, Self::Addr)>>; | ||
|
||
/// Accepts a new incoming connection from this listener. | ||
fn accept(&mut self) -> ListenerAcceptFut<'_, Self> | ||
where | ||
Self: Sized + Unpin, | ||
{ | ||
ListenerAcceptFut::new(self) | ||
} | ||
|
||
/// Returns the local address that this listener is bound to. | ||
fn local_addr(&self) -> Result<Self::Addr>; | ||
} | ||
|
||
impl Listener for tokio::net::TcpListener { | ||
type Io = tokio::net::TcpStream; | ||
type Addr = std::net::SocketAddr; | ||
|
||
fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll<Result<(Self::Io, Self::Addr)>> { | ||
Self::poll_accept(self, cx) | ||
} | ||
|
||
fn local_addr(&self) -> Result<Self::Addr> { | ||
self.local_addr().map(Into::into) | ||
} | ||
} | ||
|
||
/// Future for accepting a new connection from a listener. | ||
#[derive(Debug)] | ||
pub struct ListenerAcceptFut<'a, L> { | ||
cecton marked this conversation as resolved.
Show resolved
Hide resolved
|
||
listener: &'a mut L, | ||
} | ||
|
||
impl<'a, L> ListenerAcceptFut<'a, L> | ||
where | ||
L: Listener, | ||
{ | ||
fn new(listener: &'a mut L) -> Self { | ||
Self { listener } | ||
} | ||
} | ||
|
||
impl<'a, L> Future for ListenerAcceptFut<'a, L> | ||
where | ||
L: Listener, | ||
{ | ||
type Output = Result<(L::Io, L::Addr)>; | ||
|
||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
self.listener.poll_accept(cx) | ||
} | ||
} | ||
|
||
impl<L, R> Listener for Either<L, R> | ||
where | ||
L: Listener, | ||
R: Listener, | ||
{ | ||
type Io = Either<<L as Listener>::Io, <R as Listener>::Io>; | ||
type Addr = Either<<L as Listener>::Addr, <R as Listener>::Addr>; | ||
|
||
fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll<Result<(Self::Io, Self::Addr)>> { | ||
match self { | ||
Either::Left(listener) => listener | ||
.poll_accept(cx) | ||
.map(|res| res.map(|(stream, addr)| (Either::Left(stream), Either::Left(addr)))), | ||
Either::Right(listener) => listener | ||
.poll_accept(cx) | ||
.map(|res| res.map(|(stream, addr)| (Either::Right(stream), Either::Right(addr)))), | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. It's unfortunate that the types here allow it return stuff like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow I'm so blind I didn't even notice. Yes you're right in practice this will never happen. If someones use .... 🤔 I will think about it... but I don't see a solution right now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably making yet another I noticed the I assume we could have a " pub enum EitherListener<L: Listener, R: Listener> {
Left(L),
Right(R),
} I don't think the user absolutely "needs" to use tokio-util's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not convinced that adding an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That would work too but not super elegant I guess...
Good point There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I replaced the Listener impl on Either by custom methods here: b55b62c3 Unfortunately the accept Future struct is slightly different so I had to duplicate some code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just make it an async fn and drop the poll function? We don't need these tricks when it's not a trait method. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤯 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 316f015 |
||
|
||
fn local_addr(&self) -> Result<Self::Addr> { | ||
match self { | ||
Either::Left(listener) => { | ||
let addr = listener.local_addr()?; | ||
Ok(Either::Left(addr)) | ||
} | ||
Either::Right(listener) => { | ||
let addr = listener.local_addr()?; | ||
Ok(Either::Right(addr)) | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
//! Unix domain socket helpers. | ||
|
||
use super::Listener; | ||
use std::io::Result; | ||
use std::task::{Context, Poll}; | ||
|
||
impl Listener for tokio::net::UnixListener { | ||
type Io = tokio::net::UnixStream; | ||
type Addr = tokio::net::unix::SocketAddr; | ||
|
||
fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll<Result<(Self::Io, Self::Addr)>> { | ||
Self::poll_accept(self, cx) | ||
} | ||
|
||
fn local_addr(&self) -> Result<Self::Addr> { | ||
self.local_addr().map(Into::into) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either
poll_accept
should takeself: Pin<&mut Self>
, or theUnpin
bound should be removed fromaccept
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in a538f0b