Skip to content
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

net: Provide a raw NamedPipe implementation and builders #3760

Merged
merged 59 commits into from Jun 15, 2021
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
fd4f217
net: Provide a raw NamedPipe implementation and builders
udoprog May 7, 2021
11b486f
rustfmt
udoprog May 7, 2021
006f364
only test named pipes on windows
udoprog May 7, 2021
036a9a3
examples can't use path dependencies
udoprog May 7, 2021
d04700a
Merge remote-tracking branch 'upstream/master' into more-low-level-na…
udoprog May 8, 2021
97a8942
re-enable examples
udoprog May 8, 2021
aa950e7
add access controls for client and info fn
udoprog May 8, 2021
82a74be
new examples are only support on windows
udoprog May 8, 2021
8f2c1e9
add local asyncify for tokio::net
udoprog May 8, 2021
ff4c6b3
rustfmt
udoprog May 8, 2021
89b05a9
more documentation, tests, idiomatic listen and connect
udoprog May 8, 2021
7c07f79
fix example
udoprog May 8, 2021
ef03f09
enable windows-specific docs to build on non-windows
udoprog May 8, 2021
2acf90b
Revert "enable windows-specific docs to build on non-windows"
udoprog May 9, 2021
b5d5d85
make sure cross platform documentation for named pipes work
udoprog May 10, 2021
b25019b
fix private doc link
udoprog May 10, 2021
edf4fa5
switch to options style building
udoprog May 10, 2021
8fb3c7f
add peeking functionality
udoprog May 10, 2021
491f70b
fix non-windows build failure
udoprog May 10, 2021
06147b5
fix link
udoprog May 10, 2021
a2d300f
mark more things as #[non_exhaustive]
udoprog May 12, 2021
f349c27
use ReadBuf in NamedPipe::peek
udoprog May 12, 2021
ec66427
make wait_named_pipe non-async
udoprog May 12, 2021
435a84f
fix rustdoc
udoprog May 12, 2021
08db16b
first batch of review fixes
udoprog May 14, 2021
a8a207d
rip wait_named_pipe
udoprog May 14, 2021
591de4e
remove dead links
udoprog May 17, 2021
88b6a9f
remove panicky FromRawHandle impl
udoprog May 17, 2021
056c5de
make from_raw_handle pub
udoprog May 17, 2021
0d6af11
adjust documentation
udoprog May 19, 2021
18505ff
avoid additional allocations in encode_addr
udoprog May 19, 2021
7586e38
clamp buffers
udoprog May 19, 2021
6099b0c
remove shutdown logic in doctest
udoprog May 19, 2021
477a569
allow setting security_qos_flags and ensure SECURITY_SQOS_PRESENT is set
udoprog May 19, 2021
50ff91c
adjust safety documentation to mention it can be null
udoprog May 19, 2021
e7b1010
document the behavior of max_instances
udoprog May 19, 2021
bb28d50
add missing constant in doc building
udoprog May 19, 2021
da2920b
Update tokio/src/net/windows/named_pipe.rs
udoprog May 19, 2021
ed598ca
fix some broken and weird links
udoprog May 19, 2021
e4dd1a0
use winapi instead of redefining a const
udoprog May 19, 2021
5753f3c
more missing doc items
udoprog May 19, 2021
31b8584
refactor weird links
udoprog May 19, 2021
2a6acd9
use *mut c_void for opaque pointers
udoprog May 20, 2021
e355dca
rustfmt again
udoprog May 20, 2021
eb863cb
create_with_security_attributes to *_raw and improved docs
udoprog May 22, 2021
3f272d0
rename named_pipe builders and remove re-export
udoprog Jun 2, 2021
f5c822c
rename ClientOptions::create to ClientOptions::open
udoprog Jun 2, 2021
c8761a3
doc nits
udoprog Jun 2, 2021
93b7dd0
enabled I/O
udoprog Jun 2, 2021
7d1d277
improve docs and lie less
udoprog Jun 2, 2021
93b8ff1
no aliasing of traits to _
udoprog Jun 2, 2021
ebe98dd
Update tokio/src/net/windows/named_pipe.rs
udoprog Jun 2, 2021
47c951e
remove comment and just connect
udoprog Jun 2, 2021
fb9ed13
separate doc tests for errors and examples
udoprog Jun 2, 2021
890d81d
separate doctess for errors and examples
udoprog Jun 2, 2021
19248e4
try_from_raw_handle to from_raw_handle
udoprog Jun 7, 2021
dc694fd
vec! instead of array
udoprog Jun 7, 2021
1f85125
remove peek-related fns
udoprog Jun 7, 2021
960522e
Separate NamedPipe{Client,Server}
udoprog Jun 9, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/Cargo.toml
Expand Up @@ -22,7 +22,10 @@ serde_json = "1.0"
httparse = "1.0"
time = "0.1"
once_cell = "1.5.2"
rand = "0.8.3"

