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 quiche::h3::NameValue;
33 
34 use ring::rand::*;
35 
36 const MAX_DATAGRAM_SIZE: usize = 1350;
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(quiche::h3::APPLICATION_PROTOCOL)
85         .unwrap();
86 
87     config.set_max_idle_timeout(5000);
88     config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE);
89     config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE);
90     config.set_initial_max_data(10_000_000);
91     config.set_initial_max_stream_data_bidi_local(1_000_000);
92     config.set_initial_max_stream_data_bidi_remote(1_000_000);
93     config.set_initial_max_stream_data_uni(1_000_000);
94     config.set_initial_max_streams_bidi(100);
95     config.set_initial_max_streams_uni(100);
96     config.set_disable_active_migration(true);
97 
98     let mut http3_conn = None;
99 
100     // Generate a random source connection ID for the connection.
101     let mut scid = [0; quiche::MAX_CONN_ID_LEN];
102     SystemRandom::new().fill(&mut scid[..]).unwrap();
103 
104     let scid = quiche::ConnectionId::from_ref(&scid);
105 
106     // Get local address.
107     let local_addr = socket.local_addr().unwrap();
108 
109     // Create a QUIC connection and initiate handshake.
110     let mut conn =
111         quiche::connect(url.domain(), &scid, local_addr, peer_addr, &mut config)
112             .unwrap();
113 
114     info!(
115         "connecting to {:} from {:} with scid {}",
116         peer_addr,
117         socket.local_addr().unwrap(),
118         hex_dump(&scid)
119     );
120 
121     let (write, send_info) = conn.send(&mut out).expect("initial send failed");
122 
123     while let Err(e) = socket.send_to(&out[..write], send_info.to) {
124         if e.kind() == std::io::ErrorKind::WouldBlock {
125             debug!("send() would block");
126             continue;
127         }
128 
129         panic!("send() failed: {:?}", e);
130     }
131 
132     debug!("written {}", write);
133 
134     let h3_config = quiche::h3::Config::new().unwrap();
135 
136     // Prepare request.
137     let mut path = String::from(url.path());
138 
139     if let Some(query) = url.query() {
140         path.push('?');
141         path.push_str(query);
142     }
143 
144     let req = vec![
145         quiche::h3::Header::new(b":method", b"GET"),
146         quiche::h3::Header::new(b":scheme", url.scheme().as_bytes()),
147         quiche::h3::Header::new(
148             b":authority",
149             url.host_str().unwrap().as_bytes(),
150         ),
151         quiche::h3::Header::new(b":path", path.as_bytes()),
152         quiche::h3::Header::new(b"user-agent", b"quiche"),
153     ];
154 
155     let req_start = std::time::Instant::now();
156 
157     let mut req_sent = false;
158 
159     loop {
160         poll.poll(&mut events, conn.timeout()).unwrap();
161 
162         // Read incoming UDP packets from the socket and feed them to quiche,
163         // until there are no more packets to read.
164         'read: loop {
165             // If the event loop reported no events, it means that the timeout
166             // has expired, so handle it without attempting to read packets. We
167             // will then proceed with the send loop.
168             if events.is_empty() {
169                 debug!("timed out");
170 
171                 conn.on_timeout();
172 
173                 break 'read;
174             }
175 
176             let (len, from) = match socket.recv_from(&mut buf) {
177                 Ok(v) => v,
178 
179                 Err(e) => {
180                     // There are no more UDP packets to read, so end the read
181                     // loop.
182                     if e.kind() == std::io::ErrorKind::WouldBlock {
183                         debug!("recv() would block");
184                         break 'read;
185                     }
186 
187                     panic!("recv() failed: {:?}", e);
188                 },
189             };
190 
191             debug!("got {} bytes", len);
192 
193             let recv_info = quiche::RecvInfo {
194                 to: local_addr,
195                 from,
196             };
197 
198             // Process potentially coalesced packets.
199             let read = match conn.recv(&mut buf[..len], recv_info) {
200                 Ok(v) => v,
201 
202                 Err(e) => {
203                     error!("recv failed: {:?}", e);
204                     continue 'read;
205                 },
206             };
207 
208             debug!("processed {} bytes", read);
209         }
210 
211         debug!("done reading");
212 
213         if conn.is_closed() {
214             info!("connection closed, {:?}", conn.stats());
215             break;
216         }
217 
218         // Create a new HTTP/3 connection once the QUIC connection is established.
219         if conn.is_established() && http3_conn.is_none() {
220             http3_conn = Some(
221                 quiche::h3::Connection::with_transport(&mut conn, &h3_config)
222                 .expect("Unable to create HTTP/3 connection, check the server's uni stream limit and window size"),
223             );
224         }
225 
226         // Send HTTP requests once the QUIC connection is established, and until
227         // all requests have been sent.
228         if let Some(h3_conn) = &mut http3_conn {
229             if !req_sent {
230                 info!("sending HTTP request {:?}", req);
231 
232                 h3_conn.send_request(&mut conn, &req, true).unwrap();
233 
234                 req_sent = true;
235             }
236         }
237 
238         if let Some(http3_conn) = &mut http3_conn {
239             // Process HTTP/3 events.
240             loop {
241                 match http3_conn.poll(&mut conn) {
242                     Ok((stream_id, quiche::h3::Event::Headers { list, .. })) => {
243                         info!(
244                             "got response headers {:?} on stream id {}",
245                             hdrs_to_strings(&list),
246                             stream_id
247                         );
248                     },
249 
250                     Ok((stream_id, quiche::h3::Event::Data)) => {
251                         while let Ok(read) =
252                             http3_conn.recv_body(&mut conn, stream_id, &mut buf)
253                         {
254                             debug!(
255                                 "got {} bytes of response data on stream {}",
256                                 read, stream_id
257                             );
258 
259                             print!("{}", unsafe {
260                                 std::str::from_utf8_unchecked(&buf[..read])
261                             });
262                         }
263                     },
264 
265                     Ok((_stream_id, quiche::h3::Event::Finished)) => {
266                         info!(
267                             "response received in {:?}, closing...",
268                             req_start.elapsed()
269                         );
270 
271                         conn.close(true, 0x00, b"kthxbye").unwrap();
272                     },
273 
274                     Ok((_stream_id, quiche::h3::Event::Reset(e))) => {
275                         error!(
276                             "request was reset by peer with {}, closing...",
277                             e
278                         );
279 
280                         conn.close(true, 0x00, b"kthxbye").unwrap();
281                     },
282 
283                     Ok((_flow_id, quiche::h3::Event::Datagram)) => (),
284 
285                     Ok((_, quiche::h3::Event::PriorityUpdate)) => unreachable!(),
286 
287                     Ok((goaway_id, quiche::h3::Event::GoAway)) => {
288                         info!("GOAWAY id={}", goaway_id);
289                     },
290 
291                     Err(quiche::h3::Error::Done) => {
292                         break;
293                     },
294 
295                     Err(e) => {
296                         error!("HTTP/3 processing failed: {:?}", e);
297 
298                         break;
299                     },
300                 }
301             }
302         }
303 
304         // Generate outgoing QUIC packets and send them on the UDP socket, until
305         // quiche reports that there are no more packets to be sent.
306         loop {
307             let (write, send_info) = match conn.send(&mut out) {
308                 Ok(v) => v,
309 
310                 Err(quiche::Error::Done) => {
311                     debug!("done writing");
312                     break;
313                 },
314 
315                 Err(e) => {
316                     error!("send failed: {:?}", e);
317 
318                     conn.close(false, 0x1, b"fail").ok();
319                     break;
320                 },
321             };
322 
323             if let Err(e) = socket.send_to(&out[..write], send_info.to) {
324                 if e.kind() == std::io::ErrorKind::WouldBlock {
325                     debug!("send() would block");
326                     break;
327                 }
328 
329                 panic!("send() failed: {:?}", e);
330             }
331 
332             debug!("written {}", write);
333         }
334 
335         if conn.is_closed() {
336             info!("connection closed, {:?}", conn.stats());
337             break;
338         }
339     }
340 }
341 
hex_dump(buf: &[u8]) -> String342 fn hex_dump(buf: &[u8]) -> String {
343     let vec: Vec<String> = buf.iter().map(|b| format!("{b:02x}")).collect();
344 
345     vec.join("")
346 }
347 
hdrs_to_strings(hdrs: &[quiche::h3::Header]) -> Vec<(String, String)>348 pub fn hdrs_to_strings(hdrs: &[quiche::h3::Header]) -> Vec<(String, String)> {
349     hdrs.iter()
350         .map(|h| {
351             let name = String::from_utf8_lossy(h.name()).to_string();
352             let value = String::from_utf8_lossy(h.value()).to_string();
353 
354             (name, value)
355         })
356         .collect()
357 }
358