Skip to content

Implement an async version of ToSocketAddrs #74

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

Merged
14 commits merged into from
Sep 4, 2019
Merged
25 changes: 10 additions & 15 deletions docs/src/tutorial/accept_loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,20 @@ First of all, let's add required import boilerplate:

```rust,edition2018
# extern crate async_std;
use std::net::ToSocketAddrs; // 1
use async_std::{
prelude::*, // 2
task, // 3
net::TcpListener, // 4
prelude::*, // 1
task, // 2
net::{TcpListener, ToSocketAddrs}, // 3
};

type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; // 5
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; // 4
```

1. `async_std` uses `std` types where appropriate.
We'll need `ToSocketAddrs` to specify address to listen on.
2. `prelude` re-exports some traits required to work with futures and streams.
3. The `task` module roughly corresponds to the `std::thread` module, but tasks are much lighter weight.
1. `prelude` re-exports some traits required to work with futures and streams.
2. The `task` module roughly corresponds to the `std::thread` module, but tasks are much lighter weight.
A single thread can run many tasks.
4. For the socket type, we use `TcpListener` from `async_std`, which is just like `std::net::TcpListener`, but is non-blocking and uses `async` API.
5. We will skip implementing comprehensive error handling in this example.
3. For the socket type, we use `TcpListener` from `async_std`, which is just like `std::net::TcpListener`, but is non-blocking and uses `async` API.
4. We will skip implementing comprehensive error handling in this example.
To propagate the errors, we will use a boxed error trait object.
Do you know that there's `From<&'_ str> for Box<dyn Error>` implementation in stdlib, which allows you to use strings with `?` operator?

Expand All @@ -31,10 +28,9 @@ Now we can write the server's accept loop:
```rust,edition2018
# extern crate async_std;
# use async_std::{
# net::TcpListener,
# net::{TcpListener, ToSocketAddrs},
# prelude::Stream,
# };
# use std::net::ToSocketAddrs;
#
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#
Expand Down Expand Up @@ -69,11 +65,10 @@ Finally, let's add main:
```rust,edition2018
# extern crate async_std;
# use async_std::{
# net::TcpListener,
# net::{TcpListener, ToSocketAddrs},
# prelude::Stream,
# task,
# };
# use std::net::ToSocketAddrs;
#
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#
Expand Down
3 changes: 1 addition & 2 deletions docs/src/tutorial/all_together.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ At this point, we only need to start the broker to get a fully-functioning (in t
# extern crate futures;
use async_std::{
io::{self, BufReader},
net::{TcpListener, TcpStream},
net::{TcpListener, TcpStream, ToSocketAddrs},
prelude::*,
task,
};
Expand All @@ -17,7 +17,6 @@ use futures::{
};
use std::{
collections::hash_map::{HashMap, Entry},
net::ToSocketAddrs,
sync::Arc,
};

Expand Down
6 changes: 2 additions & 4 deletions docs/src/tutorial/clean_shutdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Let's add waiting to the server:
# extern crate futures;
# use async_std::{
# io::{self, BufReader},
# net::{TcpListener, TcpStream},
# net::{TcpListener, TcpStream, ToSocketAddrs},
# prelude::*,
# task,
# };
Expand All @@ -35,7 +35,6 @@ Let's add waiting to the server:
# };
# use std::{
# collections::hash_map::{HashMap, Entry},
# net::ToSocketAddrs,
# sync::Arc,
# };
#
Expand Down Expand Up @@ -160,7 +159,7 @@ And to the broker:
# extern crate futures;
# use async_std::{
# io::{self, BufReader},
# net::{TcpListener, TcpStream},
# net::{TcpListener, TcpStream, ToSocketAddrs},
# prelude::*,
# task,
# };
Expand All @@ -170,7 +169,6 @@ And to the broker:
# };
# use std::{
# collections::hash_map::{HashMap, Entry},
# net::ToSocketAddrs,
# sync::Arc,
# };
#
Expand Down
3 changes: 1 addition & 2 deletions docs/src/tutorial/handling_disconnection.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,12 @@ The final code looks like this:
# extern crate futures;
use async_std::{
io::{BufReader, BufRead, Write},
net::{TcpListener, TcpStream},
net::{TcpListener, TcpStream, ToSocketAddrs},
task,
};
use futures::{channel::mpsc, future::Future, select, FutureExt, SinkExt, StreamExt};
use std::{
collections::hash_map::{Entry, HashMap},
net::ToSocketAddrs,
sync::Arc,
};

Expand Down
3 changes: 1 addition & 2 deletions docs/src/tutorial/implementing_a_client.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ With async, we can just use the `select!` macro.
# extern crate futures;
use async_std::{
io::{stdin, BufRead, BufReader, Write},
net::TcpStream,
net::{TcpStream, ToSocketAddrs},
task,
};
use futures::{select, FutureExt, StreamExt};
use std::net::ToSocketAddrs;

type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

Expand Down
6 changes: 2 additions & 4 deletions docs/src/tutorial/receiving_messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ We need to:
# extern crate async_std;
# use async_std::{
# io::{BufRead, BufReader},
# net::{TcpListener, TcpStream},
# net::{TcpListener, TcpStream, ToSocketAddrs},
# prelude::Stream,
# task,
# };
# use std::net::ToSocketAddrs;
#
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#
Expand Down Expand Up @@ -77,11 +76,10 @@ We can "fix" it by waiting for the task to be joined, like this:
# extern crate async_std;
# use async_std::{
# io::{BufRead, BufReader},
# net::{TcpListener, TcpStream},
# net::{TcpListener, TcpStream, ToSocketAddrs},
# prelude::Stream,
# task,
# };
# use std::net::ToSocketAddrs;
#
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#
Expand Down
162 changes: 162 additions & 0 deletions src/net/addr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
use std::pin::Pin;

use cfg_if::cfg_if;
use futures::future::{ready, Ready};

use crate::future::Future;
use crate::io;
use crate::task::blocking;
use crate::task::{Context, Poll};
use std::marker::PhantomData;

cfg_if! {
if #[cfg(feature = "docs")] {
#[doc(hidden)]
pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>);

macro_rules! ret {
($a:lifetime, $f:tt, $i:ty) => (ImplFuture<$a, io::Result<$i>>);
}
} else {
macro_rules! ret {
($a:lifetime, $f:tt, $i:ty) => ($f<$a, $i>);
}
}
}

/// A trait for objects which can be converted or resolved to one or more [`SocketAddr`] values.
///
/// This trait is an async version of [`std::net::ToSocketAddrs`].
///
/// [`std::net::ToSocketAddrs`]: https://doc.rust-lang.org/std/net/trait.ToSocketAddrs.html
/// [`SocketAddr`]: https://doc.rust-lang.org/std/net/enum.SocketAddr.html
pub trait ToSocketAddrs {
/// Returned iterator over socket addresses which this type may correspond to.
type Iter: Iterator<Item = SocketAddr>;

/// Converts this object to an iterator of resolved `SocketAddr`s.
///
/// The returned iterator may not actually yield any values depending on the outcome of any
/// resolution performed.
///
/// Note that this function may block a backend thread while resolution is performed.
fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter);
}

#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub enum ToSocketAddrsFuture<'a, I: Iterator<Item = SocketAddr>> {
Phantom(PhantomData<&'a ()>),
Join(blocking::JoinHandle<io::Result<I>>),
Ready(Ready<io::Result<I>>),
}

impl<I: Iterator<Item = SocketAddr>> Future for ToSocketAddrsFuture<'_, I> {
type Output = io::Result<I>;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.get_mut() {
ToSocketAddrsFuture::Join(f) => Pin::new(&mut *f).poll(cx),
ToSocketAddrsFuture::Ready(f) => Pin::new(&mut *f).poll(cx),
_ => unreachable!(),
}
}
}

impl ToSocketAddrs for SocketAddr {
type Iter = std::option::IntoIter<SocketAddr>;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self)))
}
}

impl ToSocketAddrs for SocketAddrV4 {
type Iter = std::option::IntoIter<SocketAddr>;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self)))
}
}

impl ToSocketAddrs for SocketAddrV6 {
type Iter = std::option::IntoIter<SocketAddr>;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self)))
}
}

impl ToSocketAddrs for (IpAddr, u16) {
type Iter = std::option::IntoIter<SocketAddr>;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self)))
}
}

impl ToSocketAddrs for (Ipv4Addr, u16) {
type Iter = std::option::IntoIter<SocketAddr>;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self)))
}
}

impl ToSocketAddrs for (Ipv6Addr, u16) {
type Iter = std::option::IntoIter<SocketAddr>;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self)))
}
}

impl ToSocketAddrs for (&str, u16) {
type Iter = std::vec::IntoIter<SocketAddr>;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
let host = self.0.to_string();
let port = self.1;
let join = blocking::spawn(async move {
std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port))
});
ToSocketAddrsFuture::Join(join)
}
}

impl ToSocketAddrs for str {
type Iter = std::vec::IntoIter<SocketAddr>;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
let socket_addrs = self.to_string();
let join =
blocking::spawn(async move { std::net::ToSocketAddrs::to_socket_addrs(&socket_addrs) });
ToSocketAddrsFuture::Join(join)
}
}

impl<'a> ToSocketAddrs for &'a [SocketAddr] {
type Iter = std::iter::Cloned<std::slice::Iter<'a, SocketAddr>>;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self)))
}
}

impl<T: ToSocketAddrs + ?Sized> ToSocketAddrs for &T {
type Iter = T::Iter;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
(**self).to_socket_addrs()
}
}

impl ToSocketAddrs for String {
type Iter = std::vec::IntoIter<SocketAddr>;

fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
ToSocketAddrs::to_socket_addrs(self.as_str())
}
}
2 changes: 2 additions & 0 deletions src/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
//! # }) }
//! ```