[target.'cfg(windows)'.dev-dependencies.winapi]
version = "0.3.8"

[[example]]
name = "chat"
Expand Down Expand Up @@ -76,3 +79,15 @@ path = "custom-executor.rs"
[[example]]
name = "custom-executor-tokio-context"
path = "custom-executor-tokio-context.rs"

[[example]]
name = "named-pipe"
path = "named-pipe.rs"

[[example]]
name = "named-pipe-multi-client"
path = "named-pipe-multi-client.rs"

[[example]]
name = "named-pipe-peek"
path = "named-pipe-peek.rs"
98 changes: 98 additions & 0 deletions examples/named-pipe-multi-client.rs
@@ -0,0 +1,98 @@
use std::io;

#[cfg(windows)]
async fn windows_main() -> io::Result<()> {
use std::time::Duration;
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
use tokio::time;
use winapi::shared::winerror;

const PIPE_NAME: &str = r"\\.\pipe\named-pipe-multi-client";
const N: usize = 10;

// The first server needs to be constructed early so that clients can
// be correctly connected. Otherwise a waiting client will error.
//
// Here we also make use of `first_pipe_instance`, which will ensure
// that there are no other servers up and running already.
let mut server = ServerOptions::new()
.first_pipe_instance(true)
.create(PIPE_NAME)?;

let server = tokio::spawn(async move {
// Artificial workload.
time::sleep(Duration::from_secs(1)).await;

for _ in 0..N {
// Wait for client to connect.
server.connect().await?;
let mut inner = server;

// Construct the next server to be connected before sending the one
// we already have of onto a task. This ensures that the server
// isn't closed (after it's done in the task) before a new one is
// available. Otherwise the client might error with
// `io::ErrorKind::NotFound`.
server = ServerOptions::new().create(PIPE_NAME)?;

let _ = tokio::spawn(async move {
let mut buf = [0u8; 4];
inner.read_exact(&mut buf).await?;
inner.write_all(b"pong").await?;
Ok::<_, io::Error>(())
});
}

Ok::<_, io::Error>(())
});

let mut clients = Vec::new();

for _ in 0..N {
clients.push(tokio::spawn(async move {
// This showcases a generic connect loop.
//
// We immediately try to create a client, if it's not found or
// the pipe is busy we use the specialized wait function on the
// client builder.
let mut client = loop {
match ClientOptions::new().open(PIPE_NAME) {
Ok(client) => break client,
Err(e) if e.raw_os_error() == Some(winerror::ERROR_PIPE_BUSY as i32) => (),
Err(e) => return Err(e),
}

time::sleep(Duration::from_millis(5)).await;
};

let mut buf = [0u8; 4];
udoprog marked this conversation as resolved.
Show resolved Hide resolved
client.write_all(b"ping").await?;
client.read_exact(&mut buf).await?;
Ok::<_, io::Error>(buf)
}));
}

for client in clients {
let result = client.await?;
assert_eq!(&result?[..], b"pong");
}

server.await??;
Ok(())
}

#[tokio::main]
async fn main() -> io::Result<()> {
#[cfg(windows)]
{
windows_main().await?;
}

#[cfg(not(windows))]
{
println!("Named pipes are only supported on Windows!");
}

Ok(())
}
66 changes: 66 additions & 0 deletions examples/named-pipe-peek.rs
@@ -0,0 +1,66 @@
use std::io;

#[cfg(windows)]
async fn windows_main() -> io::Result<()> {
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};

