mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-07 00:00:09 +02:00
363 lines
13 KiB
Rust
363 lines
13 KiB
Rust
use std::fmt;
|
|
use std::io::{self, ErrorKind, Read, Write};
|
|
use std::mem;
|
|
use std::net::{self, SocketAddr, Shutdown};
|
|
use std::sync::Arc;
|
|
|
|
use futures::stream::{self, Stream};
|
|
use futures::{Future, IntoFuture, failed, Task, Poll};
|
|
use futures_io::{Ready, IoFuture, IoStream};
|
|
use mio;
|
|
|
|
use {ReadinessStream, LoopHandle};
|
|
use event_loop::Source;
|
|
|
|
/// An I/O object representing a TCP socket listening for incoming connections.
|
|
///
|
|
/// This object can be converted into a stream of incoming connections for
|
|
/// various forms of processing.
|
|
pub struct TcpListener {
|
|
loop_handle: LoopHandle,
|
|
ready: ReadinessStream,
|
|
listener: Arc<Source<mio::tcp::TcpListener>>,
|
|
}
|
|
|
|
impl TcpListener {
|
|
fn new(listener: mio::tcp::TcpListener,
|
|
handle: LoopHandle) -> Box<IoFuture<TcpListener>> {
|
|
let listener = Arc::new(Source::new(listener));
|
|
ReadinessStream::new(handle.clone(), listener.clone()).map(|r| {
|
|
TcpListener {
|
|
loop_handle: handle,
|
|
ready: r,
|
|
listener: listener,
|
|
}
|
|
}).boxed()
|
|
}
|
|
|
|
/// Create a new TCP listener from the standard library's TCP listener.
|
|
///
|
|
/// This method can be used when the `LoopHandle::tcp_listen` method isn't
|
|
/// sufficient because perhaps some more configuration is needed in terms of
|
|
/// before the calls to `bind` and `listen`.
|
|
///
|
|
/// This API is typically paired with the `net2` crate and the `TcpBuilder`
|
|
/// type to build up and customize a listener before it's shipped off to the
|
|
/// backing event loop. This allows configuration of options like
|
|
/// `SO_REUSEPORT`, binding to multiple addresses, etc.
|
|
///
|
|
/// The `addr` argument here is one of the addresses that `listener` is
|
|
/// bound to and the listener will only be guaranteed to accept connections
|
|
/// of the same address type currently.
|
|
///
|
|
/// Finally, the `handle` argument is the event loop that this listener will
|
|
/// be bound to.
|
|
///
|
|
/// The platform specific behavior of this function looks like:
|
|
///
|
|
/// * On Unix, the socket is placed into nonblocking mode and connections
|
|
/// can be accepted as normal
|
|
///
|
|
/// * On Windows, the address is stored internally and all future accepts
|
|
/// will only be for the same IP version as `addr` specified. That is, if
|
|
/// `addr` is an IPv4 address then all sockets accepted will be IPv4 as
|
|
/// well (same for IPv6).
|
|
pub fn from_listener(listener: net::TcpListener,
|
|
addr: &SocketAddr,
|
|
handle: LoopHandle) -> Box<IoFuture<TcpListener>> {
|
|
mio::tcp::TcpListener::from_listener(listener, addr)
|
|
.into_future()
|
|
.and_then(|l| TcpListener::new(l, handle))
|
|
.boxed()
|
|
}
|
|
|
|
/// Returns the local address that this listener is bound to.
|
|
///
|
|
/// This can be useful, for example, when binding to port 0 to figure out
|
|
/// which port was actually bound.
|
|
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
|
self.listener.io().local_addr()
|
|
}
|
|
|
|
/// Consumes this listener, returning a stream of the sockets this listener
|
|
/// accepts.
|
|
///
|
|
/// This method returns an implementation of the `Stream` trait which
|
|
/// resolves to the sockets the are accepted on this listener.
|
|
pub fn incoming(self) -> Box<IoStream<(TcpStream, SocketAddr)>> {
|
|
let TcpListener { loop_handle, listener, ready } = self;
|
|
|
|
ready
|
|
.map(move |_| {
|
|
stream::iter(NonblockingIter { source: listener.clone() }.fuse())
|
|
})
|
|
.flatten()
|
|
.and_then(move |(tcp, addr)| {
|
|
let tcp = Arc::new(Source::new(tcp));
|
|
ReadinessStream::new(loop_handle.clone(),
|
|
tcp.clone()).map(move |ready| {
|
|
let stream = TcpStream {
|
|
source: tcp,
|
|
ready: ready,
|
|
};
|
|
(stream, addr)
|
|
})
|
|
}).boxed()
|
|
}
|
|
}
|
|
|
|
struct NonblockingIter {
|
|
source: Arc<Source<mio::tcp::TcpListener>>,
|
|
}
|
|
|
|
impl Iterator for NonblockingIter {
|
|
type Item = io::Result<(mio::tcp::TcpStream, SocketAddr)>;
|
|
|
|
fn next(&mut self) -> Option<io::Result<(mio::tcp::TcpStream, SocketAddr)>> {
|
|
match self.source.io().accept() {
|
|
Ok(Some(e)) => {
|
|
debug!("accepted connection");
|
|
Some(Ok(e))
|
|
}
|
|
Ok(None) => {
|
|
debug!("no connection ready");
|
|
None
|
|
}
|
|
Err(e) => Some(Err(e)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for TcpListener {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
self.listener.io().fmt(f)
|
|
}
|
|
}
|
|
|
|
impl Stream for TcpListener {
|
|
type Item = Ready;
|
|
type Error = io::Error;
|
|
|
|
fn poll(&mut self, task: &mut Task) -> Poll<Option<Ready>, io::Error> {
|
|
self.ready.poll(task)
|
|
}
|
|
|
|
fn schedule(&mut self, task: &mut Task) {
|
|
self.ready.schedule(task)
|
|
}
|
|
}
|
|
|
|
/// An I/O object representing a TCP stream connected to a remote endpoint.
|
|
///
|
|
/// A TCP stream can either be created by connecting to an endpoint or by
|
|
/// accepting a connection from a listener. Inside the stream is access to the
|
|
/// raw underlying I/O object as well as streams for the read/write
|
|
/// notifications on the stream itself.
|
|
pub struct TcpStream {
|
|
source: Arc<Source<mio::tcp::TcpStream>>,
|
|
ready: ReadinessStream,
|
|
}
|
|
|
|
enum TcpStreamNew {
|
|
Waiting(TcpStream),
|
|
Empty,
|
|
}
|
|
|
|
impl LoopHandle {
|
|
/// Create a new TCP listener associated with this event loop.
|
|
///
|
|
/// The TCP listener will bind to the provided `addr` address, if available,
|
|
/// and will be returned as a future. The returned future, if resolved
|
|
/// successfully, can then be used to accept incoming connections.
|
|
pub fn tcp_listen(self, addr: &SocketAddr) -> Box<IoFuture<TcpListener>> {
|
|
match mio::tcp::TcpListener::bind(addr) {
|
|
Ok(l) => TcpListener::new(l, self),
|
|
Err(e) => failed(e).boxed(),
|
|
}
|
|
}
|
|
|
|
/// Create a new TCP stream connected to the specified address.
|
|
///
|
|
/// This function will create a new TCP socket and attempt to connect it to
|
|
/// the `addr` provided. The returned future will be resolved once the
|
|
/// stream has successfully connected. If an error happens during the
|
|
/// connection or during the socket creation, that error will be returned to
|
|
/// the future instead.
|
|
pub fn tcp_connect(self, addr: &SocketAddr) -> Box<IoFuture<TcpStream>> {
|
|
match mio::tcp::TcpStream::connect(addr) {
|
|
Ok(tcp) => TcpStream::new(tcp, self),
|
|
Err(e) => failed(e).boxed(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TcpStream {
|
|
fn new(connected_stream: mio::tcp::TcpStream,
|
|
handle: LoopHandle)
|
|
-> Box<IoFuture<TcpStream>> {
|
|
// Once we've connected, wait for the stream to be writable as that's
|
|
// when the actual connection has been initiated. Once we're writable we
|
|
// check for `take_socket_error` to see if the connect actually hit an
|
|
// error or not.
|
|
//
|
|
// If all that succeeded then we ship everything on up.
|
|
let connected_stream = Arc::new(Source::new(connected_stream));
|
|
ReadinessStream::new(handle, connected_stream.clone()).and_then(|ready| {
|
|
TcpStreamNew::Waiting(TcpStream {
|
|
source: connected_stream,
|
|
ready: ready,
|
|
})
|
|
}).boxed()
|
|
}
|
|
|
|
/// Creates a new `TcpStream` from the pending socket inside the given
|
|
/// `std::net::TcpStream`, connecting it to the address specified.
|
|
///
|
|
/// This constructor allows configuring the socket before it's actually
|
|
/// connected, and this function will transfer ownership to the returned
|
|
/// `TcpStream` if successful. An unconnected `TcpStream` can be created
|
|
/// with the `net2::TcpBuilder` type (and also configured via that route).
|
|
///
|
|
/// The platform specific behavior of this function looks like:
|
|
///
|
|
/// * On Unix, the socket is placed into nonblocking mode and then a
|
|
/// `connect` call is issued.
|
|
///
|
|
/// * On Windows, the address is stored internally and the connect operation
|
|
/// is issued when the returned `TcpStream` is registered with an event
|
|
/// loop. Note that on Windows you must `bind` a socket before it can be
|
|
/// connected, so if a custom `TcpBuilder` is used it should be bound
|
|
/// (perhaps to `INADDR_ANY`) before this method is called.
|
|
pub fn connect_stream(stream: net::TcpStream,
|
|
addr: &SocketAddr,
|
|
handle: LoopHandle) -> Box<IoFuture<TcpStream>> {
|
|
match mio::tcp::TcpStream::connect_stream(stream, addr) {
|
|
Ok(tcp) => TcpStream::new(tcp, handle),
|
|
Err(e) => failed(e).boxed(),
|
|
}
|
|
}
|
|
|
|
/// Returns the local address that this stream is bound to.
|
|
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
|
self.source.io().local_addr()
|
|
}
|
|
|
|
/// Returns the remote address that this stream is connected to.
|
|
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
|
self.source.io().peer_addr()
|
|
}
|
|
|
|
/// Shuts down the read, write, or both halves of this connection.
|
|
///
|
|
/// This function will cause all pending and future I/O on the specified
|
|
/// portions to return immediately with an appropriate value (see the
|
|
/// documentation of `Shutdown`).
|
|
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
|
|
self.source.io().shutdown(how)
|
|
}
|
|
|
|
/// Sets the value of the `TCP_NODELAY` option on this socket.
|
|
///
|
|
/// If set, this option disables the Nagle algorithm. This means that
|
|
/// segments are always sent as soon as possible, even if there is only a
|
|
/// small amount of data. When not set, data is buffered until there is a
|
|
/// sufficient amount to send out, thereby avoiding the frequent sending of
|
|
/// small packets.
|
|
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
|
|
self.source.io().set_nodelay(nodelay)
|
|
}
|
|
|
|
/// Sets the keepalive time in seconds for this socket.
|
|
pub fn set_keepalive_s(&self, seconds: Option<u32>) -> io::Result<()> {
|
|
self.source.io().set_keepalive(seconds)
|
|
}
|
|
}
|
|
|
|
impl Future for TcpStreamNew {
|
|
type Item = TcpStream;
|
|
type Error = io::Error;
|
|
|
|
fn poll(&mut self, task: &mut Task) -> Poll<TcpStream, io::Error> {
|
|
let mut stream = match mem::replace(self, TcpStreamNew::Empty) {
|
|
TcpStreamNew::Waiting(s) => s,
|
|
TcpStreamNew::Empty => panic!("can't poll TCP stream twice"),
|
|
};
|
|
match stream.ready.poll(task) {
|
|
Poll::Ok(None) => panic!(),
|
|
Poll::Ok(Some(_)) => {
|
|
match stream.source.io().take_socket_error() {
|
|
Ok(()) => return Poll::Ok(stream),
|
|
Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
|
|
Err(e) => return Poll::Err(e),
|
|
}
|
|
}
|
|
Poll::Err(e) => return Poll::Err(e),
|
|
Poll::NotReady => {}
|
|
}
|
|
*self = TcpStreamNew::Waiting(stream);
|
|
Poll::NotReady
|
|
}
|
|
|
|
fn schedule(&mut self, task: &mut Task) {
|
|
match *self {
|
|
TcpStreamNew::Waiting(ref mut s) => {
|
|
s.ready.schedule(task);
|
|
}
|
|
TcpStreamNew::Empty => task.notify(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Read for TcpStream {
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
let r = self.source.io().read(buf);
|
|
trace!("read[{:p}] {:?} on {:?}", self, r, self.source.io());
|
|
return r
|
|
}
|
|
}
|
|
|
|
impl Write for TcpStream {
|
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
let r = self.source.io().write(buf);
|
|
trace!("write[{:p}] {:?} on {:?}", self, r, self.source.io());
|
|
return r
|
|
}
|
|
fn flush(&mut self) -> io::Result<()> {
|
|
self.source.io().flush()
|
|
}
|
|
}
|
|
|
|
impl<'a> Read for &'a TcpStream {
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
self.source.io().read(buf)
|
|
}
|
|
}
|
|
|
|
impl<'a> Write for &'a TcpStream {
|
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
self.source.io().write(buf)
|
|
}
|
|
fn flush(&mut self) -> io::Result<()> {
|
|
self.source.io().flush()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for TcpStream {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
self.source.io().fmt(f)
|
|
}
|
|
}
|
|
|
|
impl Stream for TcpStream {
|
|
type Item = Ready;
|
|
type Error = io::Error;
|
|
|
|
fn poll(&mut self, task: &mut Task) -> Poll<Option<Ready>, io::Error> {
|
|
self.ready.poll(task)
|
|
}
|
|
|
|
fn schedule(&mut self, task: &mut Task) {
|
|
self.ready.schedule(task)
|
|
}
|
|
}
|