• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2018-2019, Cloudflare, Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright notice,
9 //       this list of conditions and the following disclaimer.
10 //
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 #[macro_use]
28 extern crate log;
29 
30 use std::net::ToSocketAddrs;
31 
32 use ring::rand::*;
33 
34 const MAX_DATAGRAM_SIZE: usize = 1350;
35 
36 const HTTP_REQ_STREAM_ID: u64 = 4;
37 
main()38 fn main() {
39     let mut buf = [0; 65535];
40     let mut out = [0; MAX_DATAGRAM_SIZE];
41 
42     let mut args = std::env::args();
43 
44     let cmd = &args.next().unwrap();
45 
46     if args.len() != 1 {
47         println!("Usage: {cmd} URL");
48         println!("\nSee tools/apps/ for more complete implementations.");
49         return;
50     }
51 
52     let url = url::Url::parse(&args.next().unwrap()).unwrap();
53 
54     // Setup the event loop.
55     let mut poll = mio::Poll::new().unwrap();
56     let mut events = mio::Events::with_capacity(1024);
57 
58     // Resolve server address.
59     let peer_addr = url.to_socket_addrs().unwrap().next().unwrap();
60 
61     // Bind to INADDR_ANY or IN6ADDR_ANY depending on the IP family of the
62     // server address. This is needed on macOS and BSD variants that don't
63     // support binding to IN6ADDR_ANY for both v4 and v6.
64     let bind_addr = match peer_addr {
65         std::net::SocketAddr::V4(_) => "0.0.0.0:0",
66         std::net::SocketAddr::V6(_) => "[::]:0",
67     };
68 
69     // Create the UDP socket backing the QUIC connection, and register it with
70     // the event loop.
71     let mut socket =
72         mio::net::UdpSocket::bind(bind_addr.parse().unwrap()).unwrap();
73     poll.registry()
74         .register(&mut socket, mio::Token(0), mio::Interest::READABLE)
75         .unwrap();
76 
77     // Create the configuration for the QUIC connection.
78     let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
79 
80     // *CAUTION*: this should not be set to `false` in production!!!
81     config.verify_peer(false);
82 
83     config
84         .set_application_protos(&[
85             b"hq-interop",
86             b"hq-29",
87             b"hq-28",
88             b"hq-27",
89             b"http/0.9",
90         ])
91         .unwrap();
92 
93     config.set_max_idle_timeout(5000);
94     config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE);
95     config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE);
96     config.set_initial_max_data(10_000_000);
97     config.set_initial_max_stream_data_bidi_local(1_000_000);
98     config.set_initial_max_stream_data_bidi_remote(1_000_000);
99     config.set_initial_max_streams_bidi(100);
100     config.set_initial_max_streams_uni(100);
101     config.set_disable_active_migration(true);
102 
103     // Generate a random source connection ID for the connection.
104     let mut scid = [0; quiche::MAX_CONN_ID_LEN];
105     SystemRandom::new().fill(&mut scid[..]).unwrap();
106 
107     let scid = quiche::ConnectionId::from_ref(&scid);
108 
109     // Get local address.
110     let local_addr = socket.local_addr().unwrap();
111 
112     // Create a QUIC connection and initiate handshake.
113     let mut conn =
114         quiche::connect(url.domain(), &scid, local_addr, peer_addr, &mut config)
115             .unwrap();
116 
117     info!(
118         "connecting to {:} from {:} with scid {}",
119         peer_addr,
120         socket.local_addr().unwrap(),
121         hex_dump(&scid)
122     );
123 
124     let (write, send_info) = conn.send(&mut out).expect("initial send failed");
125 
126     while let Err(e) = socket.send_to(&out[..write], send_info.to) {
127         if e.kind() == std::io::ErrorKind::WouldBlock {
128             debug!("send() would block");
129             continue;
130         }
131 
132         panic!("send() failed: {:?}", e);
133     }
134 
135     debug!("written {}", write);
136 
137     let req_start = std::time::Instant::now();
138 
139     let mut req_sent = false;
140 
141     loop {
142         poll.poll(&mut events, conn.timeout()).unwrap();
143 
144         // Read incoming UDP packets from the socket and feed them to quiche,
145         // until there are no more packets to read.
146         'read: loop {
147             // If the event loop reported no events, it means that the timeout
148             // has expired, so handle it without attempting to read packets. We
149             // will then proceed with the send loop.
150             if events.is_empty() {
151                 debug!("timed out");
152 
153                 conn.on_timeout();
154                 break 'read;
155             }
156 
157             let (len, from) = match socket.recv_from(&mut buf) {
158                 Ok(v) => v,
159 
160                 Err(e) => {
161                     // There are no more UDP packets to read, so end the read
162                     // loop.
163                     if e.kind() == std::io::ErrorKind::WouldBlock {
164                         debug!("recv() would block");
165                         break 'read;
166                     }
167 
168                     panic!("recv() failed: {:?}", e);
169                 },
170             };
171 
172             debug!("got {} bytes", len);
173 
174             let recv_info = quiche::RecvInfo {
175                 to: socket.local_addr().unwrap(),
176                 from,
177             };
178 
179             // Process potentially coalesced packets.
180             let read = match conn.recv(&mut buf[..len], recv_info) {
181                 Ok(v) => v,
182 
183                 Err(e) => {
184                     error!("recv failed: {:?}", e);
185                     continue 'read;
186                 },
187             };
188 
189             debug!("processed {} bytes", read);
190         }
191 
192         debug!("done reading");
193 
194         if conn.is_closed() {
195             info!("connection closed, {:?}", conn.stats());
196             break;
197         }
198 
199         // Send an HTTP request as soon as the connection is established.
200         if conn.is_established() && !req_sent {
201             info!("sending HTTP request for {}", url.path());
202 
203             let req = format!("GET {}\r\n", url.path());
204             conn.stream_send(HTTP_REQ_STREAM_ID, req.as_bytes(), true)
205                 .unwrap();
206 
207             req_sent = true;
208         }
209 
210         // Process all readable streams.
211         for s in conn.readable() {
212             while let Ok((read, fin)) = conn.stream_recv(s, &mut buf) {
213                 debug!("received {} bytes", read);
214 
215                 let stream_buf = &buf[..read];
216 
217                 debug!(
218                     "stream {} has {} bytes (fin? {})",
219                     s,
220                     stream_buf.len(),
221                     fin
222                 );
223 
224                 print!("{}", unsafe {
225                     std::str::from_utf8_unchecked(stream_buf)
226                 });
227 
228                 // The server reported that it has no more data to send, which
229                 // we got the full response. Close the connection.
230                 if s == HTTP_REQ_STREAM_ID && fin {
231                     info!(
232                         "response received in {:?}, closing...",
233                         req_start.elapsed()
234                     );
235 
236                     conn.close(true, 0x00, b"kthxbye").unwrap();
237                 }
238             }
239         }
240 
241         // Generate outgoing QUIC packets and send them on the UDP socket, until
242         // quiche reports that there are no more packets to be sent.
243         loop {
244             let (write, send_info) = match conn.send(&mut out) {
245                 Ok(v) => v,
246 
247                 Err(quiche::Error::Done) => {
248                     debug!("done writing");
249                     break;
250                 },
251 
252                 Err(e) => {
253                     error!("send failed: {:?}", e);
254 
255                     conn.close(false, 0x1, b"fail").ok();
256                     break;
257                 },
258             };
259 
260             if let Err(e) = socket.send_to(&out[..write], send_info.to) {
261                 if e.kind() == std::io::ErrorKind::WouldBlock {
262                     debug!("send() would block");
263                     break;
264                 }
265 
266                 panic!("send() failed: {:?}", e);
267             }
268 
269             debug!("written {}", write);
270         }
271 
272         if conn.is_closed() {
273             info!("connection closed, {:?}", conn.stats());
274             break;
275         }
276     }
277 }
278 
hex_dump(buf: &[u8]) -> String279 fn hex_dump(buf: &[u8]) -> String {
280     let vec: Vec<String> = buf.iter().map(|b| format!("{b:02x}")).collect();
281 
282     vec.join("")
283 }
284