const PIPE_NAME: &str = r"\\.\pipe\tokio-named-pipe-peek-consumed";
const N: usize = 1000;

let mut server = ServerOptions::new().create(PIPE_NAME)?;
let mut client = ClientOptions::new().open(PIPE_NAME)?;
server.connect().await?;

let client = tokio::spawn(async move {
for _ in 0..N {
client.write_all(b"ping").await?;
}

let mut buf = [0u8; 4];
client.read_exact(&mut buf).await?;

Ok::<_, io::Error>(buf)
});

let mut buf = [0u8; 4];
let mut available = 0;

for n in 0..N {
if available < 4 {
println!("read_exact: (n: {}, available: {})", n, available);
server.read_exact(&mut buf).await?;
assert_eq!(&buf[..], b"ping");

let info = server.peek(None)?;
available = info.total_bytes_available;
continue;
}

println!("read_exact: (n: {}, available: {})", n, available);
server.read_exact(&mut buf).await?;
available -= buf.len();
assert_eq!(&buf[..], b"ping");
}

server.write_all(b"pong").await?;

let buf = client.await??;
assert_eq!(&buf[..], b"pong");
Ok(())
}

#[tokio::main]
async fn main() -> io::Result<()> {
#[cfg(windows)]
{
windows_main().await?;
}

#[cfg(not(windows))]
{
println!("Named pipes are only supported on Windows!");
}

Ok(())
}
60 changes: 60 additions & 0 deletions examples/named-pipe.rs
@@ -0,0 +1,60 @@
use std::io;

#[cfg(windows)]
async fn windows_main() -> io::Result<()> {
use tokio::io::AsyncWriteExt as _;
use tokio::io::{AsyncBufReadExt as _, BufReader};
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};

const PIPE_NAME: &str = r"\\.\pipe\named-pipe-single-client";

let server = ServerOptions::new().create(PIPE_NAME)?;

let server = tokio::spawn(async move {
// Note: we wait for a client to connect.
server.connect().await?;

let mut server = BufReader::new(server);

let mut buf = String::new();
server.read_line(&mut buf).await?;
server.write_all(b"pong\n").await?;
Ok::<_, io::Error>(buf)
});

let client = tokio::spawn(async move {
// There's no need to use a connect loop here, since we know that the
// server is already up - `open` was called before spawning any of the
// tasks.
let client = ClientOptions::new().open(PIPE_NAME)?;

let mut client = BufReader::new(client);

let mut buf = String::new();
client.write_all(b"ping\n").await?;
client.read_line(&mut buf).await?;
Ok::<_, io::Error>(buf)
});

let (server, client) = tokio::try_join!(server, client)?;

assert_eq!(server?, "ping\n");
assert_eq!(client?, "pong\n");

Ok(())
}

