mirror of https://github.com/smol-rs/nb-connect
151 lines
4.6 KiB
Rust
151 lines
4.6 KiB
Rust
//! Non-blocking TCP or Unix connect.
|
|
//!
|
|
//! This crate allows you to create a [`TcpStream`] or a [`UnixStream`] in a non-blocking way,
|
|
//! without waiting for the connection to become fully established.
|
|
//!
|
|
//! [`TcpStream`]: https://doc.rust-lang.org/stable/std/net/struct.TcpStream.html
|
|
//! [`UnixStream`]: https://doc.rust-lang.org/stable/std/os/unix/net/struct.UnixStream.html
|
|
//!
|
|
//! # Examples
|
|
//!
|
|
//! ```
|
|
//! use polling::{Event, Poller};
|
|
//! use std::time::Duration;
|
|
//!
|
|
//! // Create a pending TCP connection.
|
|
//! let stream = nb_connect::tcp(([127, 0, 0, 1], 80))?;
|
|
//!
|
|
//! // Create a poller that waits for the stream to become writable.
|
|
//! let poller = Poller::new()?;
|
|
//! poller.add(&stream, Event::writable(0))?;
|
|
//!
|
|
//! // Wait for at most 1 second.
|
|
//! if poller.wait(&mut Vec::new(), Some(Duration::from_secs(1)))? == 0 {
|
|
//! println!("timeout");
|
|
//! } else if let Some(err) = stream.take_error()? {
|
|
//! println!("error: {}", err);
|
|
//! } else {
|
|
//! println!("connected");
|
|
//! }
|
|
//! # std::io::Result::Ok(())
|
|
//! ```
|
|
|
|
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
|
|
|
|
use std::io;
|
|
use std::net::{SocketAddr, TcpStream};
|
|
|
|
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
|
|
|
|
#[cfg(unix)]
|
|
use std::{os::unix::net::UnixStream, path::Path};
|
|
|
|
fn connect(addr: SockAddr, domain: Domain, protocol: Option<Protocol>) -> io::Result<Socket> {
|
|
let sock_type = Type::STREAM;
|
|
#[cfg(any(
|
|
target_os = "android",
|
|
target_os = "dragonfly",
|
|
target_os = "freebsd",
|
|
target_os = "fuchsia",
|
|
target_os = "illumos",
|
|
target_os = "linux",
|
|
target_os = "netbsd",
|
|
target_os = "openbsd"
|
|
))]
|
|
// If we can, set nonblocking at socket creation for unix
|
|
let sock_type = sock_type.nonblocking();
|
|
// This automatically handles cloexec on unix, no_inherit on windows and nosigpipe on macos
|
|
let socket = Socket::new(domain, sock_type, protocol)?;
|
|
#[cfg(not(any(
|
|
target_os = "android",
|
|
target_os = "dragonfly",
|
|
target_os = "freebsd",
|
|
target_os = "fuchsia",
|
|
target_os = "illumos",
|
|
target_os = "linux",
|
|
target_os = "netbsd",
|
|
target_os = "openbsd"
|
|
)))]
|
|
// If the current platform doesn't support nonblocking at creation, enable it after creation
|
|
socket.set_nonblocking(true)?;
|
|
match socket.connect(&addr) {
|
|
Ok(_) => {}
|
|
#[cfg(unix)]
|
|
Err(err) if err.raw_os_error() == Some(libc::EINPROGRESS) => {}
|
|
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
|
|
Err(err) => return Err(err),
|
|
}
|
|
Ok(socket)
|
|
}
|
|
|
|
/// Creates a pending Unix connection to the specified path.
|
|
///
|
|
/// The returned Unix stream will be in non-blocking mode and in the process of connecting to the
|
|
/// specified path.
|
|
///
|
|
/// The stream becomes writable when connected.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```no_run
|
|
/// use polling::{Event, Poller};
|
|
/// use std::time::Duration;
|
|
///
|
|
/// // Create a pending Unix connection.
|
|
/// let stream = nb_connect::unix("/tmp/socket")?;
|
|
///
|
|
/// // Create a poller that waits for the stream to become writable.
|
|
/// let poller = Poller::new()?;
|
|
/// poller.add(&stream, Event::writable(0))?;
|
|
///
|
|
/// // Wait for at most 1 second.
|
|
/// if poller.wait(&mut Vec::new(), Some(Duration::from_secs(1)))? == 0 {
|
|
/// println!("timeout");
|
|
/// } else {
|
|
/// println!("connected");
|
|
/// }
|
|
/// # std::io::Result::Ok(())
|
|
/// ```
|
|
#[cfg(unix)]
|
|
pub fn unix<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> {
|
|
let socket = connect(SockAddr::unix(path)?, Domain::UNIX, None)?;
|
|
Ok(socket.into())
|
|
}
|
|
|
|
/// Creates a pending TCP connection to the specified address.
|
|
///
|
|
/// The returned TCP stream will be in non-blocking mode and in the process of connecting to the
|
|
/// specified address.
|
|
///
|
|
/// The stream becomes writable when connected.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use polling::{Event, Poller};
|
|
/// use std::time::Duration;
|
|
///
|
|
/// // Create a pending TCP connection.
|
|
/// let stream = nb_connect::tcp(([127, 0, 0, 1], 80))?;
|
|
///
|
|
/// // Create a poller that waits for the stream to become writable.
|
|
/// let poller = Poller::new()?;
|
|
/// poller.add(&stream, Event::writable(0))?;
|
|
///
|
|
/// // Wait for at most 1 second.
|
|
/// if poller.wait(&mut Vec::new(), Some(Duration::from_secs(1)))? == 0 {
|
|
/// println!("timeout");
|
|
/// } else if let Some(err) = stream.take_error()? {
|
|
/// println!("error: {}", err);
|
|
/// } else {
|
|
/// println!("connected");
|
|
/// }
|
|
/// # std::io::Result::Ok(())
|
|
/// ```
|
|
pub fn tcp<A: Into<SocketAddr>>(addr: A) -> io::Result<TcpStream> {
|
|
let addr = addr.into();
|
|
let domain = Domain::for_address(addr);
|
|
let socket = connect(addr.into(), domain, Some(Protocol::TCP))?;
|
|
Ok(socket.into())
|
|
}
|