pub use addr::ToSocketAddrs;
pub use tcp::{Incoming, TcpListener, TcpStream};
pub use udp::UdpSocket;

mod addr;
pub(crate) mod driver;
mod tcp;
mod udp;
11 changes: 6 additions & 5 deletions src/net/tcp/listener.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::net::{self, SocketAddr, ToSocketAddrs};
use std::net::SocketAddr;
use std::pin::Pin;

use cfg_if::cfg_if;
Expand All @@ -8,6 +8,7 @@ use super::TcpStream;
use crate::future::Future;
use crate::io;
use crate::net::driver::IoHandle;
use crate::net::ToSocketAddrs;
use crate::task::{Context, Poll};

/// A TCP socket server, listening for connections.
Expand Down Expand Up @@ -82,7 +83,7 @@ impl TcpListener {
pub async fn bind<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpListener> {
let mut last_err = None;

for addr in addrs.to_socket_addrs()? {
for addr in addrs.to_socket_addrs().await? {
match mio::net::TcpListener::bind(&addr) {
Ok(mio_listener) => {
#[cfg(unix)]
Expand Down Expand Up @@ -236,9 +237,9 @@ impl<'a> futures::Stream for Incoming<'a> {
}
}

impl From<net::TcpListener> for TcpListener {
impl From<std::net::TcpListener> for TcpListener {
/// Converts a `std::net::TcpListener` into its asynchronous equivalent.
fn from(listener: net::TcpListener) -> TcpListener {
fn from(listener: std::net::TcpListener) -> TcpListener {
let mio_listener = mio::net::TcpListener::from_std(listener).unwrap();

#[cfg(unix)]
Expand Down Expand Up @@ -279,7 +280,7 @@ cfg_if! {

impl FromRawFd for TcpListener {
unsafe fn from_raw_fd(fd: RawFd) -> TcpListener {
net::TcpListener::from_raw_fd(fd).into()
std::net::TcpListener::from_raw_fd(fd).into()
}
}

Expand Down
Loading