#[tokio::main]
async fn main() -> io::Result<()> {
#[cfg(windows)]
{
windows_main().await?;
}

#[cfg(not(windows))]
{
println!("Named pipes are only supported on Windows!");
}

Ok(())
}
4 changes: 4 additions & 0 deletions tokio/Cargo.toml
Expand Up @@ -54,6 +54,7 @@ net = [
"mio/tcp",
"mio/udp",
"mio/uds",
"winapi/namedpipeapi",
]
process = [
"bytes",
Expand Down Expand Up @@ -115,6 +116,9 @@ version = "0.3.8"
default-features = false
optional = true

[target.'cfg(windows)'.dev-dependencies.ntapi]
version = "0.3.6"

[dev-dependencies]
tokio-test = { version = "0.4.0", path = "../tokio-test" }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
Expand Down
23 changes: 23 additions & 0 deletions tokio/src/doc/mod.rs
@@ -0,0 +1,23 @@
//! Types which are documented locally in the Tokio crate, but does not actually
//! live here.
//!
//! **Note** this module is only visible on docs.rs, you cannot use it directly
//! in your own code.

/// The name of a type which is not defined here.
///
/// This is typically used as an alias for another type, like so:
///
/// ```rust,ignore
/// /// See [some::other::location](https://example.com).
/// type DEFINED_ELSEWHERE = crate::doc::NotDefinedHere;
/// ```
///
/// This type is uninhabitable like the [`never` type] to ensure that no one
/// will ever accidentally use it.
///
/// [`never` type]: https://doc.rust-lang.org/std/primitive.never.html
pub enum NotDefinedHere {}

pub mod os;
pub mod winapi;
26 changes: 26 additions & 0 deletions tokio/src/doc/os.rs
@@ -0,0 +1,26 @@
//! See [std::os](https://doc.rust-lang.org/std/os/index.html).

/// Platform-specific extensions to `std` for Windows.
///
/// See [std::os::windows](https://doc.rust-lang.org/std/os/windows/index.html).
pub mod windows {
/// Windows-specific extensions to general I/O primitives.
///
/// See [std::os::windows::io](https://doc.rust-lang.org/std/os/windows/io/index.html).
pub mod io {
/// See [std::os::windows::io::RawHandle](https://doc.rust-lang.org/std/os/windows/io/type.RawHandle.html)
pub type RawHandle = crate::doc::NotDefinedHere;

/// See [std::os::windows::io::AsRawHandle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html)
pub trait AsRawHandle {
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html#tymethod.as_raw_handle)
fn as_raw_handle(&self) -> RawHandle;
}

/// See [std::os::windows::io::FromRawHandle](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html)
pub trait FromRawHandle {
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html#tymethod.from_raw_handle)
unsafe fn from_raw_handle(handle: RawHandle) -> Self;
}
}
}
66 changes: 66 additions & 0 deletions tokio/src/doc/winapi.rs
@@ -0,0 +1,66 @@
//! See [winapi].
//!
//! [winapi]: https://docs.rs/winapi

/// See [winapi::shared](https://docs.rs/winapi/*/winapi/shared/index.html).
pub mod shared {
/// See [winapi::shared::winerror](https://docs.rs/winapi/*/winapi/shared/winerror/index.html).
#[allow(non_camel_case_types)]
pub mod winerror {
/// See [winapi::shared::winerror::ERROR_ACCESS_DENIED][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_ACCESS_DENIED.html
pub type ERROR_ACCESS_DENIED = crate::doc::NotDefinedHere;

/// See [winapi::shared::winerror::ERROR_PIPE_BUSY][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_PIPE_BUSY.html
pub type ERROR_PIPE_BUSY = crate::doc::NotDefinedHere;

/// See [winapi::shared::winerror::ERROR_MORE_DATA][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_MORE_DATA.html
pub type ERROR_MORE_DATA = crate::doc::NotDefinedHere;
}
}

/// See [winapi::um](https://docs.rs/winapi/*/winapi/um/index.html).
pub mod um {
/// See [winapi::um::winbase](https://docs.rs/winapi/*/winapi/um/winbase/index.html).
#[allow(non_camel_case_types)]
pub mod winbase {
/// See [winapi::um::winbase::PIPE_TYPE_MESSAGE][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_TYPE_MESSAGE.html
pub type PIPE_TYPE_MESSAGE = crate::doc::NotDefinedHere;

/// See [winapi::um::winbase::PIPE_TYPE_BYTE][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_TYPE_BYTE.html
pub type PIPE_TYPE_BYTE = crate::doc::NotDefinedHere;

/// See [winapi::um::winbase::PIPE_CLIENT_END][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_CLIENT_END.html
pub type PIPE_CLIENT_END = crate::doc::NotDefinedHere;

/// See [winapi::um::winbase::PIPE_SERVER_END][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_SERVER_END.html
pub type PIPE_SERVER_END = crate::doc::NotDefinedHere;

/// See [winapi::um::winbase::SECURITY_IDENTIFICATION][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.SECURITY_IDENTIFICATION.html
pub type SECURITY_IDENTIFICATION = crate::doc::NotDefinedHere;
}

/// See [winapi::um::minwinbase](https://docs.rs/winapi/*/winapi/um/minwinbase/index.html).
#[allow(non_camel_case_types)]
pub mod minwinbase {
/// See [winapi::um::minwinbase::SECURITY_ATTRIBUTES][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/minwinbase/constant.SECURITY_ATTRIBUTES.html
pub type SECURITY_ATTRIBUTES = crate::doc::NotDefinedHere;
}
}