• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 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 
main()36 fn main() {
37     let mut buf = [0; 65535];
38     let mut out = [0; MAX_DATAGRAM_SIZE];
39 
40     let mut args = std::env::args();
41 
42     let cmd = &args.next().unwrap();
43 
44     if args.len() != 1 {
45         println!("Usage: {} URL", cmd);
46         println!("\nSee tools/apps/ for more complete implementations.");
47         return;
48     }
49 
50     let url = url::Url::parse(&args.next().unwrap()).unwrap();
51 
52     // Setup the event loop.
53     let poll = mio::Poll::new().unwrap();
54     let mut events = mio::Events::with_capacity(1024);
55 
56     // Resolve server address.
57     let peer_addr = url.to_socket_addrs().unwrap().next().unwrap();
58 
59     // Bind to INADDR_ANY or IN6ADDR_ANY depending on the IP family of the
60     // server address. This is needed on macOS and BSD variants that don't
61     // support binding to IN6ADDR_ANY for both v4 and v6.
62     let bind_addr = match peer_addr {
63         std::net::SocketAddr::V4(_) => "0.0.0.0:0",
64         std::net::SocketAddr::V6(_) => "[::]:0",
65     };
66 
67     // Create the UDP socket backing the QUIC connection, and register it with
68     // the event loop.
69     let socket = std::net::UdpSocket::bind(bind_addr).unwrap();
70 
71     let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
72     poll.register(
73         &socket,
74         mio::Token(0),
75         mio::Ready::readable(),
76         mio::PollOpt::edge(),
77     )
78     .unwrap();
79 
80     // Create the configuration for the QUIC connection.
81     let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
82 
83     // *CAUTION*: this should not be set to `false` in production!!!
84     config.verify_peer(false);
85 
86     config
87         .set_application_protos(quiche::h3::APPLICATION_PROTOCOL)
88         .unwrap();
89 
90     config.set_max_idle_timeout(5000);
91     config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE);
92     config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE);
93     config.set_initial_max_data(10_000_000);
94     config.set_initial_max_stream_data_bidi_local(1_000_000);
95     config.set_initial_max_stream_data_bidi_remote(1_000_000);
96     config.set_initial_max_stream_data_uni(1_000_000);
97     config.set_initial_max_streams_bidi(100);
98     config.set_initial_max_streams_uni(100);
99     config.set_disable_active_migration(true);
100 
101     let mut http3_conn = None;
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     // Create a QUIC connection and initiate handshake.
110     let mut conn =
111         quiche::connect(url.domain(), &scid, peer_addr, &mut config).unwrap();
112 
113     info!(
114         "connecting to {:} from {:} with scid {}",
115         peer_addr,
116         socket.local_addr().unwrap(),
117         hex_dump(&scid)
118     );
119 
120     let (write, send_info) = conn.send(&mut out).expect("initial send failed");
121 
122     while let Err(e) = socket.send_to(&out[..write], &send_info.to) {
123         if e.kind() == std::io::ErrorKind::WouldBlock {
124             debug!("send() would block");
125             continue;
126         }
127 
128         panic!("send() failed: {:?}", e);
129     }
130 
131     debug!("written {}", write);
132 
133     let h3_config = quiche::h3::Config::new().unwrap();
134 
135     // Prepare request.
136     let mut path = String::from(url.path());
137 
138     if let Some(query) = url.query() {
139         path.push('?');
140         path.push_str(query);
141     }
142 
143     let req = vec![
144         quiche::h3::Header::new(b":method", b"GET"),
145         quiche::h3::Header::new(b":scheme", url.scheme().as_bytes()),
146         quiche::h3::Header::new(
147             b":authority",
148             url.host_str().unwrap().as_bytes(),
149         ),
150         quiche::h3::Header::new(b":path", path.as_bytes()),
151         quiche::h3::Header::new(b"user-agent", b"quiche"),
152     ];
153 
154     let req_start = std::time::Instant::now();
155 
156     let mut req_sent = false;
157 
158     loop {
159         poll.poll(&mut events, conn.timeout()).unwrap();
160 
161         // Read incoming UDP packets from the socket and feed them to quiche,
162         // until there are no more packets to read.
163         'read: loop {
164             // If the event loop reported no events, it means that the timeout
165             // has expired, so handle it without attempting to read packets. We
166             // will then proceed with the send loop.
167             if events.is_empty() {
168                 debug!("timed out");
169 
170                 conn.on_timeout();
171 
172                 break 'read;
173             }
174 
175             let (len, from) = match socket.recv_from(&mut buf) {
176                 Ok(v) => v,
177 
178                 Err(e) => {
179                     // There are no more UDP packets to read, so end the read
180                     // loop.
181                     if e.kind() == std::io::ErrorKind::WouldBlock {
182                         debug!("recv() would block");
183                         break 'read;
184                     }
185 
186                     panic!("recv() failed: {:?}", e);
187                 },
188             };
189 
190             debug!("got {} bytes", len);
191 
192             let recv_info = quiche::RecvInfo { from };
193 
194             // Process potentially coalesced packets.
195             let read = match conn.recv(&mut buf[..len], recv_info) {
196                 Ok(v) => v,
197 
198                 Err(e) => {
199                     error!("recv failed: {:?}", e);
200                     continue 'read;
201                 },
202             };
203 
204             debug!("processed {} bytes", read);
205         }
206 
207         debug!("done reading");
208 
209         if conn.is_closed() {
210             info!("connection closed, {:?}", conn.stats());
211             break;
212         }
213 
214         // Create a new HTTP/3 connection once the QUIC connection is established.
215         if conn.is_established() && http3_conn.is_none() {
216             http3_conn = Some(
217                 quiche::h3::Connection::with_transport(&mut conn, &h3_config)
218                     .unwrap(),
219             );
220         }
221 
222         // Send HTTP requests once the QUIC connection is established, and until
223         // all requests have been sent.
224         if let Some(h3_conn) = &mut http3_conn {
225             if !req_sent {
226                 info!("sending HTTP request {:?}", req);
227 
228                 h3_conn.send_request(&mut conn, &req, true).unwrap();
229 
230                 req_sent = true;
231             }
232         }
233 
234         if let Some(http3_conn) = &mut http3_conn {
235             // Process HTTP/3 events.
236             loop {
237                 match http3_conn.poll(&mut conn) {
238                     Ok((stream_id, quiche::h3::Event::Headers { list, .. })) => {
239                         info!(
240                             "got response headers {:?} on stream id {}",
241                             list, stream_id
242                         );
243                     },
244 
245                     Ok((stream_id, quiche::h3::Event::Data)) => {
246                         while let Ok(read) =
247                             http3_conn.recv_body(&mut conn, stream_id, &mut buf)
248                         {
249                             debug!(
250                                 "got {} bytes of response data on stream {}",
251                                 read, stream_id
252                             );
253 
254                             print!("{}", unsafe {
255                                 std::str::from_utf8_unchecked(&buf[..read])
256                             });
257                         }
258                     },
259 
260                     Ok((_stream_id, quiche::h3::Event::Finished)) => {
261                         info!(
262                             "response received in {:?}, closing...",
263                             req_start.elapsed()
264                         );
265 
266                         conn.close(true, 0x00, b"kthxbye").unwrap();
267                     },
268 
269                     Ok((_flow_id, quiche::h3::Event::Datagram)) => (),
270 
271                     Ok((goaway_id, quiche::h3::Event::GoAway)) => {
272                         info!("GOAWAY id={}", goaway_id);
273                     },
274 
275                     Err(quiche::h3::Error::Done) => {
276                         break;
277                     },
278 
279                     Err(e) => {
280                         error!("HTTP/3 processing failed: {:?}", e);
281 
282                         break;
283                     },
284                 }
285             }
286         }
287 
288         // Generate outgoing QUIC packets and send them on the UDP socket, until
289         // quiche reports that there are no more packets to be sent.
290         loop {
291             let (write, send_info) = match conn.send(&mut out) {
292                 Ok(v) => v,
293 
294                 Err(quiche::Error::Done) => {
295                     debug!("done writing");
296                     break;
297                 },
298 
299                 Err(e) => {
300                     error!("send failed: {:?}", e);
301 
302                     conn.close(false, 0x1, b"fail").ok();
303                     break;
304                 },
305             };
306 
307             if let Err(e) = socket.send_to(&out[..write], &send_info.to) {
308                 if e.kind() == std::io::ErrorKind::WouldBlock {
309                     debug!("send() would block");
310                     break;
311                 }
312 
313                 panic!("send() failed: {:?}", e);
314             }
315 
316             debug!("written {}", write);
317         }
318 
319         if conn.is_closed() {
320             info!("connection closed, {:?}", conn.stats());
321             break;
322         }
323     }
324 }
325 
hex_dump(buf: &[u8]) -> String326 fn hex_dump(buf: &[u8]) -> String {
327     let vec: Vec<String> = buf.iter().map(|b| format!("{:02x}", b)).collect();
328 
329     vec.join("")
330 }
331