| use crate::io::prelude::*; | |
| use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut}; | |
| use crate::mem::MaybeUninit; | |
| use crate::net::test::{next_test_ip4, next_test_ip6}; | |
| use crate::net::*; | |
| use crate::sync::mpsc::channel; | |
| use crate::time::{Duration, Instant}; | |
| use crate::{fmt, thread}; | |
| fn each_ip(f: &mut dyn FnMut(SocketAddr)) { | |
| f(next_test_ip4()); | |
| f(next_test_ip6()); | |
| } | |
| macro_rules! t { | |
| ($e:expr) => { | |
| match $e { | |
| Ok(t) => t, | |
| Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), | |
| } | |
| }; | |
| } | |
| #[test] | |
| fn bind_error() { | |
| match TcpListener::bind("1.1.1.1:9999") { | |
| Ok(..) => panic!(), | |
| Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable), | |
| } | |
| } | |
| #[test] | |
| fn connect_error() { | |
| match TcpStream::connect("0.0.0.0:1") { | |
| Ok(..) => panic!(), | |
| Err(e) => assert!( | |
| e.kind() == ErrorKind::ConnectionRefused | |
| || e.kind() == ErrorKind::InvalidInput | |
| || e.kind() == ErrorKind::AddrInUse | |
| || e.kind() == ErrorKind::AddrNotAvailable | |
| || e.kind() == ErrorKind::NetworkUnreachable, | |
| "bad error: {} {:?}", | |
| e, | |
| e.kind() | |
| ), | |
| } | |
| } | |
| #[test] | |
| #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 | |
| fn connect_timeout_error() { | |
| let socket_addr = next_test_ip4(); | |
| let result = TcpStream::connect_timeout(&socket_addr, Duration::MAX); | |
| assert!(!matches!(result, Err(e) if e.kind() == ErrorKind::TimedOut)); | |
| let _listener = TcpListener::bind(&socket_addr).unwrap(); | |
| assert!(TcpStream::connect_timeout(&socket_addr, Duration::MAX).is_ok()); | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn listen_localhost() { | |
| let socket_addr = next_test_ip4(); | |
| let listener = t!(TcpListener::bind(&socket_addr)); | |
| let _t = thread::spawn(move || { | |
| let mut stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); | |
| t!(stream.write(&[144])); | |
| }); | |
| let mut stream = t!(listener.accept()).0; | |
| let mut buf = [0]; | |
| t!(stream.read(&mut buf)); | |
| assert!(buf[0] == 144); | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn connect_loopback() { | |
| each_ip(&mut |addr| { | |
| let acceptor = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| let host = match addr { | |
| SocketAddr::V4(..) => "127.0.0.1", | |
| SocketAddr::V6(..) => "::1", | |
| }; | |
| let mut stream = t!(TcpStream::connect(&(host, addr.port()))); | |
| t!(stream.write(&[66])); | |
| }); | |
| let mut stream = t!(acceptor.accept()).0; | |
| let mut buf = [0]; | |
| t!(stream.read(&mut buf)); | |
| assert!(buf[0] == 66); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn smoke_test() { | |
| each_ip(&mut |addr| { | |
| let acceptor = t!(TcpListener::bind(&addr)); | |
| let (tx, rx) = channel(); | |
| let _t = thread::spawn(move || { | |
| let mut stream = t!(TcpStream::connect(&addr)); | |
| t!(stream.write(&[99])); | |
| tx.send(t!(stream.local_addr())).unwrap(); | |
| }); | |
| let (mut stream, addr) = t!(acceptor.accept()); | |
| let mut buf = [0]; | |
| t!(stream.read(&mut buf)); | |
| assert!(buf[0] == 99); | |
| assert_eq!(addr, t!(rx.recv())); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn read_eof() { | |
| each_ip(&mut |addr| { | |
| let acceptor = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| let _stream = t!(TcpStream::connect(&addr)); | |
| // Close | |
| }); | |
| let mut stream = t!(acceptor.accept()).0; | |
| let mut buf = [0]; | |
| let nread = t!(stream.read(&mut buf)); | |
| assert_eq!(nread, 0); | |
| let nread = t!(stream.read(&mut buf)); | |
| assert_eq!(nread, 0); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn write_close() { | |
| each_ip(&mut |addr| { | |
| let acceptor = t!(TcpListener::bind(&addr)); | |
| let (tx, rx) = channel(); | |
| let _t = thread::spawn(move || { | |
| drop(t!(TcpStream::connect(&addr))); | |
| tx.send(()).unwrap(); | |
| }); | |
| let mut stream = t!(acceptor.accept()).0; | |
| rx.recv().unwrap(); | |
| let buf = [0]; | |
| match stream.write(&buf) { | |
| Ok(..) => {} | |
| Err(e) => { | |
| assert!( | |
| e.kind() == ErrorKind::ConnectionReset | |
| || e.kind() == ErrorKind::BrokenPipe | |
| || e.kind() == ErrorKind::ConnectionAborted, | |
| "unknown error: {e}" | |
| ); | |
| } | |
| } | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn multiple_connect_serial() { | |
| each_ip(&mut |addr| { | |
| let max = 10; | |
| let acceptor = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| for _ in 0..max { | |
| let mut stream = t!(TcpStream::connect(&addr)); | |
| t!(stream.write(&[99])); | |
| } | |
| }); | |
| for stream in acceptor.incoming().take(max) { | |
| let mut stream = t!(stream); | |
| let mut buf = [0]; | |
| t!(stream.read(&mut buf)); | |
| assert_eq!(buf[0], 99); | |
| } | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn multiple_connect_interleaved_greedy_schedule() { | |
| const MAX: usize = 10; | |
| each_ip(&mut |addr| { | |
| let acceptor = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| let acceptor = acceptor; | |
| for (i, stream) in acceptor.incoming().enumerate().take(MAX) { | |
| // Start another thread to handle the connection | |
| let _t = thread::spawn(move || { | |
| let mut stream = t!(stream); | |
| let mut buf = [0]; | |
| t!(stream.read(&mut buf)); | |
| assert!(buf[0] == i as u8); | |
| }); | |
| } | |
| }); | |
| connect(0, addr); | |
| }); | |
| fn connect(i: usize, addr: SocketAddr) { | |
| if i == MAX { | |
| return; | |
| } | |
| let t = thread::spawn(move || { | |
| let mut stream = t!(TcpStream::connect(&addr)); | |
| // Connect again before writing | |
| connect(i + 1, addr); | |
| t!(stream.write(&[i as u8])); | |
| }); | |
| t.join().ok().expect("thread panicked"); | |
| } | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn multiple_connect_interleaved_lazy_schedule() { | |
| const MAX: usize = 10; | |
| each_ip(&mut |addr| { | |
| let acceptor = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| for stream in acceptor.incoming().take(MAX) { | |
| // Start another thread to handle the connection | |
| let _t = thread::spawn(move || { | |
| let mut stream = t!(stream); | |
| let mut buf = [0]; | |
| t!(stream.read(&mut buf)); | |
| assert!(buf[0] == 99); | |
| }); | |
| } | |
| }); | |
| connect(0, addr); | |
| }); | |
| fn connect(i: usize, addr: SocketAddr) { | |
| if i == MAX { | |
| return; | |
| } | |
| let t = thread::spawn(move || { | |
| let mut stream = t!(TcpStream::connect(&addr)); | |
| connect(i + 1, addr); | |
| t!(stream.write(&[99])); | |
| }); | |
| t.join().ok().expect("thread panicked"); | |
| } | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn socket_and_peer_name() { | |
| each_ip(&mut |addr| { | |
| let listener = t!(TcpListener::bind(&addr)); | |
| let so_name = t!(listener.local_addr()); | |
| assert_eq!(addr, so_name); | |
| let _t = thread::spawn(move || { | |
| t!(listener.accept()); | |
| }); | |
| let stream = t!(TcpStream::connect(&addr)); | |
| assert_eq!(addr, t!(stream.peer_addr())); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn partial_read() { | |
| each_ip(&mut |addr| { | |
| let (tx, rx) = channel(); | |
| let srv = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| let mut cl = t!(srv.accept()).0; | |
| cl.write(&[10]).unwrap(); | |
| let mut b = [0]; | |
| t!(cl.read(&mut b)); | |
| tx.send(()).unwrap(); | |
| }); | |
| let mut c = t!(TcpStream::connect(&addr)); | |
| let mut b = [0; 10]; | |
| assert_eq!(c.read(&mut b).unwrap(), 1); | |
| t!(c.write(&[1])); | |
| rx.recv().unwrap(); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn read_buf() { | |
| each_ip(&mut |addr| { | |
| let srv = t!(TcpListener::bind(&addr)); | |
| let t = thread::spawn(move || { | |
| let mut s = t!(TcpStream::connect(&addr)); | |
| s.write_all(&[1, 2, 3, 4]).unwrap(); | |
| }); | |
| let mut s = t!(srv.accept()).0; | |
| let mut buf: [MaybeUninit<u8>; 128] = [MaybeUninit::uninit(); 128]; | |
| let mut buf = BorrowedBuf::from(buf.as_mut_slice()); | |
| t!(s.read_buf(buf.unfilled())); | |
| assert_eq!(buf.filled(), &[1, 2, 3, 4]); | |
| // TcpStream::read_buf should omit buffer initialization. | |
| assert_eq!(buf.init_len(), 4); | |
| t.join().ok().expect("thread panicked"); | |
| }) | |
| } | |
| #[test] | |
| fn read_vectored() { | |
| each_ip(&mut |addr| { | |
| let srv = t!(TcpListener::bind(&addr)); | |
| let mut s1 = t!(TcpStream::connect(&addr)); | |
| let mut s2 = t!(srv.accept()).0; | |
| let len = s1.write(&[10, 11, 12]).unwrap(); | |
| assert_eq!(len, 3); | |
| let mut a = []; | |
| let mut b = [0]; | |
| let mut c = [0; 3]; | |
| let len = t!(s2.read_vectored(&mut [ | |
| IoSliceMut::new(&mut a), | |
| IoSliceMut::new(&mut b), | |
| IoSliceMut::new(&mut c) | |
| ],)); | |
| assert!(len > 0); | |
| assert_eq!(b, [10]); | |
| // some implementations don't support readv, so we may only fill the first buffer | |
| assert!(len == 1 || c == [11, 12, 0]); | |
| }) | |
| } | |
| #[test] | |
| fn write_vectored() { | |
| each_ip(&mut |addr| { | |
| let srv = t!(TcpListener::bind(&addr)); | |
| let mut s1 = t!(TcpStream::connect(&addr)); | |
| let mut s2 = t!(srv.accept()).0; | |
| let a = []; | |
| let b = [10]; | |
| let c = [11, 12]; | |
| t!(s1.write_vectored(&[IoSlice::new(&a), IoSlice::new(&b), IoSlice::new(&c)])); | |
| let mut buf = [0; 4]; | |
| let len = t!(s2.read(&mut buf)); | |
| // some implementations don't support writev, so we may only write the first buffer | |
| if len == 1 { | |
| assert_eq!(buf, [10, 0, 0, 0]); | |
| } else { | |
| assert_eq!(len, 3); | |
| assert_eq!(buf, [10, 11, 12, 0]); | |
| } | |
| }) | |
| } | |
| #[test] | |
| fn double_bind() { | |
| each_ip(&mut |addr| { | |
| let listener1 = t!(TcpListener::bind(&addr)); | |
| match TcpListener::bind(&addr) { | |
| Ok(listener2) => panic!( | |
| "This system (perhaps due to options set by TcpListener::bind) \ | |
| permits double binding: {:?} and {:?}", | |
| listener1, listener2 | |
| ), | |
| Err(e) => { | |
| assert!( | |
| e.kind() == ErrorKind::ConnectionRefused | |
| || e.kind() == ErrorKind::Uncategorized | |
| || e.kind() == ErrorKind::AddrInUse, | |
| "unknown error: {} {:?}", | |
| e, | |
| e.kind() | |
| ); | |
| } | |
| } | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn tcp_clone_smoke() { | |
| each_ip(&mut |addr| { | |
| let acceptor = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| let mut s = t!(TcpStream::connect(&addr)); | |
| let mut buf = [0, 0]; | |
| assert_eq!(s.read(&mut buf).unwrap(), 1); | |
| assert_eq!(buf[0], 1); | |
| t!(s.write(&[2])); | |
| }); | |
| let mut s1 = t!(acceptor.accept()).0; | |
| let s2 = t!(s1.try_clone()); | |
| let (tx1, rx1) = channel(); | |
| let (tx2, rx2) = channel(); | |
| let _t = thread::spawn(move || { | |
| let mut s2 = s2; | |
| rx1.recv().unwrap(); | |
| t!(s2.write(&[1])); | |
| tx2.send(()).unwrap(); | |
| }); | |
| tx1.send(()).unwrap(); | |
| let mut buf = [0, 0]; | |
| assert_eq!(s1.read(&mut buf).unwrap(), 1); | |
| rx2.recv().unwrap(); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn tcp_clone_two_read() { | |
| each_ip(&mut |addr| { | |
| let acceptor = t!(TcpListener::bind(&addr)); | |
| let (tx1, rx) = channel(); | |
| let tx2 = tx1.clone(); | |
| let _t = thread::spawn(move || { | |
| let mut s = t!(TcpStream::connect(&addr)); | |
| t!(s.write(&[1])); | |
| rx.recv().unwrap(); | |
| t!(s.write(&[2])); | |
| rx.recv().unwrap(); | |
| }); | |
| let mut s1 = t!(acceptor.accept()).0; | |
| let s2 = t!(s1.try_clone()); | |
| let (done, rx) = channel(); | |
| let _t = thread::spawn(move || { | |
| let mut s2 = s2; | |
| let mut buf = [0, 0]; | |
| t!(s2.read(&mut buf)); | |
| tx2.send(()).unwrap(); | |
| done.send(()).unwrap(); | |
| }); | |
| let mut buf = [0, 0]; | |
| t!(s1.read(&mut buf)); | |
| tx1.send(()).unwrap(); | |
| rx.recv().unwrap(); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn tcp_clone_two_write() { | |
| each_ip(&mut |addr| { | |
| let acceptor = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| let mut s = t!(TcpStream::connect(&addr)); | |
| let mut buf = [0, 1]; | |
| t!(s.read(&mut buf)); | |
| t!(s.read(&mut buf)); | |
| }); | |
| let mut s1 = t!(acceptor.accept()).0; | |
| let s2 = t!(s1.try_clone()); | |
| let (done, rx) = channel(); | |
| let _t = thread::spawn(move || { | |
| let mut s2 = s2; | |
| t!(s2.write(&[1])); | |
| done.send(()).unwrap(); | |
| }); | |
| t!(s1.write(&[2])); | |
| rx.recv().unwrap(); | |
| }) | |
| } | |
| #[test] | |
| // FIXME: https://github.com/fortanix/rust-sgx/issues/110 | |
| #[cfg_attr(target_env = "sgx", ignore)] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn shutdown_smoke() { | |
| each_ip(&mut |addr| { | |
| let a = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| let mut c = t!(a.accept()).0; | |
| let mut b = [0]; | |
| assert_eq!(c.read(&mut b).unwrap(), 0); | |
| t!(c.write(&[1])); | |
| }); | |
| let mut s = t!(TcpStream::connect(&addr)); | |
| t!(s.shutdown(Shutdown::Write)); | |
| assert!(s.write(&[1]).is_err()); | |
| let mut b = [0, 0]; | |
| assert_eq!(t!(s.read(&mut b)), 1); | |
| assert_eq!(b[0], 1); | |
| }) | |
| } | |
| #[test] | |
| // FIXME: https://github.com/fortanix/rust-sgx/issues/110 | |
| #[cfg_attr(target_env = "sgx", ignore)] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn close_readwrite_smoke() { | |
| each_ip(&mut |addr| { | |
| let a = t!(TcpListener::bind(&addr)); | |
| let (tx, rx) = channel::<()>(); | |
| let _t = thread::spawn(move || { | |
| let _s = t!(a.accept()); | |
| let _ = rx.recv(); | |
| }); | |
| let mut b = [0]; | |
| let mut s = t!(TcpStream::connect(&addr)); | |
| let mut s2 = t!(s.try_clone()); | |
| // closing should prevent reads/writes | |
| t!(s.shutdown(Shutdown::Write)); | |
| assert!(s.write(&[0]).is_err()); | |
| t!(s.shutdown(Shutdown::Read)); | |
| assert_eq!(s.read(&mut b).unwrap(), 0); | |
| // closing should affect previous handles | |
| assert!(s2.write(&[0]).is_err()); | |
| assert_eq!(s2.read(&mut b).unwrap(), 0); | |
| // closing should affect new handles | |
| let mut s3 = t!(s.try_clone()); | |
| assert!(s3.write(&[0]).is_err()); | |
| assert_eq!(s3.read(&mut b).unwrap(), 0); | |
| // make sure these don't die | |
| let _ = s2.shutdown(Shutdown::Read); | |
| let _ = s2.shutdown(Shutdown::Write); | |
| let _ = s3.shutdown(Shutdown::Read); | |
| let _ = s3.shutdown(Shutdown::Write); | |
| drop(tx); | |
| }) | |
| } | |
| #[test] | |
| // FIXME: https://github.com/fortanix/rust-sgx/issues/110 | |
| #[cfg_attr(target_env = "sgx", ignore)] | |
| // On windows, shutdown will not wake up blocking I/O operations. | |
| #[cfg_attr(windows, ignore)] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn close_read_wakes_up() { | |
| each_ip(&mut |addr| { | |
| let listener = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| let (stream, _) = t!(listener.accept()); | |
| stream | |
| }); | |
| let mut stream = t!(TcpStream::connect(&addr)); | |
| let stream2 = t!(stream.try_clone()); | |
| let _t = thread::spawn(move || { | |
| let stream2 = stream2; | |
| // to make it more likely that `read` happens before `shutdown` | |
| thread::sleep(Duration::from_millis(1000)); | |
| // this should wake up the reader up | |
| t!(stream2.shutdown(Shutdown::Read)); | |
| }); | |
| // this `read` should get interrupted by `shutdown` | |
| assert_eq!(t!(stream.read(&mut [0])), 0); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn clone_while_reading() { | |
| each_ip(&mut |addr| { | |
| let accept = t!(TcpListener::bind(&addr)); | |
| // Enqueue a thread to write to a socket | |
| let (tx, rx) = channel(); | |
| let (txdone, rxdone) = channel(); | |
| let txdone2 = txdone.clone(); | |
| let _t = thread::spawn(move || { | |
| let mut tcp = t!(TcpStream::connect(&addr)); | |
| rx.recv().unwrap(); | |
| t!(tcp.write(&[0])); | |
| txdone2.send(()).unwrap(); | |
| }); | |
| // Spawn off a reading clone | |
| let tcp = t!(accept.accept()).0; | |
| let tcp2 = t!(tcp.try_clone()); | |
| let txdone3 = txdone.clone(); | |
| let _t = thread::spawn(move || { | |
| let mut tcp2 = tcp2; | |
| t!(tcp2.read(&mut [0])); | |
| txdone3.send(()).unwrap(); | |
| }); | |
| // Try to ensure that the reading clone is indeed reading | |
| for _ in 0..50 { | |
| thread::yield_now(); | |
| } | |
| // clone the handle again while it's reading, then let it finish the | |
| // read. | |
| let _ = t!(tcp.try_clone()); | |
| tx.send(()).unwrap(); | |
| rxdone.recv().unwrap(); | |
| rxdone.recv().unwrap(); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn clone_accept_smoke() { | |
| each_ip(&mut |addr| { | |
| let a = t!(TcpListener::bind(&addr)); | |
| let a2 = t!(a.try_clone()); | |
| let _t = thread::spawn(move || { | |
| let _ = TcpStream::connect(&addr); | |
| }); | |
| let _t = thread::spawn(move || { | |
| let _ = TcpStream::connect(&addr); | |
| }); | |
| t!(a.accept()); | |
| t!(a2.accept()); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn clone_accept_concurrent() { | |
| each_ip(&mut |addr| { | |
| let a = t!(TcpListener::bind(&addr)); | |
| let a2 = t!(a.try_clone()); | |
| let (tx, rx) = channel(); | |
| let tx2 = tx.clone(); | |
| let _t = thread::spawn(move || { | |
| tx.send(t!(a.accept())).unwrap(); | |
| }); | |
| let _t = thread::spawn(move || { | |
| tx2.send(t!(a2.accept())).unwrap(); | |
| }); | |
| let _t = thread::spawn(move || { | |
| let _ = TcpStream::connect(&addr); | |
| }); | |
| let _t = thread::spawn(move || { | |
| let _ = TcpStream::connect(&addr); | |
| }); | |
| rx.recv().unwrap(); | |
| rx.recv().unwrap(); | |
| }) | |
| } | |
| #[test] | |
| fn debug() { | |
| #[cfg(not(target_env = "sgx"))] | |
| fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a { | |
| addr | |
| } | |
| #[cfg(target_env = "sgx")] | |
| fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a { | |
| addr.to_string() | |
| } | |
| #[cfg(any(unix, target_os = "wasi"))] | |
| use crate::os::fd::AsRawFd; | |
| #[cfg(target_env = "sgx")] | |
| use crate::os::fortanix_sgx::io::AsRawFd; | |
| #[cfg(not(windows))] | |
| fn render_inner(addr: &dyn AsRawFd) -> impl fmt::Debug { | |
| addr.as_raw_fd() | |
| } | |
| #[cfg(windows)] | |
| fn render_inner(addr: &dyn crate::os::windows::io::AsRawSocket) -> impl fmt::Debug { | |
| addr.as_raw_socket() | |
| } | |
| let inner_name = if cfg!(windows) { "socket" } else { "fd" }; | |
| let socket_addr = next_test_ip4(); | |
| let listener = t!(TcpListener::bind(&socket_addr)); | |
| let compare = format!( | |
| "TcpListener {{ addr: {:?}, {}: {:?} }}", | |
| render_socket_addr(&socket_addr), | |
| inner_name, | |
| render_inner(&listener) | |
| ); | |
| assert_eq!(format!("{listener:?}"), compare); | |
| let stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); | |
| let compare = format!( | |
| "TcpStream {{ addr: {:?}, peer: {:?}, {}: {:?} }}", | |
| render_socket_addr(&stream.local_addr().unwrap()), | |
| render_socket_addr(&stream.peer_addr().unwrap()), | |
| inner_name, | |
| render_inner(&stream) | |
| ); | |
| assert_eq!(format!("{stream:?}"), compare); | |
| } | |
| // FIXME: re-enabled openbsd tests once their socket timeout code | |
| // no longer has rounding errors. | |
| // VxWorks ignores SO_SNDTIMEO. | |
| #[cfg_attr( | |
| any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"), | |
| ignore | |
| )] | |
| #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 | |
| #[cfg_attr(target_os = "wasi", ignore)] // timeout not supported | |
| #[test] | |
| fn timeouts() { | |
| let addr = next_test_ip4(); | |
| let listener = t!(TcpListener::bind(&addr)); | |
| let stream = t!(TcpStream::connect(&("localhost", addr.port()))); | |
| let dur = Duration::new(15410, 0); | |
| assert_eq!(None, t!(stream.read_timeout())); | |
| t!(stream.set_read_timeout(Some(dur))); | |
| assert_eq!(Some(dur), t!(stream.read_timeout())); | |
| assert_eq!(None, t!(stream.write_timeout())); | |
| t!(stream.set_write_timeout(Some(dur))); | |
| assert_eq!(Some(dur), t!(stream.write_timeout())); | |
| t!(stream.set_read_timeout(None)); | |
| assert_eq!(None, t!(stream.read_timeout())); | |
| t!(stream.set_write_timeout(None)); | |
| assert_eq!(None, t!(stream.write_timeout())); | |
| drop(listener); | |
| } | |
| #[test] | |
| #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 | |
| #[cfg_attr(target_os = "wasi", ignore)] // timeout not supported | |
| fn test_read_timeout() { | |
| let addr = next_test_ip4(); | |
| let listener = t!(TcpListener::bind(&addr)); | |
| let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); | |
| t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); | |
| let mut buf = [0; 10]; | |
| let start = Instant::now(); | |
| let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); | |
| assert!( | |
| kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, | |
| "unexpected_error: {:?}", | |
| kind | |
| ); | |
| assert!(start.elapsed() > Duration::from_millis(400)); | |
| drop(listener); | |
| } | |
| #[test] | |
| #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 | |
| #[cfg_attr(target_os = "wasi", ignore)] // timeout not supported | |
| fn test_read_with_timeout() { | |
| let addr = next_test_ip4(); | |
| let listener = t!(TcpListener::bind(&addr)); | |
| let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); | |
| t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); | |
| let mut other_end = t!(listener.accept()).0; | |
| t!(other_end.write_all(b"hello world")); | |
| let mut buf = [0; 11]; | |
| t!(stream.read(&mut buf)); | |
| assert_eq!(b"hello world", &buf[..]); | |
| let start = Instant::now(); | |
| let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); | |
| assert!( | |
| kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, | |
| "unexpected_error: {:?}", | |
| kind | |
| ); | |
| assert!(start.elapsed() > Duration::from_millis(400)); | |
| drop(listener); | |
| } | |
| // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors | |
| // when passed zero Durations | |
| #[test] | |
| fn test_timeout_zero_duration() { | |
| let addr = next_test_ip4(); | |
| let listener = t!(TcpListener::bind(&addr)); | |
| let stream = t!(TcpStream::connect(&addr)); | |
| let result = stream.set_write_timeout(Some(Duration::new(0, 0))); | |
| let err = result.unwrap_err(); | |
| assert_eq!(err.kind(), ErrorKind::InvalidInput); | |
| let result = stream.set_read_timeout(Some(Duration::new(0, 0))); | |
| let err = result.unwrap_err(); | |
| assert_eq!(err.kind(), ErrorKind::InvalidInput); | |
| drop(listener); | |
| } | |
| #[test] | |
| #[cfg_attr(target_env = "sgx", ignore)] | |
| #[cfg_attr(target_os = "wasi", ignore)] // linger not supported | |
| fn linger() { | |
| let addr = next_test_ip4(); | |
| let _listener = t!(TcpListener::bind(&addr)); | |
| let stream = t!(TcpStream::connect(&("localhost", addr.port()))); | |
| assert_eq!(None, t!(stream.linger())); | |
| t!(stream.set_linger(Some(Duration::from_secs(1)))); | |
| assert_eq!(Some(Duration::from_secs(1)), t!(stream.linger())); | |
| t!(stream.set_linger(None)); | |
| assert_eq!(None, t!(stream.linger())); | |
| } | |
| #[test] | |
| #[cfg_attr(target_env = "sgx", ignore)] | |
| fn nodelay() { | |
| let addr = next_test_ip4(); | |
| let _listener = t!(TcpListener::bind(&addr)); | |
| let stream = t!(TcpStream::connect(&("localhost", addr.port()))); | |
| assert_eq!(false, t!(stream.nodelay())); | |
| t!(stream.set_nodelay(true)); | |
| assert_eq!(true, t!(stream.nodelay())); | |
| t!(stream.set_nodelay(false)); | |
| assert_eq!(false, t!(stream.nodelay())); | |
| } | |
| #[test] | |
| #[cfg_attr(target_env = "sgx", ignore)] | |
| fn ttl() { | |
| let ttl = 100; | |
| let addr = next_test_ip4(); | |
| let listener = t!(TcpListener::bind(&addr)); | |
| t!(listener.set_ttl(ttl)); | |
| assert_eq!(ttl, t!(listener.ttl())); | |
| let stream = t!(TcpStream::connect(&("localhost", addr.port()))); | |
| t!(stream.set_ttl(ttl)); | |
| assert_eq!(ttl, t!(stream.ttl())); | |
| } | |
| #[test] | |
| #[cfg_attr(target_env = "sgx", ignore)] | |
| fn set_nonblocking() { | |
| let addr = next_test_ip4(); | |
| let listener = t!(TcpListener::bind(&addr)); | |
| t!(listener.set_nonblocking(true)); | |
| t!(listener.set_nonblocking(false)); | |
| let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); | |
| t!(stream.set_nonblocking(false)); | |
| t!(stream.set_nonblocking(true)); | |
| let mut buf = [0]; | |
| match stream.read(&mut buf) { | |
| Ok(_) => panic!("expected error"), | |
| Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} | |
| Err(e) => panic!("unexpected error {e}"), | |
| } | |
| } | |
| #[test] | |
| #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 | |
| #[cfg_attr(target_os = "wasi", ignore)] // no threads | |
| fn peek() { | |
| each_ip(&mut |addr| { | |
| let (txdone, rxdone) = channel(); | |
| let srv = t!(TcpListener::bind(&addr)); | |
| let _t = thread::spawn(move || { | |
| let mut cl = t!(srv.accept()).0; | |
| cl.write(&[1, 3, 3, 7]).unwrap(); | |
| t!(rxdone.recv()); | |
| }); | |
| let mut c = t!(TcpStream::connect(&addr)); | |
| let mut b = [0; 10]; | |
| for _ in 1..3 { | |
| let len = c.peek(&mut b).unwrap(); | |
| assert_eq!(len, 4); | |
| } | |
| let len = c.read(&mut b).unwrap(); | |
| assert_eq!(len, 4); | |
| t!(c.set_nonblocking(true)); | |
| match c.peek(&mut b) { | |
| Ok(_) => panic!("expected error"), | |
| Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} | |
| Err(e) => panic!("unexpected error {e}"), | |
| } | |
| t!(txdone.send(())); | |
| }) | |
| } | |
| #[test] | |
| #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 | |
| fn connect_timeout_valid() { | |
| let listener = TcpListener::bind("127.0.0.1:0").unwrap(); | |
| let addr = listener.local_addr().unwrap(); | |
| TcpStream::connect_timeout(&addr, Duration::from_secs(2)).unwrap(); | |
| } |