From b871bf78acdb643f7b39ac9d1110ab94d886d7d1 Mon Sep 17 00:00:00 2001 From: Haibo Huang Date: Mon, 16 Nov 2020 17:42:39 -0800 Subject: Upgrade rust/crates/mio to 0.7.6 Test: make Change-Id: I26de17b65a22ec1ba472a61c51b55681afdb8ffa --- .cargo_vcs_info.json | 2 +- Android.bp | 4 +- CHANGELOG.md | 58 +++++-- Cargo.lock | 10 +- Cargo.toml | 24 +-- Cargo.toml.orig | 34 ++-- METADATA | 6 +- README.md | 3 +- src/event/events.rs | 12 +- src/event/source.rs | 3 +- src/lib.rs | 106 ++++-------- src/macros.rs | 70 ++++++++ src/macros/mod.rs | 131 --------------- src/net/mod.rs | 21 +-- src/net/tcp/listener.rs | 3 +- src/net/tcp/mod.rs | 2 +- src/net/tcp/socket.rs | 336 +++++++++++++++++++++++++++++++++++-- src/net/tcp/stream.rs | 5 +- src/net/udp.rs | 25 ++- src/poll.rs | 18 +- src/sys/mod.rs | 30 +--- src/sys/shell/mod.rs | 10 +- src/sys/shell/selector.rs | 2 +- src/sys/shell/tcp.rs | 74 ++++++++ src/sys/unix/mod.rs | 18 +- src/sys/unix/net.rs | 140 ++++++++++++---- src/sys/unix/selector/kqueue.rs | 1 + src/sys/unix/sourcefd.rs | 6 +- src/sys/unix/tcp.rs | 304 ++++++++++++++++++++++++++++++++- src/sys/unix/udp.rs | 2 +- src/sys/unix/uds/socketaddr.rs | 2 - src/sys/windows/afd.rs | 9 +- src/sys/windows/event.rs | 2 +- src/sys/windows/io_status_block.rs | 3 +- src/sys/windows/mod.rs | 16 +- src/sys/windows/net.rs | 80 +++++++-- src/sys/windows/overlapped.rs | 6 +- src/sys/windows/selector.rs | 4 +- src/sys/windows/tcp.rs | 205 ++++++++++++++++++++-- src/sys/windows/udp.rs | 2 +- src/token.rs | 3 +- src/waker.rs | 3 +- 42 files changed, 1335 insertions(+), 460 deletions(-) create mode 100644 src/macros.rs delete mode 100644 src/macros/mod.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 3096345..7fefbd3 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "27fbd5f04bb5f52a4d1c358cf0c04c6074a3d46b" + "sha1": "e6e403fe2a4fc14dfbc74dbb3ae3a14e3044eb6f" } } diff --git a/Android.bp b/Android.bp index 1223c02..81d1675 100644 --- a/Android.bp +++ b/Android.bp @@ -7,6 +7,8 @@ rust_library { srcs: ["src/lib.rs"], edition: "2018", features: [ + "net", + "os-ext", "os-poll", "os-util", "tcp", @@ -21,5 +23,5 @@ rust_library { // dependent_library ["feature_list"] // cfg-if-0.1.10 -// libc-0.2.80 "default,std" +// libc-0.2.80 "align,default,std" // log-0.4.11 "std" diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ca5125..f024bfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,43 +1,79 @@ +# 0.7.6 + +## Added + +* `net` feature, replaces `tcp`, `udp` and `uds` features + (https://github.com/tokio-rs/mio/commit/a301ba520a8479b459c4acdcefa4a7c5eea818c7). +* `os-ext` feature, replaces `os-util` and `pipe` features + (https://github.com/tokio-rs/mio/commit/f5017fae8a3d3bb4b4cada25b01a2d76a406badc). +* Added keepalive support to `TcpSocket` + (https://github.com/tokio-rs/mio/commit/290c43a96662d54ab7c4b8814e5a9f9a9e523fda). +* `TcpSocket::set_{send, recv}_buffer_size` + (https://github.com/tokio-rs/mio/commit/40c4af79bf5b32b8fbdbf6f2e5c16290e1d3d406). +* `TcpSocket::get_linger` + (https://github.com/tokio-rs/mio/commit/13e82ced655bbb6e2729226e485a7de9f2c2ccd9). +* Implement `IntoRawFd` for `TcpSocket` + (https://github.com/tokio-rs/mio/commit/50548ed45d0b2c98f1f2e003e210d14195284ef4). + +## Deprecated + +* The `tcp`, `udp` and `uds` features, replaced by a new `net` feature. + (https://github.com/tokio-rs/mio/commit/a301ba520a8479b459c4acdcefa4a7c5eea818c7). +* The `extra-docs` feature, now enabled by default. + (https://github.com/tokio-rs/mio/commit/25731e8688a2d91c5c700674a2c2d3841240ece1). +* The `os-util` and `pipe` features, replaced by a new `os-ext` feature. + (https://github.com/tokio-rs/mio/commit/f5017fae8a3d3bb4b4cada25b01a2d76a406badc). + +## Fixes + +* Incorrect assumption of the layout of `std::net::SocketAddr`. Previously Mio + would assume that `SocketAddrV{4,6}` had the same layout as + `libc::sockaddr_in(6)`, however this is not guaranteed by the standard + library. + (https://github.com/tokio-rs/mio/commit/152e0751f0be1c9b0cbd6778645b76bcb0eba93c). +* Also bumped the miow dependency to version 0.3.6 to solve the same problem as + above. + # 0.7.5 ## Added * `TcpSocket::get_localaddr()` retrieves local address - (https://github.com/tokio-rs/mio/commit/b41a022b2242eef1969c70c8ba93e04c528dba47) -* `TcpSocket::set_reuseport()` & `TcpSocket::get_reuseport()` configures and reads SO_REUSEPORT - (https://github.com/tokio-rs/mio/commit/183bbe409ab69cbf9db41d0263b41ec86202d9a0) + (https://github.com/tokio-rs/mio/commit/b41a022b2242eef1969c70c8ba93e04c528dba47). +* `TcpSocket::set_reuseport()` & `TcpSocket::get_reuseport()` configures and reads `SO_REUSEPORT` + (https://github.com/tokio-rs/mio/commit/183bbe409ab69cbf9db41d0263b41ec86202d9a0). * `unix:pipe()` a wrapper around pipe(2) sys call - (https://github.com/tokio-rs/mio/commit/2b7c0967a7362303946deb3d4ca2ae507af6c72d) + (https://github.com/tokio-rs/mio/commit/2b7c0967a7362303946deb3d4ca2ae507af6c72d). * Add a check that a single Waker is active per Poll instance (only in debug mode) - (https://github.com/tokio-rs/mio/commit/f4874f28b32efcf4841691884c65a89734d96a56) + (https://github.com/tokio-rs/mio/commit/f4874f28b32efcf4841691884c65a89734d96a56). * Added `Interest:remove()` - (https://github.com/tokio-rs/mio/commit/b8639c3d9ac07bb7e2e27685680c8a6510fa1357) + (https://github.com/tokio-rs/mio/commit/b8639c3d9ac07bb7e2e27685680c8a6510fa1357). # 0.7.4 ## Fixes * lost "socket closed" events on windows - (https://github.com/tokio-rs/mio/commit/50c299aca56c4a26e5ed20c283007239fbe6a7a7) + (https://github.com/tokio-rs/mio/commit/50c299aca56c4a26e5ed20c283007239fbe6a7a7). ## Added * `TcpSocket::set_linger()` configures SO_LINGER - (https://github.com/tokio-rs/mio/commit/3b4096565c1a879f651b8f8282ecdcbdbd5c92d3) + (https://github.com/tokio-rs/mio/commit/3b4096565c1a879f651b8f8282ecdcbdbd5c92d3). # 0.7.3 ## Added -* `TcpSocket` for configuring a TCP socket before connecting or listening. - (https://github.com/tokio-rs/mio/commit/5b09e60d0f64419b989bda88c86a3147334a03b3) +* `TcpSocket` for configuring a TCP socket before connecting or listening + (https://github.com/tokio-rs/mio/commit/5b09e60d0f64419b989bda88c86a3147334a03b3). # 0.7.2 ## Added * Windows named pipe support. - (https://github.com/tokio-rs/mio/commit/52e8c2220e87696d20f13561402bcaabba4136ed) + (https://github.com/tokio-rs/mio/commit/52e8c2220e87696d20f13561402bcaabba4136ed). # 0.7.1 diff --git a/Cargo.lock b/Cargo.lock index 2a0e767..25ad6d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.5" +version = "0.7.6" dependencies = [ "env_logger", "libc", @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" dependencies = [ "socket2", "winapi", @@ -113,9 +113,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "socket2" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" +checksum = "7fd8b795c389288baa5f355489c65e71fd48a02104600d15c4cfbc561e9e429d" dependencies = [ "cfg-if", "libc", diff --git a/Cargo.toml b/Cargo.toml index a490943..7f0d023 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,12 +13,12 @@ [package] edition = "2018" name = "mio" -version = "0.7.5" +version = "0.7.6" authors = ["Carl Lerche "] include = ["Cargo.toml", "LICENSE", "README.md", "CHANGELOG.md", "src/**/*.rs", "examples/**/*.rs"] description = "Lightweight non-blocking IO" homepage = "https://github.com/tokio-rs/mio" -documentation = "https://docs.rs/mio/0.7.5" +documentation = "https://docs.rs/mio/0.7.6" readme = "README.md" keywords = ["io", "async", "non-blocking"] categories = ["asynchronous"] @@ -29,15 +29,15 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [package.metadata.playground] -features = ["os-poll", "os-util", "tcp", "udp", "uds"] +features = ["os-poll", "os-ext", "net"] [[example]] name = "tcp_server" -required-features = ["os-poll", "tcp"] +required-features = ["os-poll", "net"] [[example]] name = "udp_server" -required-features = ["os-poll", "udp"] +required-features = ["os-poll", "net"] [dependencies.log] version = "0.4.8" [dev-dependencies.env_logger] @@ -50,16 +50,18 @@ version = "0.4" [features] default = [] extra-docs = [] +net = [] +os-ext = ["os-poll"] os-poll = [] -os-util = [] -pipe = ["os-poll"] -tcp = [] -udp = [] -uds = [] +os-util = ["os-ext"] +pipe = ["os-ext"] +tcp = ["net"] +udp = ["net"] +uds = ["net"] [target."cfg(unix)".dependencies.libc] version = "0.2.69" [target."cfg(windows)".dependencies.miow] -version = "0.3.3" +version = "0.3.6" [target."cfg(windows)".dependencies.ntapi] version = "0.3" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index e01d453..e0937ed 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -6,11 +6,11 @@ name = "mio" # - Update CHANGELOG.md. # - Update doc URL. # - Create git tag -version = "0.7.5" +version = "0.7.6" license = "MIT" authors = ["Carl Lerche "] description = "Lightweight non-blocking IO" -documentation = "https://docs.rs/mio/0.7.5" +documentation = "https://docs.rs/mio/0.7.6" homepage = "https://github.com/tokio-rs/mio" repository = "https://github.com/tokio-rs/mio" readme = "README.md" @@ -25,15 +25,25 @@ include = [ "examples/**/*.rs", ] +# For documentation of features see the `mio::features` module. [features] +# By default Mio only provides a shell implementation. default = [] + +# Enables the `Poll` and `Registry` types. os-poll = [] -os-util = [] -pipe = ["os-poll"] -tcp = [] -udp = [] -uds = [] -extra-docs = [] +# Enables additional OS specific extensions, e.g. Unix `pipe(2)`. +os-ext = ["os-poll"] +# Enables `mio::net` module containing networking primitives. +net = [] + +# Deprecated features, will be removed in a future version. +extra-docs = [] # Docs are now always present. +tcp = ["net"] # Replaced with "net" feature. +udp = ["net"] # Replaced with "net" feature. +uds = ["net"] # Replaced with "net" feature. +pipe = ["os-ext"] # Replaced with "os-ext" feature. +os-util = ["os-ext"]# Replaced with "os-ext" feature. [dependencies] log = "0.4.8" @@ -42,7 +52,7 @@ log = "0.4.8" libc = "0.2.69" [target.'cfg(windows)'.dependencies] -miow = "0.3.3" +miow = "0.3.6" winapi = { version = "0.3", features = ["winsock2", "mswsock"] } ntapi = "0.3" @@ -55,12 +65,12 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [package.metadata.playground] -features = ["os-poll", "os-util", "tcp", "udp", "uds"] +features = ["os-poll", "os-ext", "net"] [[example]] name = "tcp_server" -required-features = ["os-poll", "tcp"] +required-features = ["os-poll", "net"] [[example]] name = "udp_server" -required-features = ["os-poll", "udp"] +required-features = ["os-poll", "net"] diff --git a/METADATA b/METADATA index a63bcc3..54e4612 100644 --- a/METADATA +++ b/METADATA @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/mio/mio-0.7.5.crate" + value: "https://static.crates.io/crates/mio/mio-0.7.6.crate" } - version: "0.7.5" + version: "0.7.6" license_type: NOTICE last_upgrade_date { year: 2020 month: 11 - day: 2 + day: 16 } } diff --git a/README.md b/README.md index 62ebc47..c8653f8 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,8 @@ mio = "0.7" ``` Next we can start using Mio. The following is quick introduction using -`TcpListener` and `TcpStream`. Note that `features = ["os-poll", "tcp"]` must be specified for this example. +`TcpListener` and `TcpStream`. Note that `features = ["os-poll", "net"]` must be +specified for this example. ```rust use std::error::Error; diff --git a/src/event/events.rs b/src/event/events.rs index 8fbdc10..f3c5a2f 100644 --- a/src/event/events.rs +++ b/src/event/events.rs @@ -17,7 +17,8 @@ use std::fmt; /// /// # Examples /// -/// ``` +#[cfg_attr(feature = "os-poll", doc = "```")] +#[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Poll}; @@ -51,7 +52,8 @@ pub struct Events { /// /// # Examples /// -/// ``` +#[cfg_attr(feature = "os-poll", doc = "```")] +#[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Poll}; @@ -123,7 +125,8 @@ impl Events { /// /// # Examples /// - /// ``` + #[cfg_attr(feature = "os-poll", doc = "```")] + #[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Poll}; @@ -158,7 +161,8 @@ impl Events { /// /// # Examples /// - /// ``` + #[cfg_attr(feature = "os-poll", doc = "```")] + #[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Poll}; diff --git a/src/event/source.rs b/src/event/source.rs index 1db6596..f38268a 100644 --- a/src/event/source.rs +++ b/src/event/source.rs @@ -38,7 +38,8 @@ use std::io; /// /// Implementing `Source` on a struct containing a socket: /// -/// ``` +#[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] /// use mio::{Interest, Registry, Token}; /// use mio::event::Source; /// use mio::net::TcpStream; diff --git a/src/lib.rs b/src/lib.rs index 332ee24..1491a62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![doc(html_root_url = "https://docs.rs/mio/0.7.5")] +#![doc(html_root_url = "https://docs.rs/mio/0.7.6")] #![deny( missing_docs, missing_debug_implementations, @@ -35,27 +35,11 @@ //! //! ## Guide //! -//! A getting started guide is available in the -#![cfg_attr( - feature = "extra-docs", - doc = "[`guide`](../mio/guide/index.html) module." -)] -#![cfg_attr( - not(feature = "extra-docs"), - doc = "`guide` (only available when the `extra-docs` feature is enabled)." -)] +//! A getting started guide is available in the [`guide`] module. //! //! ## Available features //! -//! The available features are described in the -#![cfg_attr( - feature = "extra-docs", - doc = "[`features`](../mio/features/index.html) module." -)] -#![cfg_attr( - not(feature = "extra-docs"), - doc = "`features` (only available when the `extra-docs` feature is enabled)." -)] +//! The available features are described in the [`features`] module. // macros used internally #[macro_use] @@ -84,41 +68,30 @@ pub use poll::{Poll, Registry}; pub use token::Token; pub use waker::Waker; -#[cfg(all(unix, any(feature = "os-util", feature = "pipe")))] -#[cfg_attr( - docsrs, - doc(cfg(all(unix, any(feature = "os-util", feature = "pipe")))) -)] +#[cfg(all(unix, feature = "os-ext"))] +#[cfg_attr(docsrs, doc(cfg(all(unix, feature = "os-ext"))))] pub mod unix { //! Unix only extensions. - #[cfg(feature = "os-util")] - #[cfg_attr(docsrs, doc(cfg(all(unix, feature = "os-util"))))] - pub use crate::sys::SourceFd; - - cfg_pipe! { - pub mod pipe { - //! Unix pipe. - //! - //! See the [`new`] function for documentation. + pub mod pipe { + //! Unix pipe. + //! + //! See the [`new`] function for documentation. - pub use crate::sys::pipe::{new, Receiver, Sender}; - } + pub use crate::sys::pipe::{new, Receiver, Sender}; } + + pub use crate::sys::SourceFd; } -#[cfg(all(windows, feature = "os-util"))] -#[cfg_attr(docsrs, doc(cfg(all(windows, feature = "os-util"))))] +#[cfg(all(windows, feature = "os-ext"))] +#[cfg_attr(docsrs, doc(cfg(all(windows, feature = "os-ext"))))] pub mod windows { //! Windows only extensions. - cfg_os_poll! { - pub use crate::sys::named_pipe::NamedPipe; - } + pub use crate::sys::named_pipe::NamedPipe; } -// Enable with `cargo doc --features extra-docs`. -#[cfg(feature = "extra-docs")] pub mod features { //! # Mio's optional features. //! @@ -133,44 +106,18 @@ pub mod features { //! //! This makes `Poll`, `Registry` and `Waker` functional. //! - #![cfg_attr(feature = "os-util", doc = "## `os-util` (enabled)")] - #![cfg_attr(not(feature = "os-util"), doc = "## `os-util` (disabled)")] + #![cfg_attr(feature = "os-ext", doc = "## `os-ext` (enabled)")] + #![cfg_attr(not(feature = "os-ext"), doc = "## `os-ext` (disabled)")] //! - //! `os-util` enables additional OS specific facilities. Currently this - //! means the `unix` module (with `SourceFd`) becomes available. + //! `os-ext` enables additional OS specific facilities. These facilities can + //! be found in the `unix` and `windows` module. //! - #![cfg_attr(feature = "pipe", doc = "## `pipe` (enabled)")] - #![cfg_attr(not(feature = "pipe"), doc = "## `pipe` (disabled)")] + #![cfg_attr(feature = "net", doc = "## Network types (enabled)")] + #![cfg_attr(not(feature = "net"), doc = "## Network types (disabled)")] //! - //! The `pipe` feature adds `unix::pipe`, and related types, a non-blocking - //! wrapper around the `pipe(2)` system call. - //! - //! ## Network types - //! - //! Mio provide three features to enable network types: - //! - #![cfg_attr(feature = "tcp", doc = "* `tcp` (enabled)")] - #![cfg_attr(not(feature = "tcp"), doc = "* `tcp` (disabled)")] - //! : includes `TcpStream` and `TcpListener`, - #![cfg_attr(feature = "udp", doc = "* `udp` (enabled)")] - #![cfg_attr(not(feature = "udp"), doc = "* `udp` (disabled)")] - //! : includes `UdpSocket`, and - #![cfg_attr(feature = "uds", doc = "* `uds` (enabled)")] - #![cfg_attr(not(feature = "uds"), doc = "* `uds` (disabled)")] - //! : includes `UnixDatagram`, `UnixListener`, `UnixStream` and `SocketAddr`. - //! - //! All types can be found in the `net` module. - //! - #![cfg_attr(feature = "extra-docs", doc = "## `extra-docs` (enabled)")] - #![cfg_attr(not(feature = "extra-docs"), doc = "## `extra-docs` (disabled)")] - //! - //! This feature includes additional documentation such as this document and - //! the getting started guide. It adds nothing in terms of types (only - //! documentation). + //! The `net` feature enables networking primitives in the `net` module. } -// Enable with `cargo doc --features extra-docs`. -#[cfg(feature = "extra-docs")] pub mod guide { //! # Getting started guide. //! @@ -192,7 +139,8 @@ pub mod guide { //! [`Poll`]: ../struct.Poll.html //! [`Events`]: ../event/struct.Events.html //! - //! ``` + #![cfg_attr(feature = "os-poll", doc = "```")] + #![cfg_attr(not(feature = "os-poll"), doc = "```ignore")] //! # use mio::{Poll, Events}; //! # fn main() -> std::io::Result<()> { //! // `Poll` allows for polling of readiness events. @@ -226,7 +174,8 @@ pub mod guide { //! //! [event source]: ../event/trait.Source.html //! - //! ``` + #![cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] + #![cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] //! # use mio::net::TcpListener; //! # use mio::{Poll, Token, Interest}; //! # fn main() -> std::io::Result<()> { @@ -264,7 +213,8 @@ pub mod guide { //! [poll]: ../struct.Poll.html#method.poll //! [event sources]: ../event/trait.Source.html //! - //! ``` + #![cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] + #![cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] //! # use std::io; //! # use std::time::Duration; //! # use mio::net::TcpListener; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..db93dfd --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,70 @@ +//! Macros to ease conditional code based on enabled features. + +// Depending on the features not all macros are used. +#![allow(unused_macros)] + +/// The `os-poll` feature is enabled. +macro_rules! cfg_os_poll { + ($($item:item)*) => { + $( + #[cfg(feature = "os-poll")] + #[cfg_attr(docsrs, doc(cfg(feature = "os-poll")))] + $item + )* + } +} + +/// The `os-poll` feature is disabled. +macro_rules! cfg_not_os_poll { + ($($item:item)*) => { + $( + #[cfg(not(feature = "os-poll"))] + $item + )* + } +} + +/// The `os-ext` feature is enabled. +macro_rules! cfg_os_ext { + ($($item:item)*) => { + $( + #[cfg(feature = "os-ext")] + #[cfg_attr(docsrs, doc(cfg(feature = "os-ext")))] + $item + )* + } +} + +/// The `net` feature is enabled. +macro_rules! cfg_net { + ($($item:item)*) => { + $( + #[cfg(feature = "net")] + #[cfg_attr(docsrs, doc(cfg(feature = "net")))] + $item + )* + } +} + +/// One of the features enabled that needs `IoSource`. That is `net` or `os-ext` +/// on Unix (for `pipe`). +macro_rules! cfg_io_source { + ($($item:item)*) => { + $( + #[cfg(any(feature = "net", all(unix, feature = "os-ext")))] + #[cfg_attr(docsrs, doc(any(feature = "net", all(unix, feature = "os-ext"))))] + $item + )* + } +} + +/// The `os-ext` feature is enabled, or one of the features that need `os-ext`. +macro_rules! cfg_any_os_ext { + ($($item:item)*) => { + $( + #[cfg(any(feature = "os-ext", feature = "net"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "os-ext", feature = "net"))))] + $item + )* + } +} diff --git a/src/macros/mod.rs b/src/macros/mod.rs deleted file mode 100644 index 2275ed9..0000000 --- a/src/macros/mod.rs +++ /dev/null @@ -1,131 +0,0 @@ -//! Macros to ease conditional code based on enabled features. - -// Depending on the features not all macros are used. -#![allow(unused_macros)] - -/// Feature `os-poll` enabled. -macro_rules! cfg_os_poll { - ($($item:item)*) => { - $( - #[cfg(feature = "os-poll")] - #[cfg_attr(docsrs, doc(cfg(feature = "os-poll")))] - $item - )* - } -} - -/// Feature `os-poll` disabled. -macro_rules! cfg_not_os_poll { - ($($item:item)*) => { - $( - #[cfg(not(feature = "os-poll"))] - $item - )* - } -} - -/// One of the `tcp`, `udp`, `uds` features enabled. -#[cfg(unix)] -macro_rules! cfg_net { - ($($item:item)*) => { - $( - #[cfg(any(feature = "tcp", feature = "udp", feature = "uds"))] - #[cfg_attr(docsrs, doc(cfg(any(feature = "tcp", feature = "udp", feature = "uds"))))] - $item - )* - } -} - -/// One of the features enabled that needs `IoSource`. That is `tcp`, or `udp`, -/// or on Unix `uds` or `pipe`. -macro_rules! cfg_io_source { - ($($item:item)*) => { - $( - #[cfg(any(feature = "tcp", feature = "udp", all(unix, any(feature = "uds", feature = "pipe"))))] - #[cfg_attr(docsrs, doc(any(feature = "tcp", feature = "udp", all(unix, any(feature = "uds", feature = "pipe")))))] - $item - )* - } -} - -/// One of the `tcp`, `udp` features enabled. -#[cfg(windows)] -macro_rules! cfg_net { - ($($item:item)*) => { - $( - #[cfg(any(feature = "tcp", feature = "udp"))] - #[cfg_attr(docsrs, doc(cfg(any(feature = "tcp", feature = "udp"))))] - $item - )* - } -} - -/// Feature `tcp` enabled. -macro_rules! cfg_tcp { - ($($item:item)*) => { - $( - #[cfg(feature = "tcp")] - #[cfg_attr(docsrs, doc(cfg(feature = "tcp")))] - $item - )* - } -} - -/// Feature `udp` enabled. -macro_rules! cfg_udp { - ($($item:item)*) => { - $( - #[cfg(feature = "udp")] - #[cfg_attr(docsrs, doc(cfg(feature = "udp")))] - $item - )* - } -} - -/// Feature `uds` enabled. -#[cfg(unix)] -macro_rules! cfg_uds { - ($($item:item)*) => { - $( - #[cfg(feature = "uds")] - #[cfg_attr(docsrs, doc(cfg(feature = "uds")))] - $item - )* - } -} - -/// Feature `pipe` enabled. -#[cfg(unix)] -macro_rules! cfg_pipe { - ($($item:item)*) => { - $( - #[cfg(feature = "pipe")] - #[cfg_attr(docsrs, doc(cfg(feature = "pipe")))] - $item - )* - } -} - -/// Feature `os-util` enabled, or one of the features that need `os-util`. -#[cfg(unix)] -macro_rules! cfg_any_os_util { - ($($item:item)*) => { - $( - #[cfg(any(feature = "os-util", feature = "tcp", feature = "udp", feature = "uds", feature = "pipe"))] - #[cfg_attr(docsrs, doc(cfg(any(feature = "os-util", feature = "tcp", feature = "udp", feature = "uds", feature = "pipe"))))] - $item - )* - } -} - -/// Feature `os-util` enabled, or one of the features that need `os-util`. -#[cfg(windows)] -macro_rules! cfg_any_os_util { - ($($item:item)*) => { - $( - #[cfg(any(feature = "os-util", feature = "tcp", feature = "udp", feature = "pipe"))] - #[cfg_attr(docsrs, doc(cfg(any(feature = "os-util", feature = "tcp", feature = "udp", feature = "pipe"))))] - $item - )* - } -} diff --git a/src/net/mod.rs b/src/net/mod.rs index 91804ec..4df701d 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,4 +1,4 @@ -//! Networking primitives +//! Networking primitives. //! //! The types provided in this module are non-blocking by default and are //! designed to be portable across all supported Mio platforms. As long as the @@ -7,18 +7,13 @@ //! //! [portability guidelines]: ../struct.Poll.html#portability -cfg_tcp! { - mod tcp; - pub use self::tcp::{TcpListener, TcpSocket, TcpStream}; -} +mod tcp; +pub use self::tcp::{TcpListener, TcpSocket, TcpStream, TcpKeepalive}; -cfg_udp! { - mod udp; - pub use self::udp::UdpSocket; -} +mod udp; +pub use self::udp::UdpSocket; #[cfg(unix)] -cfg_uds! { - mod uds; - pub use self::uds::{SocketAddr, UnixDatagram, UnixListener, UnixStream}; -} +mod uds; +#[cfg(unix)] +pub use self::uds::{SocketAddr, UnixDatagram, UnixListener, UnixStream}; diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index b6f5736..da276f3 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -13,7 +13,8 @@ use crate::{event, sys, Interest, Registry, Token}; /// /// # Examples /// -/// ``` +#[cfg_attr(feature = "os-poll", doc = "```")] +#[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Interest, Poll, Token}; diff --git a/src/net/tcp/mod.rs b/src/net/tcp/mod.rs index b39b909..4e47aee 100644 --- a/src/net/tcp/mod.rs +++ b/src/net/tcp/mod.rs @@ -2,7 +2,7 @@ mod listener; pub use self::listener::TcpListener; mod socket; -pub use self::socket::TcpSocket; +pub use self::socket::{TcpSocket, TcpKeepalive}; mod stream; pub use self::stream::TcpStream; diff --git a/src/net/tcp/socket.rs b/src/net/tcp/socket.rs index f3e27c3..35a589c 100644 --- a/src/net/tcp/socket.rs +++ b/src/net/tcp/socket.rs @@ -1,14 +1,14 @@ -use crate::net::{TcpStream, TcpListener}; -use crate::sys; - use std::io; use std::mem; use std::net::SocketAddr; -use std::time::Duration; #[cfg(unix)] -use std::os::unix::io::{AsRawFd, RawFd, FromRawFd}; +use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; +use std::time::Duration; + +use crate::net::{TcpListener, TcpStream}; +use crate::sys; /// A non-blocking TCP socket used to configure a stream or listener. /// @@ -22,23 +22,42 @@ pub struct TcpSocket { sys: sys::tcp::TcpSocket, } +/// Configures a socket's TCP keepalive parameters. +#[derive(Debug, Default, Clone)] +pub struct TcpKeepalive { + pub(crate) time: Option, + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + target_os = "windows", + ))] + pub(crate) interval: Option, + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + ))] + pub(crate) retries: Option, +} + impl TcpSocket { /// Create a new IPv4 TCP socket. /// /// This calls `socket(2)`. pub fn new_v4() -> io::Result { - sys::tcp::new_v4_socket().map(|sys| TcpSocket { - sys - }) + sys::tcp::new_v4_socket().map(|sys| TcpSocket { sys }) } /// Create a new IPv6 TCP socket. /// /// This calls `socket(2)`. pub fn new_v6() -> io::Result { - sys::tcp::new_v6_socket().map(|sys| TcpSocket { - sys - }) + sys::tcp::new_v6_socket().map(|sys| TcpSocket { sys }) } pub(crate) fn new_for_addr(addr: SocketAddr) -> io::Result { @@ -106,6 +125,201 @@ impl TcpSocket { sys::tcp::set_linger(self.sys, dur) } + /// Gets the value of `SO_LINGER` on this socket + pub fn get_linger(&self) -> io::Result> { + sys::tcp::get_linger(self.sys) + } + + /// Sets the value of `SO_RCVBUF` on this socket. + pub fn set_recv_buffer_size(&self, size: u32) -> io::Result<()> { + sys::tcp::set_recv_buffer_size(self.sys, size) + } + + /// Get the value of `SO_RCVBUF` set on this socket. + /// + /// Note that if [`set_recv_buffer_size`] has been called on this socket + /// previously, the value returned by this function may not be the same as + /// the argument provided to `set_recv_buffer_size`. This is for the + /// following reasons: + /// + /// * Most operating systems have minimum and maximum allowed sizes for the + /// receive buffer, and will clamp the provided value if it is below the + /// minimum or above the maximum. The minimum and maximum buffer sizes are + /// OS-dependent. + /// * Linux will double the buffer size to account for internal bookkeeping + /// data, and returns the doubled value from `getsockopt(2)`. As per `man + /// 7 socket`: + /// > Sets or gets the maximum socket receive buffer in bytes. The + /// > kernel doubles this value (to allow space for bookkeeping + /// > overhead) when it is set using `setsockopt(2)`, and this doubled + /// > value is returned by `getsockopt(2)`. + /// + /// [`set_recv_buffer_size`]: #method.set_recv_buffer_size + pub fn get_recv_buffer_size(&self) -> io::Result { + sys::tcp::get_recv_buffer_size(self.sys) + } + + /// Sets the value of `SO_SNDBUF` on this socket. + pub fn set_send_buffer_size(&self, size: u32) -> io::Result<()> { + sys::tcp::set_send_buffer_size(self.sys, size) + } + + /// Get the value of `SO_SNDBUF` set on this socket. + /// + /// Note that if [`set_send_buffer_size`] has been called on this socket + /// previously, the value returned by this function may not be the same as + /// the argument provided to `set_send_buffer_size`. This is for the + /// following reasons: + /// + /// * Most operating systems have minimum and maximum allowed sizes for the + /// receive buffer, and will clamp the provided value if it is below the + /// minimum or above the maximum. The minimum and maximum buffer sizes are + /// OS-dependent. + /// * Linux will double the buffer size to account for internal bookkeeping + /// data, and returns the doubled value from `getsockopt(2)`. As per `man + /// 7 socket`: + /// > Sets or gets the maximum socket send buffer in bytes. The + /// > kernel doubles this value (to allow space for bookkeeping + /// > overhead) when it is set using `setsockopt(2)`, and this doubled + /// > value is returned by `getsockopt(2)`. + /// + /// [`set_send_buffer_size`]: #method.set_send_buffer_size + pub fn get_send_buffer_size(&self) -> io::Result { + sys::tcp::get_send_buffer_size(self.sys) + } + + /// Sets whether keepalive messages are enabled to be sent on this socket. + /// + /// This will set the `SO_KEEPALIVE` option on this socket. + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + sys::tcp::set_keepalive(self.sys, keepalive) + } + + /// Returns whether or not TCP keepalive probes will be sent by this socket. + pub fn get_keepalive(&self) -> io::Result { + sys::tcp::get_keepalive(self.sys) + } + + /// Sets parameters configuring TCP keepalive probes for this socket. + /// + /// The supported parameters depend on the operating system, and are + /// configured using the [`TcpKeepalive`] struct. At a minimum, all systems + /// support configuring the [keepalive time]: the time after which the OS + /// will start sending keepalive messages on an idle connection. + /// + /// # Notes + /// + /// * This will enable TCP keepalive on this socket, if it is not already + /// enabled. + /// * On some platforms, such as Windows, any keepalive parameters *not* + /// configured by the `TcpKeepalive` struct passed to this function may be + /// overwritten with their default values. Therefore, this function should + /// either only be called once per socket, or the same parameters should + /// be passed every time it is called. + /// + /// # Examples + /// ``` + /// use mio::net::{TcpSocket, TcpKeepalive}; + /// use std::time::Duration; + /// + /// # fn main() -> Result<(), std::io::Error> { + /// let socket = TcpSocket::new_v6()?; + /// let keepalive = TcpKeepalive::default() + /// .with_time(Duration::from_secs(4)); + /// // Depending on the target operating system, we may also be able to + /// // configure the keepalive probe interval and/or the number of retries + /// // here as well. + /// + /// socket.set_keepalive_params(keepalive)?; + /// # Ok(()) } + /// ``` + /// + /// [`TcpKeepalive`]: ../struct.TcpKeepalive.html + /// [keepalive time]: ../struct.TcpKeepalive.html#method.with_time + pub fn set_keepalive_params(&self, keepalive: TcpKeepalive) -> io::Result<()> { + self.set_keepalive(true)?; + sys::tcp::set_keepalive_params(self.sys, keepalive) + } + + /// Returns the amount of time after which TCP keepalive probes will be sent + /// on idle connections. + /// + /// If `None`, then keepalive messages are disabled. + /// + /// This returns the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD, + /// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE` + /// on all other Unix operating systems. On Windows, it is not possible to + /// access the value of TCP keepalive parameters after they have been set. + /// + /// Some platforms specify this value in seconds, so sub-second + /// specifications may be omitted. + #[cfg_attr(docsrs, doc(cfg(not(target_os = "windows"))))] + #[cfg(not(target_os = "windows"))] + pub fn get_keepalive_time(&self) -> io::Result> { + sys::tcp::get_keepalive_time(self.sys) + } + + /// Returns the time interval between TCP keepalive probes, if TCP keepalive is + /// enabled on this socket. + /// + /// If `None`, then keepalive messages are disabled. + /// + /// This returns the value of `TCP_KEEPINTVL` on supported Unix operating + /// systems. On Windows, it is not possible to access the value of TCP + /// keepalive parameters after they have been set.. + /// + /// Some platforms specify this value in seconds, so sub-second + /// specifications may be omitted. + #[cfg_attr( + docsrs, + doc(cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + ))) + )] + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + ))] + pub fn get_keepalive_interval(&self) -> io::Result> { + sys::tcp::get_keepalive_interval(self.sys) + } + + /// Returns the maximum number of TCP keepalive probes that will be sent before + /// dropping a connection, if TCP keepalive is enabled on this socket. + /// + /// If `None`, then keepalive messages are disabled. + /// + /// This returns the value of `TCP_KEEPCNT` on Unix operating systems that + /// support this option. On Windows, it is not possible to access the value + /// of TCP keepalive parameters after they have been set. + #[cfg_attr( + docsrs, + doc(cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + ))) + )] + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + ))] + pub fn get_keepalive_retries(&self) -> io::Result> { + sys::tcp::get_keepalive_retries(self.sys) + } + /// Returns the local address of this socket /// /// Will return `Err` result in windows if called before calling `bind` @@ -120,6 +334,16 @@ impl Drop for TcpSocket { } } +#[cfg(unix)] +impl IntoRawFd for TcpSocket { + fn into_raw_fd(self) -> RawFd { + let ret = self.sys; + // Avoid closing the socket + mem::forget(self); + ret + } +} + #[cfg(unix)] impl AsRawFd for TcpSocket { fn as_raw_fd(&self) -> RawFd { @@ -172,6 +396,94 @@ impl FromRawSocket for TcpSocket { /// The caller is responsible for ensuring that the socket is in /// non-blocking mode. unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket { - TcpSocket { sys: socket as sys::tcp::TcpSocket } + TcpSocket { + sys: socket as sys::tcp::TcpSocket, + } + } +} + +impl TcpKeepalive { + // Sets the amount of time after which TCP keepalive probes will be sent + /// on idle connections. + /// + /// This will set the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD, + /// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE` + /// on all other Unix operating systems. On Windows, this sets the value of + /// the `tcp_keepalive` struct's `keepalivetime` field. + /// + /// Some platforms specify this value in seconds, so sub-second + /// specifications may be omitted. + pub fn with_time(self, time: Duration) -> Self { + Self { + time: Some(time), + ..self + } + } + + /// Sets the time interval between TCP keepalive probes. + /// This sets the value of `TCP_KEEPINTVL` on supported Unix operating + /// systems. On Windows, this sets the value of the `tcp_keepalive` struct's + /// `keepaliveinterval` field. + /// + /// Some platforms specify this value in seconds, so sub-second + /// specifications may be omitted. + #[cfg_attr( + docsrs, + doc(cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + target_os = "windows" + ))) + )] + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + target_os = "windows" + ))] + pub fn with_interval(self, interval: Duration) -> Self { + Self { + interval: Some(interval), + ..self + } + } + + /// Sets the maximum number of TCP keepalive probes that will be sent before + /// dropping a connection, if TCP keepalive is enabled on this socket. + /// + /// This will set the value of `TCP_KEEPCNT` on Unix operating systems that + /// support this option. + #[cfg_attr( + docsrs, + doc(cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + ))) + )] + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + ))] + pub fn with_retries(self, retries: u32) -> Self { + Self { + retries: Some(retries), + ..self + } + } + + /// Returns a new, empty set of TCP keepalive parameters. + pub fn new() -> Self { + Self::default() } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 86f674c..cdbd46a 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -7,8 +7,8 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; use crate::io_source::IoSource; -use crate::{event, Interest, Registry, Token}; use crate::net::TcpSocket; +use crate::{event, Interest, Registry, Token}; /// A non-blocking TCP stream between a local socket and a remote socket. /// @@ -16,7 +16,8 @@ use crate::net::TcpSocket; /// /// # Examples /// -/// ``` +#[cfg_attr(feature = "os-poll", doc = "```")] +#[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::net::{TcpListener, SocketAddr}; /// # use std::error::Error; /// # diff --git a/src/net/udp.rs b/src/net/udp.rs index 164315a..436b4cc 100644 --- a/src/net/udp.rs +++ b/src/net/udp.rs @@ -27,7 +27,8 @@ use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket} /// /// # Examples /// -/// ``` +#[cfg_attr(feature = "os-poll", doc = "```")] +#[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # /// # fn main() -> Result<(), Box> { @@ -96,7 +97,8 @@ impl UdpSocket { /// /// # Examples /// - /// ``` + #[cfg_attr(feature = "os-poll", doc = "```")] + #[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # /// # fn main() -> Result<(), Box> { @@ -139,8 +141,11 @@ impl UdpSocket { // This assertion is almost, but not quite, universal. It fails on // shared-IP FreeBSD jails. It's hard for mio to know whether we're jailed, // so simply disable the test on FreeBSD. - #[cfg_attr(not(target_os = "freebsd"), doc = " ```")] - #[cfg_attr(target_os = "freebsd", doc = " ```no_run")] + #[cfg_attr(all(feature = "os-poll", not(target_os = "freebsd")), doc = "```")] + #[cfg_attr( + any(not(feature = "os-poll"), target_os = "freebsd"), + doc = "```ignore" + )] /// # use std::error::Error; /// # /// # fn main() -> Result<(), Box> { @@ -303,7 +308,8 @@ impl UdpSocket { /// /// # Examples /// - /// ``` + #[cfg_attr(feature = "os-poll", doc = "```")] + #[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # /// # fn main() -> Result<(), Box> { @@ -332,7 +338,8 @@ impl UdpSocket { /// /// # Examples /// - /// ``` + #[cfg_attr(feature = "os-poll", doc = "```")] + #[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # /// # fn main() -> Result<(), Box> { @@ -412,7 +419,8 @@ impl UdpSocket { /// /// # Examples /// - /// ``` + #[cfg_attr(feature = "os-poll", doc = "```")] + #[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # /// # fn main() -> Result<(), Box> { @@ -440,7 +448,8 @@ impl UdpSocket { /// /// # Examples /// - /// ``` + #[cfg_attr(feature = "os-poll", doc = "```")] + #[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # use std::error::Error; /// # /// # fn main() -> Result<(), Box> { diff --git a/src/poll.rs b/src/poll.rs index 7ff2038..b06f138 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -30,7 +30,8 @@ use std::{fmt, io}; /// /// A basic example -- establishing a `TcpStream` connection. /// -/// ``` +#[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Poll, Interest, Token}; @@ -126,7 +127,8 @@ use std::{fmt, io}; /// /// For example: /// -/// ``` +#[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] /// # use std::error::Error; /// # use std::net; /// # fn main() -> Result<(), Box> { @@ -258,7 +260,8 @@ impl Poll { /// /// A basic example -- establishing a `TcpStream` connection. /// - /// ``` + #[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] + #[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Poll, Interest, Token}; @@ -422,7 +425,8 @@ impl Registry { /// /// # Examples /// - /// ``` + #[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] + #[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] /// # use std::error::Error; /// # use std::net; /// # fn main() -> Result<(), Box> { @@ -499,7 +503,8 @@ impl Registry { /// /// # Examples /// - /// ``` + #[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] + #[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] /// # use std::error::Error; /// # use std::net; /// # fn main() -> Result<(), Box> { @@ -565,7 +570,8 @@ impl Registry { /// /// # Examples /// - /// ``` + #[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] + #[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] /// # use std::error::Error; /// # use std::net; /// # fn main() -> Result<(), Box> { diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 08bd271..81ae6d2 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -54,31 +54,7 @@ cfg_os_poll! { #[cfg(unix)] cfg_os_poll! { mod unix; - pub use self::unix::SourceFd; - - pub(crate) use self::unix::{event, Event, Events, Selector, Waker}; - - cfg_tcp! { - pub(crate) use self::unix::tcp; - } - - cfg_udp! { - pub(crate) use self::unix::udp; - } - - cfg_uds! { - pub use self::unix::SocketAddr; - - pub(crate) use self::unix::uds; - } - - cfg_pipe! { - pub(crate) use self::unix::pipe; - } - - cfg_io_source! { - pub(crate) use self::unix::IoSourceState; - } + pub use self::unix::*; } #[cfg(windows)] @@ -92,13 +68,13 @@ cfg_not_os_poll! { pub(crate) use self::shell::*; #[cfg(unix)] - cfg_any_os_util! { + cfg_any_os_ext! { mod unix; pub use self::unix::SourceFd; } #[cfg(unix)] - cfg_uds! { + cfg_net! { pub use self::unix::SocketAddr; } } diff --git a/src/sys/shell/mod.rs b/src/sys/shell/mod.rs index a63760a..7e1533f 100644 --- a/src/sys/shell/mod.rs +++ b/src/sys/shell/mod.rs @@ -10,16 +10,10 @@ pub(crate) use self::selector::{event, Event, Events, Selector}; mod waker; pub(crate) use self::waker::Waker; -cfg_tcp! { +cfg_net! { pub(crate) mod tcp; -} - -cfg_udp! { pub(crate) mod udp; -} - -#[cfg(unix)] -cfg_uds! { + #[cfg(unix)] pub(crate) mod uds; } diff --git a/src/sys/shell/selector.rs b/src/sys/shell/selector.rs index 69be370..91fc0bf 100644 --- a/src/sys/shell/selector.rs +++ b/src/sys/shell/selector.rs @@ -26,7 +26,7 @@ impl Selector { } #[cfg(unix)] -cfg_any_os_util! { +cfg_any_os_ext! { use crate::{Interest, Token}; impl Selector { diff --git a/src/sys/shell/tcp.rs b/src/sys/shell/tcp.rs index 3073d42..2017bda 100644 --- a/src/sys/shell/tcp.rs +++ b/src/sys/shell/tcp.rs @@ -1,6 +1,7 @@ use std::io; use std::net::{self, SocketAddr}; use std::time::Duration; +use crate::net::TcpKeepalive; pub(crate) type TcpSocket = i32; @@ -50,6 +51,79 @@ pub(crate) fn set_linger(_: TcpSocket, _: Option) -> io::Result<()> { os_required!(); } +pub(crate) fn get_linger(_: TcpSocket) -> io::Result> { + os_required!(); +} + +pub(crate) fn set_recv_buffer_size(_: TcpSocket, _: u32) -> io::Result<()> { + os_required!(); +} + +pub(crate) fn get_recv_buffer_size(_: TcpSocket) -> io::Result { + os_required!(); +} + +pub(crate) fn set_send_buffer_size(_: TcpSocket, _: u32) -> io::Result<()> { + os_required!(); +} + +pub(crate) fn get_send_buffer_size(_: TcpSocket) -> io::Result { + os_required!(); +} + +pub(crate) fn set_keepalive(_: TcpSocket, _: bool) -> io::Result<()> { + os_required!(); +} + +pub(crate) fn get_keepalive(_: TcpSocket) -> io::Result { + os_required!(); +} + +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + target_os = "windows", +))] +pub(crate) fn set_keepalive_params(_: TcpSocket, _: TcpKeepalive) -> io::Result<()> { + os_required!() +} + +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", +))] +pub(crate) fn get_keepalive_time(_: TcpSocket) -> io::Result> { + os_required!() +} + +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", +))] +pub(crate) fn get_keepalive_interval(_: TcpSocket) -> io::Result> { + os_required!() +} + +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", +))] +pub(crate) fn get_keepalive_retries(_: TcpSocket) -> io::Result> { + os_required!() +} + pub fn accept(_: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> { os_required!(); } diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs index f045fb5..231480a 100644 --- a/src/sys/unix/mod.rs +++ b/src/sys/unix/mod.rs @@ -14,8 +14,6 @@ macro_rules! syscall { } cfg_os_poll! { - mod net; - mod selector; pub(crate) use self::selector::{event, Event, Events, Selector}; @@ -25,15 +23,11 @@ cfg_os_poll! { mod waker; pub(crate) use self::waker::Waker; - cfg_tcp! { - pub(crate) mod tcp; - } + cfg_net! { + mod net; - cfg_udp! { + pub(crate) mod tcp; pub(crate) mod udp; - } - - cfg_uds! { pub(crate) mod uds; pub use self::uds::SocketAddr; } @@ -60,18 +54,18 @@ cfg_os_poll! { } } - cfg_pipe! { + cfg_os_ext! { pub(crate) mod pipe; } } cfg_not_os_poll! { - cfg_uds! { + cfg_net! { mod uds; pub use self::uds::SocketAddr; } - cfg_any_os_util! { + cfg_any_os_ext! { mod sourcefd; pub use self::sourcefd::SourceFd; } diff --git a/src/sys/unix/net.rs b/src/sys/unix/net.rs index 2671b42..2f8d618 100644 --- a/src/sys/unix/net.rs +++ b/src/sys/unix/net.rs @@ -1,11 +1,8 @@ -#[cfg(all(feature = "os-poll", any(feature = "tcp", feature = "udp")))] -use std::net::SocketAddr; +use std::io; +use std::mem::size_of; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; -#[cfg(all(feature = "os-poll", any(feature = "udp")))] -pub(crate) fn new_ip_socket( - addr: SocketAddr, - socket_type: libc::c_int, -) -> std::io::Result { +pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result { let domain = match addr { SocketAddr::V4(..) => libc::AF_INET, SocketAddr::V6(..) => libc::AF_INET6, @@ -15,14 +12,7 @@ pub(crate) fn new_ip_socket( } /// Create a new non-blocking socket. -#[cfg(all( - feature = "os-poll", - any(feature = "tcp", feature = "udp", feature = "uds") -))] -pub(crate) fn new_socket( - domain: libc::c_int, - socket_type: libc::c_int, -) -> std::io::Result { +pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::Result { #[cfg(any( target_os = "android", target_os = "dragonfly", @@ -46,7 +36,7 @@ pub(crate) fn new_socket( libc::SOL_SOCKET, libc::SO_NOSIGPIPE, &1 as *const libc::c_int as *const libc::c_void, - std::mem::size_of::() as libc::socklen_t + size_of::() as libc::socklen_t )) .map(|_| socket) }); @@ -70,34 +60,110 @@ pub(crate) fn new_socket( socket } -#[cfg(all(feature = "os-poll", any(feature = "tcp", feature = "udp")))] -pub(crate) fn socket_addr(addr: &SocketAddr) -> (*const libc::sockaddr, libc::socklen_t) { - use std::mem::size_of_val; +/// A type with the same memory layout as `libc::sockaddr`. Used in converting Rust level +/// SocketAddr* types into their system representation. The benefit of this specific +/// type over using `libc::sockaddr_storage` is that this type is exactly as large as it +/// needs to be and not a lot larger. And it can be initialized cleaner from Rust. +#[repr(C)] +pub(crate) union SocketAddrCRepr { + v4: libc::sockaddr_in, + v6: libc::sockaddr_in6, +} + +impl SocketAddrCRepr { + pub(crate) fn as_ptr(&self) -> *const libc::sockaddr { + self as *const _ as *const libc::sockaddr + } +} +/// Converts a Rust `SocketAddr` into the system representation. +pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t) { match addr { - SocketAddr::V4(ref addr) => ( - addr as *const _ as *const libc::sockaddr, - size_of_val(addr) as libc::socklen_t, - ), - SocketAddr::V6(ref addr) => ( - addr as *const _ as *const libc::sockaddr, - size_of_val(addr) as libc::socklen_t, - ), + SocketAddr::V4(ref addr) => { + // `s_addr` is stored as BE on all machine and the array is in BE order. + // So the native endian conversion method is used so that it's never swapped. + let sin_addr = libc::in_addr { + s_addr: u32::from_ne_bytes(addr.ip().octets()), + }; + + let sockaddr_in = libc::sockaddr_in { + sin_family: libc::AF_INET as libc::sa_family_t, + sin_port: addr.port().to_be(), + sin_addr, + sin_zero: [0; 8], + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + sin_len: 0, + }; + + let sockaddr = SocketAddrCRepr { v4: sockaddr_in }; + let socklen = size_of::() as libc::socklen_t; + (sockaddr, socklen) + } + SocketAddr::V6(ref addr) => { + let sockaddr_in6 = libc::sockaddr_in6 { + sin6_family: libc::AF_INET6 as libc::sa_family_t, + sin6_port: addr.port().to_be(), + sin6_addr: libc::in6_addr { + s6_addr: addr.ip().octets(), + }, + sin6_flowinfo: addr.flowinfo(), + sin6_scope_id: addr.scope_id(), + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + sin6_len: 0, + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + __sin6_src_id: 0, + }; + + let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 }; + let socklen = size_of::() as libc::socklen_t; + (sockaddr, socklen) + } } } -/// `storage` must be initialised to `sockaddr_in` or `sockaddr_in6`. -#[cfg(all(feature = "os-poll", feature = "tcp"))] +/// Converts a `libc::sockaddr` compatible struct into a native Rust `SocketAddr`. +/// +/// # Safety +/// +/// `storage` must have the `ss_family` field correctly initialized. +/// `storage` must be initialised to a `sockaddr_in` or `sockaddr_in6`. pub(crate) unsafe fn to_socket_addr( storage: *const libc::sockaddr_storage, -) -> std::io::Result { +) -> io::Result { match (*storage).ss_family as libc::c_int { - libc::AF_INET => Ok(SocketAddr::V4( - *(storage as *const libc::sockaddr_in as *const _), - )), - libc::AF_INET6 => Ok(SocketAddr::V6( - *(storage as *const libc::sockaddr_in6 as *const _), - )), - _ => Err(std::io::ErrorKind::InvalidInput.into()), + libc::AF_INET => { + // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in. + let addr: &libc::sockaddr_in = &*(storage as *const libc::sockaddr_in); + let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()); + let port = u16::from_be(addr.sin_port); + Ok(SocketAddr::V4(SocketAddrV4::new(ip, port))) + } + libc::AF_INET6 => { + // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6. + let addr: &libc::sockaddr_in6 = &*(storage as *const libc::sockaddr_in6); + let ip = Ipv6Addr::from(addr.sin6_addr.s6_addr); + let port = u16::from_be(addr.sin6_port); + Ok(SocketAddr::V6(SocketAddrV6::new( + ip, + port, + addr.sin6_flowinfo, + addr.sin6_scope_id, + ))) + } + _ => Err(io::ErrorKind::InvalidInput.into()), } } diff --git a/src/sys/unix/selector/kqueue.rs b/src/sys/unix/selector/kqueue.rs index 454f47d..f509f92 100644 --- a/src/sys/unix/selector/kqueue.rs +++ b/src/sys/unix/selector/kqueue.rs @@ -671,6 +671,7 @@ pub mod event { } #[test] +#[cfg(feature = "os-ext")] fn does_not_register_rw() { use crate::unix::SourceFd; use crate::{Poll, Token}; diff --git a/src/sys/unix/sourcefd.rs b/src/sys/unix/sourcefd.rs index 68511d7..ba52b38 100644 --- a/src/sys/unix/sourcefd.rs +++ b/src/sys/unix/sourcefd.rs @@ -25,7 +25,8 @@ use std::os::unix::io::RawFd; /// /// Basic usage. /// -/// ``` +#[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Interest, Poll, Token}; @@ -50,7 +51,8 @@ use std::os::unix::io::RawFd; /// /// Implementing [`event::Source`] for a custom type backed by a [`RawFd`]. /// -/// ``` +#[cfg_attr(all(feature = "os-poll", features = "os-ext"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", features = "os-ext")), doc = "```ignore")] /// use mio::{event, Interest, Registry, Token}; /// use mio::unix::SourceFd; /// diff --git a/src/sys/unix/tcp.rs b/src/sys/unix/tcp.rs index 65b7400..9e1d700 100644 --- a/src/sys/unix/tcp.rs +++ b/src/sys/unix/tcp.rs @@ -1,12 +1,26 @@ +use std::convert::TryInto; use std::io; use std::mem; use std::mem::{size_of, MaybeUninit}; use std::net::{self, SocketAddr}; -use std::time::Duration; use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::time::Duration; use crate::sys::unix::net::{new_socket, socket_addr, to_socket_addr}; +use crate::net::TcpKeepalive; +#[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "haiku"))] +use libc::SO_KEEPALIVE as KEEPALIVE_TIME; +#[cfg(any(target_os = "macos", target_os = "ios"))] +use libc::TCP_KEEPALIVE as KEEPALIVE_TIME; +#[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "openbsd", + target_os = "netbsd", + target_os = "haiku" +)))] +use libc::TCP_KEEPIDLE as KEEPALIVE_TIME; pub type TcpSocket = libc::c_int; pub(crate) fn new_v4_socket() -> io::Result { @@ -19,14 +33,14 @@ pub(crate) fn new_v6_socket() -> io::Result { pub(crate) fn bind(socket: TcpSocket, addr: SocketAddr) -> io::Result<()> { let (raw_addr, raw_addr_length) = socket_addr(&addr); - syscall!(bind(socket, raw_addr, raw_addr_length))?; + syscall!(bind(socket, raw_addr.as_ptr(), raw_addr_length))?; Ok(()) } pub(crate) fn connect(socket: TcpSocket, addr: SocketAddr) -> io::Result { let (raw_addr, raw_addr_length) = socket_addr(&addr); - match syscall!(connect(socket, raw_addr, raw_addr_length)) { + match syscall!(connect(socket, raw_addr.as_ptr(), raw_addr_length)) { Err(err) if err.raw_os_error() != Some(libc::EINPROGRESS) => { Err(err) } @@ -37,8 +51,6 @@ pub(crate) fn connect(socket: TcpSocket, addr: SocketAddr) -> io::Result io::Result { - use std::convert::TryInto; - let backlog = backlog.try_into().unwrap_or(i32::max_value()); syscall!(listen(socket, backlog))?; Ok(unsafe { net::TcpListener::from_raw_fd(socket) }) @@ -56,7 +68,8 @@ pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<() libc::SO_REUSEADDR, &val as *const libc::c_int as *const libc::c_void, size_of::() as libc::socklen_t, - )).map(|_| ()) + )) + .map(|_| ()) } pub(crate) fn get_reuseaddr(socket: TcpSocket) -> io::Result { @@ -84,7 +97,8 @@ pub(crate) fn set_reuseport(socket: TcpSocket, reuseport: bool) -> io::Result<() libc::SO_REUSEPORT, &val as *const libc::c_int as *const libc::c_void, size_of::() as libc::socklen_t, - )).map(|_| ()) + )) + .map(|_| ()) } #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] @@ -119,7 +133,9 @@ pub(crate) fn get_localaddr(socket: TcpSocket) -> io::Result { pub(crate) fn set_linger(socket: TcpSocket, dur: Option) -> io::Result<()> { let val: libc::linger = libc::linger { l_onoff: if dur.is_some() { 1 } else { 0 }, - l_linger: dur.map(|dur| dur.as_secs() as libc::c_int).unwrap_or_default(), + l_linger: dur + .map(|dur| dur.as_secs() as libc::c_int) + .unwrap_or_default(), }; syscall!(setsockopt( socket, @@ -127,7 +143,277 @@ pub(crate) fn set_linger(socket: TcpSocket, dur: Option) -> io::Result libc::SO_LINGER, &val as *const libc::linger as *const libc::c_void, size_of::() as libc::socklen_t, - )).map(|_| ()) + )) + .map(|_| ()) +} + +pub(crate) fn get_linger(socket: TcpSocket) -> io::Result> { + let mut val: libc::linger = unsafe { std::mem::zeroed() }; + let mut len = mem::size_of::() as libc::socklen_t; + + syscall!(getsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_LINGER, + &mut val as *mut _ as *mut _, + &mut len, + ))?; + + if val.l_onoff == 0 { + Ok(None) + } else { + Ok(Some(Duration::from_secs(val.l_linger as u64))) + } +} + +pub(crate) fn set_recv_buffer_size(socket: TcpSocket, size: u32) -> io::Result<()> { + let size = size.try_into().ok().unwrap_or_else(i32::max_value); + syscall!(setsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_RCVBUF, + &size as *const _ as *const libc::c_void, + size_of::() as libc::socklen_t + )) + .map(|_| ()) +} + +pub(crate) fn get_recv_buffer_size(socket: TcpSocket) -> io::Result { + let mut optval: libc::c_int = 0; + let mut optlen = size_of::() as libc::socklen_t; + syscall!(getsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_RCVBUF, + &mut optval as *mut _ as *mut _, + &mut optlen, + ))?; + + Ok(optval as u32) +} + +pub(crate) fn set_send_buffer_size(socket: TcpSocket, size: u32) -> io::Result<()> { + let size = size.try_into().ok().unwrap_or_else(i32::max_value); + syscall!(setsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_SNDBUF, + &size as *const _ as *const libc::c_void, + size_of::() as libc::socklen_t + )) + .map(|_| ()) +} + +pub(crate) fn get_send_buffer_size(socket: TcpSocket) -> io::Result { + let mut optval: libc::c_int = 0; + let mut optlen = size_of::() as libc::socklen_t; + + syscall!(getsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_SNDBUF, + &mut optval as *mut _ as *mut _, + &mut optlen, + ))?; + + Ok(optval as u32) +} + +pub(crate) fn set_keepalive(socket: TcpSocket, keepalive: bool) -> io::Result<()> { + let val: libc::c_int = if keepalive { 1 } else { 0 }; + syscall!(setsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_KEEPALIVE, + &val as *const _ as *const libc::c_void, + size_of::() as libc::socklen_t + )) + .map(|_| ()) +} + +pub(crate) fn get_keepalive(socket: TcpSocket) -> io::Result { + let mut optval: libc::c_int = 0; + let mut optlen = mem::size_of::() as libc::socklen_t; + + syscall!(getsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_KEEPALIVE, + &mut optval as *mut _ as *mut _, + &mut optlen, + ))?; + + Ok(optval != 0) +} + +pub(crate) fn set_keepalive_params(socket: TcpSocket, keepalive: TcpKeepalive) -> io::Result<()> { + if let Some(dur) = keepalive.time { + set_keepalive_time(socket, dur)?; + } + + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + ))] + { + if let Some(dur) = keepalive.interval { + set_keepalive_interval(socket, dur)?; + } + + if let Some(retries) = keepalive.retries { + set_keepalive_retries(socket, retries)?; + } + } + + + Ok(()) +} + +fn set_keepalive_time(socket: TcpSocket, time: Duration) -> io::Result<()> { + let time_secs = time + .as_secs() + .try_into() + .ok() + .unwrap_or_else(i32::max_value); + syscall!(setsockopt( + socket, + libc::IPPROTO_TCP, + KEEPALIVE_TIME, + &(time_secs as libc::c_int) as *const _ as *const libc::c_void, + size_of::() as libc::socklen_t + )) + .map(|_| ()) +} + +pub(crate) fn get_keepalive_time(socket: TcpSocket) -> io::Result> { + if !get_keepalive(socket)? { + return Ok(None); + } + + let mut optval: libc::c_int = 0; + let mut optlen = mem::size_of::() as libc::socklen_t; + syscall!(getsockopt( + socket, + libc::IPPROTO_TCP, + KEEPALIVE_TIME, + &mut optval as *mut _ as *mut _, + &mut optlen, + ))?; + + Ok(Some(Duration::from_secs(optval as u64))) +} + +/// Linux, FreeBSD, and NetBSD support setting the keepalive interval via +/// `TCP_KEEPINTVL`. +/// See: +/// - https://man7.org/linux/man-pages/man7/tcp.7.html +/// - https://www.freebsd.org/cgi/man.cgi?query=tcp#end +/// - http://man.netbsd.org/tcp.4#DESCRIPTION +/// +/// OpenBSD does not: +/// https://man.openbsd.org/tcp +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", +))] +fn set_keepalive_interval(socket: TcpSocket, interval: Duration) -> io::Result<()> { + let interval_secs = interval + .as_secs() + .try_into() + .ok() + .unwrap_or_else(i32::max_value); + syscall!(setsockopt( + socket, + libc::IPPROTO_TCP, + libc::TCP_KEEPINTVL, + &(interval_secs as libc::c_int) as *const _ as *const libc::c_void, + size_of::() as libc::socklen_t + )) + .map(|_| ()) +} + +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", +))] +pub(crate) fn get_keepalive_interval(socket: TcpSocket) -> io::Result> { + if !get_keepalive(socket)? { + return Ok(None); + } + + let mut optval: libc::c_int = 0; + let mut optlen = mem::size_of::() as libc::socklen_t; + syscall!(getsockopt( + socket, + libc::IPPROTO_TCP, + libc::TCP_KEEPINTVL, + &mut optval as *mut _ as *mut _, + &mut optlen, + ))?; + + Ok(Some(Duration::from_secs(optval as u64))) +} + +/// Linux, macOS/iOS, FreeBSD, and NetBSD support setting the number of TCP +/// keepalive retries via `TCP_KEEPCNT`. +/// See: +/// - https://man7.org/linux/man-pages/man7/tcp.7.html +/// - https://www.freebsd.org/cgi/man.cgi?query=tcp#end +/// - http://man.netbsd.org/tcp.4#DESCRIPTION +/// +/// OpenBSD does not: +/// https://man.openbsd.org/tcp +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", +))] +fn set_keepalive_retries(socket: TcpSocket, retries: u32) -> io::Result<()> { + let retries = retries.try_into().ok().unwrap_or_else(i32::max_value); + syscall!(setsockopt( + socket, + libc::IPPROTO_TCP, + libc::TCP_KEEPCNT, + &(retries as libc::c_int) as *const _ as *const libc::c_void, + size_of::() as libc::socklen_t + )) + .map(|_| ()) +} + +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", +))] +pub(crate) fn get_keepalive_retries(socket: TcpSocket) -> io::Result> { + if !get_keepalive(socket)? { + return Ok(None); + } + + let mut optval: libc::c_int = 0; + let mut optlen = mem::size_of::() as libc::socklen_t; + syscall!(getsockopt( + socket, + libc::IPPROTO_TCP, + libc::TCP_KEEPCNT, + &mut optval as *mut _ as *mut _, + &mut optlen, + ))?; + + Ok(Some(optval as u32)) } pub fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> { diff --git a/src/sys/unix/udp.rs b/src/sys/unix/udp.rs index 947a60a..e9c4d4c 100644 --- a/src/sys/unix/udp.rs +++ b/src/sys/unix/udp.rs @@ -11,7 +11,7 @@ pub fn bind(addr: SocketAddr) -> io::Result { socket.and_then(|socket| { let (raw_addr, raw_addr_length) = socket_addr(&addr); - syscall!(bind(socket, raw_addr, raw_addr_length)) + syscall!(bind(socket, raw_addr.as_ptr(), raw_addr_length)) .map_err(|err| { // Close the socket if we hit an error, ignoring the error // from closing since we can't pass back two errors. diff --git a/src/sys/unix/uds/socketaddr.rs b/src/sys/unix/uds/socketaddr.rs index ddfa2f0..31f8a51 100644 --- a/src/sys/unix/uds/socketaddr.rs +++ b/src/sys/unix/uds/socketaddr.rs @@ -109,8 +109,6 @@ impl fmt::Debug for SocketAddr { } } -// ===== impl AsciiEscaped ===== - impl<'a> fmt::Display for AsciiEscaped<'a> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "\"")?; diff --git a/src/sys/windows/afd.rs b/src/sys/windows/afd.rs index b2e3b11..bf3704d 100644 --- a/src/sys/windows/afd.rs +++ b/src/sys/windows/afd.rs @@ -112,17 +112,16 @@ impl Afd { } cfg_io_source! { - use miow::iocp::CompletionPort; - use ntapi::ntioapi::FILE_OPEN; - use ntapi::ntioapi::NtCreateFile; use std::mem::zeroed; use std::os::windows::io::{FromRawHandle, RawHandle}; use std::sync::atomic::{AtomicUsize, Ordering}; + + use miow::iocp::CompletionPort; + use ntapi::ntioapi::{NtCreateFile, FILE_OPEN}; use winapi::shared::ntdef::{OBJECT_ATTRIBUTES, UNICODE_STRING, USHORT, WCHAR}; use winapi::um::handleapi::INVALID_HANDLE_VALUE; use winapi::um::winbase::{SetFileCompletionNotificationModes, FILE_SKIP_SET_EVENT_ON_HANDLE}; - use winapi::um::winnt::SYNCHRONIZE; - use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE}; + use winapi::um::winnt::{SYNCHRONIZE, FILE_SHARE_READ, FILE_SHARE_WRITE}; const AFD_HELPER_ATTRIBUTES: OBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES { Length: size_of::() as ULONG, diff --git a/src/sys/windows/event.rs b/src/sys/windows/event.rs index 235074a..4d04e64 100644 --- a/src/sys/windows/event.rs +++ b/src/sys/windows/event.rs @@ -26,7 +26,7 @@ impl Event { self.flags |= afd::POLL_RECEIVE } - #[cfg(feature = "os-util")] + #[cfg(feature = "os-ext")] pub(super) fn set_writable(&mut self) { self.flags |= afd::POLL_SEND; } diff --git a/src/sys/windows/io_status_block.rs b/src/sys/windows/io_status_block.rs index 9da5e7a..3e60334 100644 --- a/src/sys/windows/io_status_block.rs +++ b/src/sys/windows/io_status_block.rs @@ -1,7 +1,8 @@ -use ntapi::ntioapi::IO_STATUS_BLOCK; use std::fmt; use std::ops::{Deref, DerefMut}; +use ntapi::ntioapi::IO_STATUS_BLOCK; + pub struct IoStatusBlock(IO_STATUS_BLOCK); cfg_io_source! { diff --git a/src/sys/windows/mod.rs b/src/sys/windows/mod.rs index 25590c2..98b6fc6 100644 --- a/src/sys/windows/mod.rs +++ b/src/sys/windows/mod.rs @@ -25,26 +25,20 @@ cfg_net! { } }}; } -} -cfg_tcp! { - pub(crate) mod tcp; -} + mod net; -cfg_udp! { + pub(crate) mod tcp; pub(crate) mod udp; } -#[cfg(feature = "os-util")] -pub(crate) mod named_pipe; +cfg_os_ext! { + pub(crate) mod named_pipe; +} mod waker; pub(crate) use waker::Waker; -cfg_net! { - mod net; -} - cfg_io_source! { use std::io; use std::os::windows::io::RawSocket; diff --git a/src/sys/windows/net.rs b/src/sys/windows/net.rs index f825ee3..2de98fa 100644 --- a/src/sys/windows/net.rs +++ b/src/sys/windows/net.rs @@ -1,13 +1,14 @@ use std::io; -use std::mem::size_of_val; +use std::mem; use std::net::SocketAddr; use std::sync::Once; use winapi::ctypes::c_int; -use winapi::shared::ws2def::SOCKADDR; -use winapi::um::winsock2::{ - ioctlsocket, socket, FIONBIO, INVALID_SOCKET, SOCKET, -}; +use winapi::shared::inaddr::{in_addr_S_un, IN_ADDR}; +use winapi::shared::in6addr::{in6_addr_u, IN6_ADDR}; +use winapi::shared::ws2def::{AF_INET, AF_INET6, ADDRESS_FAMILY, SOCKADDR, SOCKADDR_IN}; +use winapi::shared::ws2ipdef::{SOCKADDR_IN6_LH, SOCKADDR_IN6_LH_u}; +use winapi::um::winsock2::{ioctlsocket, socket, FIONBIO, INVALID_SOCKET, SOCKET}; /// Initialise the network stack for Windows. pub(crate) fn init() { @@ -21,7 +22,6 @@ pub(crate) fn init() { } /// Create a new non-blocking socket. -#[cfg(feature = "udp")] pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: c_int) -> io::Result { use winapi::um::winsock2::{PF_INET, PF_INET6}; @@ -44,15 +44,65 @@ pub(crate) fn new_socket(domain: c_int, socket_type: c_int) -> io::Result (*const SOCKADDR, c_int) { +/// A type with the same memory layout as `SOCKADDR`. Used in converting Rust level +/// SocketAddr* types into their system representation. The benefit of this specific +/// type over using `SOCKADDR_STORAGE` is that this type is exactly as large as it +/// needs to be and not a lot larger. And it can be initialized cleaner from Rust. +#[repr(C)] +pub(crate) union SocketAddrCRepr { + v4: SOCKADDR_IN, + v6: SOCKADDR_IN6_LH, +} + +impl SocketAddrCRepr { + pub(crate) fn as_ptr(&self) -> *const SOCKADDR { + self as *const _ as *const SOCKADDR + } +} + +pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, c_int) { match addr { - SocketAddr::V4(ref addr) => ( - addr as *const _ as *const SOCKADDR, - size_of_val(addr) as c_int, - ), - SocketAddr::V6(ref addr) => ( - addr as *const _ as *const SOCKADDR, - size_of_val(addr) as c_int, - ), + SocketAddr::V4(ref addr) => { + // `s_addr` is stored as BE on all machine and the array is in BE order. + // So the native endian conversion method is used so that it's never swapped. + let sin_addr = unsafe { + let mut s_un = mem::zeroed::(); + *s_un.S_addr_mut() = u32::from_ne_bytes(addr.ip().octets()); + IN_ADDR { S_un: s_un } + }; + + let sockaddr_in = SOCKADDR_IN { + sin_family: AF_INET as ADDRESS_FAMILY, + sin_port: addr.port().to_be(), + sin_addr, + sin_zero: [0; 8], + }; + + let sockaddr = SocketAddrCRepr { v4: sockaddr_in }; + (sockaddr, mem::size_of::() as c_int) + }, + SocketAddr::V6(ref addr) => { + let sin6_addr = unsafe { + let mut u = mem::zeroed::(); + *u.Byte_mut() = addr.ip().octets(); + IN6_ADDR { u } + }; + let u = unsafe { + let mut u = mem::zeroed::(); + *u.sin6_scope_id_mut() = addr.scope_id(); + u + }; + + let sockaddr_in6 = SOCKADDR_IN6_LH { + sin6_family: AF_INET6 as ADDRESS_FAMILY, + sin6_port: addr.port().to_be(), + sin6_addr, + sin6_flowinfo: addr.flowinfo(), + u, + }; + + let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 }; + (sockaddr, mem::size_of::() as c_int) + } } } diff --git a/src/sys/windows/overlapped.rs b/src/sys/windows/overlapped.rs index 3708f9e..837b78b 100644 --- a/src/sys/windows/overlapped.rs +++ b/src/sys/windows/overlapped.rs @@ -3,9 +3,9 @@ use crate::sys::windows::Event; use std::cell::UnsafeCell; use std::fmt; -use winapi::um::minwinbase::OVERLAPPED_ENTRY; -#[cfg(feature = "os-util")] +#[cfg(feature = "os-ext")] use winapi::um::minwinbase::OVERLAPPED; +use winapi::um::minwinbase::OVERLAPPED_ENTRY; #[repr(C)] pub(crate) struct Overlapped { @@ -13,7 +13,7 @@ pub(crate) struct Overlapped { pub(crate) callback: fn(&OVERLAPPED_ENTRY, Option<&mut Vec>), } -#[cfg(feature = "os-util")] +#[cfg(feature = "os-ext")] impl Overlapped { pub(crate) fn new(cb: fn(&OVERLAPPED_ENTRY, Option<&mut Vec>)) -> Overlapped { Overlapped { diff --git a/src/sys/windows/selector.rs b/src/sys/windows/selector.rs index df2c3f0..572a9a9 100644 --- a/src/sys/windows/selector.rs +++ b/src/sys/windows/selector.rs @@ -374,7 +374,7 @@ impl Selector { self.inner.cp.clone() } - #[cfg(feature = "os-util")] + #[cfg(feature = "os-ext")] pub(super) fn same_port(&self, other: &Arc) -> bool { Arc::ptr_eq(&self.inner.cp, other) } @@ -749,4 +749,4 @@ cfg_net! { flags } -} \ No newline at end of file +} diff --git a/src/sys/windows/tcp.rs b/src/sys/windows/tcp.rs index b78d864..6757b44 100644 --- a/src/sys/windows/tcp.rs +++ b/src/sys/windows/tcp.rs @@ -1,21 +1,25 @@ use std::io; +use std::convert::TryInto; use std::mem::size_of; -use std::net::{self, SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::time::Duration; +use std::ptr; use std::os::windows::io::FromRawSocket; use std::os::windows::raw::SOCKET as StdSocket; // winapi uses usize, stdlib uses u32/u64. -use winapi::ctypes::{c_char, c_int, c_ushort}; -use winapi::shared::ws2def::{SOCKADDR_STORAGE, AF_INET, SOCKADDR_IN}; +use winapi::ctypes::{c_char, c_int, c_ushort, c_ulong}; +use winapi::shared::ws2def::{SOCKADDR_STORAGE, AF_INET, AF_INET6, SOCKADDR_IN}; use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH; +use winapi::shared::mstcpip; -use winapi::shared::minwindef::{BOOL, TRUE, FALSE}; +use winapi::shared::minwindef::{BOOL, TRUE, FALSE, DWORD, LPVOID, LPDWORD}; use winapi::um::winsock2::{ self, closesocket, linger, setsockopt, getsockopt, getsockname, PF_INET, PF_INET6, SOCKET, SOCKET_ERROR, - SOCK_STREAM, SOL_SOCKET, SO_LINGER, SO_REUSEADDR, + SOCK_STREAM, SOL_SOCKET, SO_LINGER, SO_REUSEADDR, SO_RCVBUF, SO_SNDBUF, SO_KEEPALIVE, WSAIoctl, LPWSAOVERLAPPED, }; use crate::sys::windows::net::{init, new_socket, socket_addr}; +use crate::net::TcpKeepalive; pub(crate) type TcpSocket = SOCKET; @@ -34,7 +38,7 @@ pub(crate) fn bind(socket: TcpSocket, addr: SocketAddr) -> io::Result<()> { let (raw_addr, raw_addr_length) = socket_addr(&addr); syscall!( - bind(socket, raw_addr, raw_addr_length), + bind(socket, raw_addr.as_ptr(), raw_addr_length), PartialEq::eq, SOCKET_ERROR )?; @@ -47,7 +51,7 @@ pub(crate) fn connect(socket: TcpSocket, addr: SocketAddr) -> io::Result io::Result { } pub(crate) fn get_localaddr(socket: TcpSocket) -> io::Result { - let mut addr: SOCKADDR_STORAGE = unsafe { std::mem::zeroed() }; - let mut length = std::mem::size_of_val(&addr) as c_int; + let mut storage: SOCKADDR_STORAGE = unsafe { std::mem::zeroed() }; + let mut length = std::mem::size_of_val(&storage) as c_int; match unsafe { getsockname( socket, - &mut addr as *mut _ as *mut _, + &mut storage as *mut _ as *mut _, &mut length ) } { SOCKET_ERROR => Err(io::Error::last_os_error()), _ => { - let storage: *const SOCKADDR_STORAGE = (&addr) as *const _; - if addr.ss_family as c_int == AF_INET { - let sock_addr : SocketAddrV4 = unsafe { *(storage as *const SOCKADDR_IN as *const _) }; - Ok(sock_addr.into()) + if storage.ss_family as c_int == AF_INET { + // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in. + let addr: &SOCKADDR_IN = unsafe { &*(&storage as *const _ as *const SOCKADDR_IN) }; + let ip_bytes = unsafe { addr.sin_addr.S_un.S_un_b() }; + let ip = Ipv4Addr::from([ip_bytes.s_b1, ip_bytes.s_b2, ip_bytes.s_b3, ip_bytes.s_b4]); + let port = u16::from_be(addr.sin_port); + Ok(SocketAddr::V4(SocketAddrV4::new(ip, port))) + } else if storage.ss_family as c_int == AF_INET6 { + // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6. + let addr: &SOCKADDR_IN6_LH = unsafe { &*(&storage as *const _ as *const SOCKADDR_IN6_LH) }; + let ip = Ipv6Addr::from(*unsafe { addr.sin6_addr.u.Byte() }); + let port = u16::from_be(addr.sin6_port); + let scope_id = unsafe { *addr.u.sin6_scope_id() }; + Ok(SocketAddr::V6(SocketAddrV6::new(ip, port, addr.sin6_flowinfo, scope_id))) } else { - let sock_addr : SocketAddrV6 = unsafe { *(storage as *const SOCKADDR_IN6_LH as *const _) }; - Ok(sock_addr.into()) + Err(std::io::ErrorKind::InvalidInput.into()) } }, } - - } pub(crate) fn set_linger(socket: TcpSocket, dur: Option) -> io::Result<()> { @@ -149,6 +160,164 @@ pub(crate) fn set_linger(socket: TcpSocket, dur: Option) -> io::Result } } +pub(crate) fn get_linger(socket: TcpSocket) -> io::Result> { + let mut val: linger = unsafe { std::mem::zeroed() }; + let mut len = size_of::() as c_int; + + match unsafe { getsockopt( + socket, + SOL_SOCKET, + SO_LINGER, + &mut val as *mut _ as *mut _, + &mut len, + ) } { + SOCKET_ERROR => Err(io::Error::last_os_error()), + _ => { + if val.l_onoff == 0 { + Ok(None) + } else { + Ok(Some(Duration::from_secs(val.l_linger as u64))) + } + }, + } +} + + +pub(crate) fn set_recv_buffer_size(socket: TcpSocket, size: u32) -> io::Result<()> { + let size = size.try_into().ok().unwrap_or_else(i32::max_value); + match unsafe { setsockopt( + socket, + SOL_SOCKET, + SO_RCVBUF, + &size as *const _ as *const c_char, + size_of::() as c_int + ) } { + SOCKET_ERROR => Err(io::Error::last_os_error()), + _ => Ok(()), + } +} + +pub(crate) fn get_recv_buffer_size(socket: TcpSocket) -> io::Result { + let mut optval: c_int = 0; + let mut optlen = size_of::() as c_int; + match unsafe { getsockopt( + socket, + SOL_SOCKET, + SO_RCVBUF, + &mut optval as *mut _ as *mut _, + &mut optlen as *mut _, + ) } { + SOCKET_ERROR => Err(io::Error::last_os_error()), + _ => Ok(optval as u32), + } +} + +pub(crate) fn set_send_buffer_size(socket: TcpSocket, size: u32) -> io::Result<()> { + let size = size.try_into().ok().unwrap_or_else(i32::max_value); + match unsafe { setsockopt( + socket, + SOL_SOCKET, + SO_SNDBUF, + &size as *const _ as *const c_char, + size_of::() as c_int + ) } { + SOCKET_ERROR => Err(io::Error::last_os_error()), + _ => Ok(()), + } +} + +pub(crate) fn get_send_buffer_size(socket: TcpSocket) -> io::Result { + let mut optval: c_int = 0; + let mut optlen = size_of::() as c_int; + match unsafe { getsockopt( + socket, + SOL_SOCKET, + SO_SNDBUF, + &mut optval as *mut _ as *mut _, + &mut optlen as *mut _, + ) } { + SOCKET_ERROR => Err(io::Error::last_os_error()), + _ => Ok(optval as u32), + } +} + +pub(crate) fn set_keepalive(socket: TcpSocket, keepalive: bool) -> io::Result<()> { + let val: BOOL = if keepalive { TRUE } else { FALSE }; + match unsafe { setsockopt( + socket, + SOL_SOCKET, + SO_KEEPALIVE, + &val as *const _ as *const c_char, + size_of::() as c_int + ) } { + SOCKET_ERROR => Err(io::Error::last_os_error()), + _ => Ok(()), + } +} + +pub(crate) fn get_keepalive(socket: TcpSocket) -> io::Result { + let mut optval: c_char = 0; + let mut optlen = size_of::() as c_int; + + match unsafe { getsockopt( + socket, + SOL_SOCKET, + SO_KEEPALIVE, + &mut optval as *mut _ as *mut _, + &mut optlen, + ) } { + SOCKET_ERROR => Err(io::Error::last_os_error()), + _ => Ok(optval != FALSE as c_char), + } +} + +pub(crate) fn set_keepalive_params(socket: TcpSocket, keepalive: TcpKeepalive) -> io::Result<()> { + /// Windows configures keepalive time/interval in a u32 of milliseconds. + fn dur_to_ulong_ms(dur: Duration) -> c_ulong { + dur.as_millis().try_into().ok().unwrap_or_else(u32::max_value) + } + + // If any of the fields on the `tcp_keepalive` struct were not provided by + // the user, just leaving them zero will clobber any existing value. + // Unfortunately, we can't access the current value, so we will use the + // defaults if a value for the time or interval was not not provided. + let time = keepalive.time.unwrap_or_else(|| { + // The default value is two hours, as per + // https://docs.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals + let two_hours = 2 * 60 * 60; + Duration::from_secs(two_hours) + }); + + let interval = keepalive.interval.unwrap_or_else(|| { + // The default value is one second, as per + // https://docs.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals + Duration::from_secs(1) + }); + + let mut keepalive = mstcpip::tcp_keepalive { + // Enable keepalive + onoff: 1, + keepalivetime: dur_to_ulong_ms(time), + keepaliveinterval: dur_to_ulong_ms(interval), + }; + + let mut out = 0; + match unsafe { WSAIoctl( + socket, + mstcpip::SIO_KEEPALIVE_VALS, + &mut keepalive as *mut _ as LPVOID, + size_of::() as DWORD, + ptr::null_mut() as LPVOID, + 0 as DWORD, + &mut out as *mut _ as LPDWORD, + 0 as LPWSAOVERLAPPED, + None, + ) } { + 0 => Ok(()), + _ => Err(io::Error::last_os_error()) + } +} + pub(crate) fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> { // The non-blocking state of `listener` is inherited. See // https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept#remarks. diff --git a/src/sys/windows/udp.rs b/src/sys/windows/udp.rs index 667c775..ba2aeac 100644 --- a/src/sys/windows/udp.rs +++ b/src/sys/windows/udp.rs @@ -12,7 +12,7 @@ pub fn bind(addr: SocketAddr) -> io::Result { new_ip_socket(addr, SOCK_DGRAM).and_then(|socket| { let (raw_addr, raw_addr_length) = socket_addr(&addr); syscall!( - win_bind(socket, raw_addr, raw_addr_length,), + win_bind(socket, raw_addr.as_ptr(), raw_addr_length,), PartialEq::eq, SOCKET_ERROR ) diff --git a/src/token.rs b/src/token.rs index 3773949..d8a1fd1 100644 --- a/src/token.rs +++ b/src/token.rs @@ -17,7 +17,8 @@ /// /// [`slab`]: https://crates.io/crates/slab /// -/// ``` +#[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Interest, Poll, Token}; diff --git a/src/waker.rs b/src/waker.rs index b8e4496..bc73029 100644 --- a/src/waker.rs +++ b/src/waker.rs @@ -34,7 +34,8 @@ use std::io; /// /// Wake a [`Poll`] instance from another thread. /// -/// ``` +#[cfg_attr(feature = "os-poll", doc = "```")] +#[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// # fn main() -> Result<(), Box> { /// use std::thread; /// use std::time::Duration; -- cgit v1.2.3