Add unix socket support to the standard library

This commit is contained in:
Steven Fackler 2016-03-16 20:50:45 -07:00
parent 173676efdc
commit c0d989ed6b
7 changed files with 1141 additions and 55 deletions

View File

@ -1457,12 +1457,12 @@ mod tests {
use prelude::v1::*;
use io::prelude::*;
use env;
use fs::{self, File, OpenOptions};
use io::{ErrorKind, SeekFrom};
use path::{Path, PathBuf};
use rand::{self, StdRng, Rng};
use path::Path;
use rand::{StdRng, Rng};
use str;
use sys_common::io::test::{TempDir, tmpdir};
#[cfg(windows)]
use os::windows::fs::{symlink_dir, symlink_file};
@ -1490,37 +1490,6 @@ mod tests {
}
) }
pub struct TempDir(PathBuf);
impl TempDir {
fn join(&self, path: &str) -> PathBuf {
let TempDir(ref p) = *self;
p.join(path)
}
fn path<'a>(&'a self) -> &'a Path {
let TempDir(ref p) = *self;
p
}
}
impl Drop for TempDir {
fn drop(&mut self) {
// Gee, seeing how we're testing the fs module I sure hope that we
// at least implement this correctly!
let TempDir(ref p) = *self;
check!(fs::remove_dir_all(p));
}
}
pub fn tmpdir() -> TempDir {
let p = env::temp_dir();
let mut r = rand::thread_rng();
let ret = p.join(&format!("rust-{}", r.next_u32()));
check!(fs::create_dir(&ret));
TempDir(ret)
}
// Several test fail on windows if the user does not have permission to
// create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of
// disabling these test on Windows, use this function to test whether we

View File

@ -51,6 +51,46 @@ pub unsafe fn read_to_end_uninitialized(r: &mut Read, buf: &mut Vec<u8>) -> io::
}
}
#[cfg(test)]
pub mod test {
use prelude::v1::*;
use path::{Path, PathBuf};
use env;
use rand::{self, Rng};
use fs;
pub struct TempDir(PathBuf);
impl TempDir {
pub fn join(&self, path: &str) -> PathBuf {
let TempDir(ref p) = *self;
p.join(path)
}
pub fn path<'a>(&'a self) -> &'a Path {
let TempDir(ref p) = *self;
p
}
}
impl Drop for TempDir {
fn drop(&mut self) {
// Gee, seeing how we're testing the fs module I sure hope that we
// at least implement this correctly!
let TempDir(ref p) = *self;
fs::remove_dir_all(p).unwrap();
}
}
pub fn tmpdir() -> TempDir {
let p = env::temp_dir();
let mut r = rand::thread_rng();
let ret = p.join(&format!("rust-{}", r.next_u32()));
fs::create_dir(&ret).unwrap();
TempDir(ret)
}
}
#[cfg(test)]
mod tests {
use prelude::v1::*;
@ -58,7 +98,6 @@ mod tests {
use super::*;
use io;
use io::{ErrorKind, Take, Repeat, repeat};
use test;
use slice::from_raw_parts;
struct ErrorRepeat {
@ -129,7 +168,7 @@ mod tests {
}
#[bench]
fn bench_uninitialized(b: &mut test::Bencher) {
fn bench_uninitialized(b: &mut ::test::Bencher) {
b.iter(|| {
let mut lr = repeat(1).take(10000000);
let mut vec = Vec::with_capacity(1024);

View File

@ -257,12 +257,7 @@ impl TcpStream {
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
let raw: c_int = try!(getsockopt(&self.inner, c::SOL_SOCKET, c::SO_ERROR));
if raw == 0 {
Ok(None)
} else {
Ok(Some(io::Error::from_raw_os_error(raw as i32)))
}
self.inner.take_error()
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
@ -367,12 +362,7 @@ impl TcpListener {
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
let raw: c_int = try!(getsockopt(&self.inner, c::SOL_SOCKET, c::SO_ERROR));
if raw == 0 {
Ok(None)
} else {
Ok(Some(io::Error::from_raw_os_error(raw as i32)))
}
self.inner.take_error()
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
@ -564,12 +554,7 @@ impl UdpSocket {
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
let raw: c_int = try!(getsockopt(&self.inner, c::SOL_SOCKET, c::SO_ERROR));
if raw == 0 {
Ok(None)
} else {
Ok(Some(io::Error::from_raw_os_error(raw as i32)))
}
self.inner.take_error()
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {

View File

@ -35,6 +35,7 @@ pub mod fs;
pub mod process;
pub mod raw;
pub mod thread;
pub mod net;
/// A prelude for conveniently writing platform-specific code.
///

File diff suppressed because it is too large Load Diff

View File

@ -57,13 +57,17 @@ impl Socket {
SocketAddr::V4(..) => libc::AF_INET,
SocketAddr::V6(..) => libc::AF_INET6,
};
Socket::new_raw(fam, ty)
}
pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
unsafe {
// On linux we first attempt to pass the SOCK_CLOEXEC flag to
// atomically create the socket and set it as CLOEXEC. Support for
// this option, however, was added in 2.6.27, and we still support
// 2.6.18 as a kernel, so if the returned error is EINVAL we
// fallthrough to the fallback.
if cfg!(target_os = "linux") {
if cfg!(linux) {
match cvt(libc::socket(fam, ty | SOCK_CLOEXEC, 0)) {
Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
@ -78,6 +82,30 @@ impl Socket {
}
}
pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> {
unsafe {
let mut fds = [0, 0];
// Like above, see if we can set cloexec atomically
if cfg!(linux) {
match cvt(libc::socketpair(fam, ty | SOCK_CLOEXEC, 0, fds.as_mut_ptr())) {
Ok(_) => {
return Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))));
}
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {},
Err(e) => return Err(e),
}
}
try!(cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr())));
let a = FileDesc::new(fds[0]);
a.set_cloexec();
let b = FileDesc::new(fds[1]);
b.set_cloexec();
Ok((Socket(a), Socket(b)))
}
}
pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t)
-> io::Result<Socket> {
// Unfortunately the only known way right now to accept a socket and
@ -120,6 +148,10 @@ impl Socket {
self.0.read_to_end(buf)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
let timeout = match dur {
Some(dur) => {
@ -186,6 +218,15 @@ impl Socket {
let mut nonblocking = nonblocking as libc::c_ulong;
cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(|_| ())
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
let raw: c_int = try!(getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR));
if raw == 0 {
Ok(None)
} else {
Ok(Some(io::Error::from_raw_os_error(raw as i32)))
}
}
}
impl AsInner<c_int> for Socket {

View File

@ -214,6 +214,15 @@ impl Socket {
let raw: c::BYTE = try!(net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY));
Ok(raw != 0)
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
let raw: c_int = try!(net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR));
if raw == 0 {
Ok(None)
} else {
Ok(Some(io::Error::from_raw_os_error(raw as i32)))
}
}
}
#[unstable(reason = "not public", issue = "0", feature = "fd_read")]