]>
Commit | Line | Data |
---|---|---|
1b1a35ee XL |
1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | use std::cell::RefCell; | |
12 | use std::io; | |
13 | use std::net::{SocketAddr, ToSocketAddrs, TcpListener, TcpStream}; | |
14 | use std::fmt; | |
15 | ||
16 | use IntoInner; | |
17 | use socket::Socket; | |
18 | use sys::c; | |
19 | ||
20 | /// An "in progress" TCP socket which has not yet been connected or listened. | |
21 | /// | |
22 | /// Allows configuration of a socket before one of these operations is executed. | |
23 | pub struct TcpBuilder { | |
24 | socket: RefCell<Option<Socket>>, | |
25 | } | |
26 | ||
27 | impl TcpBuilder { | |
28 | /// Constructs a new TcpBuilder with the `AF_INET` domain, the `SOCK_STREAM` | |
29 | /// type, and with a protocol argument of 0. | |
30 | /// | |
31 | /// Note that passing other kinds of flags or arguments can be done through | |
32 | /// the `FromRaw{Fd,Socket}` implementation. | |
33 | pub fn new_v4() -> io::Result<TcpBuilder> { | |
34 | Socket::new(c::AF_INET, c::SOCK_STREAM).map(::FromInner::from_inner) | |
35 | } | |
36 | ||
37 | /// Constructs a new TcpBuilder with the `AF_INET6` domain, the `SOCK_STREAM` | |
38 | /// type, and with a protocol argument of 0. | |
39 | /// | |
40 | /// Note that passing other kinds of flags or arguments can be done through | |
41 | /// the `FromRaw{Fd,Socket}` implementation. | |
42 | pub fn new_v6() -> io::Result<TcpBuilder> { | |
43 | Socket::new(c::AF_INET6, c::SOCK_STREAM).map(::FromInner::from_inner) | |
44 | } | |
45 | ||
46 | /// Binds this socket to the specified address. | |
47 | /// | |
48 | /// This function directly corresponds to the bind(2) function on Windows | |
49 | /// and Unix. | |
50 | pub fn bind<T>(&self, addr: T) -> io::Result<&TcpBuilder> | |
51 | where T: ToSocketAddrs | |
52 | { | |
53 | self.with_socket(|sock| { | |
54 | let addr = try!(::one_addr(addr)); | |
55 | sock.bind(&addr) | |
56 | }).map(|()| self) | |
57 | } | |
58 | ||
59 | /// Mark a socket as ready to accept incoming connection requests using | |
60 | /// accept() | |
61 | /// | |
62 | /// This function directly corresponds to the listen(2) function on Windows | |
63 | /// and Unix. | |
64 | /// | |
65 | /// An error will be returned if `listen` or `connect` has already been | |
66 | /// called on this builder. | |
67 | pub fn listen(&self, backlog: i32) -> io::Result<TcpListener> { | |
68 | self.with_socket(|sock| { | |
69 | sock.listen(backlog) | |
70 | }).and_then(|()| { | |
71 | self.to_tcp_listener() | |
72 | }) | |
73 | } | |
74 | ||
75 | /// Initiate a connection on this socket to the specified address. | |
76 | /// | |
77 | /// This function directly corresponds to the connect(2) function on Windows | |
78 | /// and Unix. | |
79 | /// | |
80 | /// An error will be returned if `listen` or `connect` has already been | |
81 | /// called on this builder. | |
82 | pub fn connect<T>(&self, addr: T) -> io::Result<TcpStream> | |
83 | where T: ToSocketAddrs | |
84 | { | |
85 | self.with_socket(|sock| { | |
86 | let err = io::Error::new(io::ErrorKind::Other, | |
87 | "no socket addresses resolved"); | |
88 | try!(addr.to_socket_addrs()).fold(Err(err), |prev, addr| { | |
89 | prev.or_else(|_| sock.connect(&addr)) | |
90 | }) | |
91 | }).and_then(|()| { | |
92 | self.to_tcp_stream() | |
93 | }) | |
94 | } | |
95 | ||
96 | /// Converts this builder into a `TcpStream` | |
97 | /// | |
98 | /// This function will consume the internal socket and return it re-wrapped | |
99 | /// as a `TcpStream`. An error will be returned if the internal socket has | |
100 | /// already been consumed from a successful call to `connect`, `listen`, | |
101 | /// etc. | |
102 | pub fn to_tcp_stream(&self) -> io::Result<TcpStream> { | |
103 | self.socket.borrow_mut().take().map(|s| s.into_inner().into_tcp_stream()) | |
104 | .ok_or(io::Error::new(io::ErrorKind::Other, | |
105 | "socket has already been consumed")) | |
106 | } | |
107 | ||
108 | /// Converts this builder into a `TcpListener` | |
109 | /// | |
110 | /// This function will consume the internal socket and return it re-wrapped | |
111 | /// as a `TcpListener`. An error will be returned if the internal socket has | |
112 | /// already been consumed from a successful call to `connect`, `listen`, | |
113 | /// etc. | |
114 | pub fn to_tcp_listener(&self) -> io::Result<TcpListener> { | |
115 | self.socket.borrow_mut().take() | |
116 | .map(|s| s.into_inner().into_tcp_listener()) | |
117 | .ok_or(io::Error::new(io::ErrorKind::Other, | |
118 | "socket has already been consumed")) | |
119 | } | |
120 | ||
121 | /// Returns the address of the local half of this TCP socket. | |
122 | /// | |
123 | /// An error will be returned if `listen` or `connect` has already been | |
124 | /// called on this builder. | |
125 | pub fn local_addr(&self) -> io::Result<SocketAddr> { | |
126 | match *self.socket.borrow() { | |
127 | Some(ref s) => s.getsockname(), | |
128 | None => Err(io::Error::new(io::ErrorKind::Other, | |
129 | "builder has already finished its socket")), | |
130 | } | |
131 | } | |
132 | ||
133 | fn with_socket<F>(&self, f: F) -> io::Result<()> | |
134 | where F: FnOnce(&Socket) -> io::Result<()> | |
135 | { | |
136 | match *self.socket.borrow() { | |
137 | Some(ref s) => f(s), | |
138 | None => Err(io::Error::new(io::ErrorKind::Other, | |
139 | "builder has already finished its socket")), | |
140 | } | |
141 | } | |
142 | } | |
143 | ||
144 | impl fmt::Debug for TcpBuilder { | |
145 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
146 | write!(f, "TcpBuilder {{ socket: {:?} }}", | |
147 | self.socket.borrow().as_ref().unwrap()) | |
148 | } | |
149 | } | |
150 | ||
151 | impl ::AsInner for TcpBuilder { | |
152 | type Inner = RefCell<Option<Socket>>; | |
153 | fn as_inner(&self) -> &RefCell<Option<Socket>> { &self.socket } | |
154 | } | |
155 | ||
156 | impl ::FromInner for TcpBuilder { | |
157 | type Inner = Socket; | |
158 | fn from_inner(sock: Socket) -> TcpBuilder { | |
159 | TcpBuilder { socket: RefCell::new(Some(sock)) } | |
160 | } | |
161 | } |