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 socket.connect(peer_addr).unwrap();
71
72 let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
73 poll.register(
74 &socket,
75 mio::Token(0),
76 mio::Ready::readable(),
77 mio::PollOpt::edge(),
78 )
79 .unwrap();
80
81 // Create the configuration for the QUIC connection.
82 let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
83
84 // *CAUTION*: this should not be set to `false` in production!!!
85 config.verify_peer(false);
86
87 config
88 .set_application_protos(quiche::h3::APPLICATION_PROTOCOL)
89 .unwrap();
90
91 config.set_max_idle_timeout(5000);
92 config.set_max_udp_payload_size(MAX_DATAGRAM_SIZE as u64);
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 // Create a QUIC connection and initiate handshake.
108 let mut conn = quiche::connect(url.domain(), &scid, &mut config).unwrap();
109
110 info!(
111 "connecting to {:} from {:} with scid {}",
112 peer_addr,
113 socket.local_addr().unwrap(),
114 hex_dump(&scid)
115 );
116
117 let write = conn.send(&mut out).expect("initial send failed");
118
119 while let Err(e) = socket.send(&out[..write]) {
120 if e.kind() == std::io::ErrorKind::WouldBlock {
121 debug!("send() would block");
122 continue;
123 }
124
125 panic!("send() failed: {:?}", e);
126 }
127
128 debug!("written {}", write);
129
130 let h3_config = quiche::h3::Config::new().unwrap();
131
132 // Prepare request.
133 let mut path = String::from(url.path());
134
135 if let Some(query) = url.query() {
136 path.push('?');
137 path.push_str(query);
138 }
139
140 let req = vec![
141 quiche::h3::Header::new(":method", "GET"),
142 quiche::h3::Header::new(":scheme", url.scheme()),
143 quiche::h3::Header::new(":authority", url.host_str().unwrap()),
144 quiche::h3::Header::new(":path", &path),
145 quiche::h3::Header::new("user-agent", "quiche"),
146 ];
147
148 let req_start = std::time::Instant::now();
149
150 let mut req_sent = false;
151
152 loop {
153 poll.poll(&mut events, conn.timeout()).unwrap();
154
155 // Read incoming UDP packets from the socket and feed them to quiche,
156 // until there are no more packets to read.
157 'read: loop {
158 // If the event loop reported no events, it means that the timeout
159 // has expired, so handle it without attempting to read packets. We
160 // will then proceed with the send loop.
161 if events.is_empty() {
162 debug!("timed out");
163
164 conn.on_timeout();
165
166 break 'read;
167 }
168
169 let len = match socket.recv(&mut buf) {
170 Ok(v) => v,
171
172 Err(e) => {
173 // There are no more UDP packets to read, so end the read
174 // loop.
175 if e.kind() == std::io::ErrorKind::WouldBlock {
176 debug!("recv() would block");
177 break 'read;
178 }
179
180 panic!("recv() failed: {:?}", e);
181 },
182 };
183
184 debug!("got {} bytes", len);
185
186 // Process potentially coalesced packets.
187 let read = match conn.recv(&mut buf[..len]) {
188 Ok(v) => v,
189
190 Err(e) => {
191 error!("recv failed: {:?}", e);
192 continue 'read;
193 },
194 };
195
196 debug!("processed {} bytes", read);
197 }
198
199 debug!("done reading");
200
201 if conn.is_closed() {
202 info!("connection closed, {:?}", conn.stats());
203 break;
204 }
205
206 // Create a new HTTP/3 connection once the QUIC connection is established.
207 if conn.is_established() && http3_conn.is_none() {
208 http3_conn = Some(
209 quiche::h3::Connection::with_transport(&mut conn, &h3_config)
210 .unwrap(),
211 );
212 }
213
214 // Send HTTP requests once the QUIC connection is established, and until
215 // all requests have been sent.
216 if let Some(h3_conn) = &mut http3_conn {
217 if !req_sent {
218 info!("sending HTTP request {:?}", req);
219
220 h3_conn.send_request(&mut conn, &req, true).unwrap();
221
222 req_sent = true;
223 }
224 }
225
226 if let Some(http3_conn) = &mut http3_conn {
227 // Process HTTP/3 events.
228 loop {
229 match http3_conn.poll(&mut conn) {
230 Ok((stream_id, quiche::h3::Event::Headers { list, .. })) => {
231 info!(
232 "got response headers {:?} on stream id {}",
233 list, stream_id
234 );
235 },
236
237 Ok((stream_id, quiche::h3::Event::Data)) => {
238 if let Ok(read) =
239 http3_conn.recv_body(&mut conn, stream_id, &mut buf)
240 {
241 debug!(
242 "got {} bytes of response data on stream {}",
243 read, stream_id
244 );
245
246 print!("{}", unsafe {
247 std::str::from_utf8_unchecked(&buf[..read])
248 });
249 }
250 },
251
252 Ok((_stream_id, quiche::h3::Event::Finished)) => {
253 info!(
254 "response received in {:?}, closing...",
255 req_start.elapsed()
256 );
257
258 conn.close(true, 0x00, b"kthxbye").unwrap();
259 },
260
261 Ok((_flow_id, quiche::h3::Event::Datagram)) => (),
262
263 Ok((goaway_id, quiche::h3::Event::GoAway)) => {
264 info!("GOAWAY id={}", goaway_id);
265 },
266
267 Err(quiche::h3::Error::Done) => {
268 break;
269 },
270
271 Err(e) => {
272 error!("HTTP/3 processing failed: {:?}", e);
273
274 break;
275 },
276 }
277 }
278 }
279
280 // Generate outgoing QUIC packets and send them on the UDP socket, until
281 // quiche reports that there are no more packets to be sent.
282 loop {
283 let write = match conn.send(&mut out) {
284 Ok(v) => v,
285
286 Err(quiche::Error::Done) => {
287 debug!("done writing");
288 break;
289 },
290
291 Err(e) => {
292 error!("send failed: {:?}", e);
293
294 conn.close(false, 0x1, b"fail").ok();
295 break;
296 },
297 };
298
299 if let Err(e) = socket.send(&out[..write]) {
300 if e.kind() == std::io::ErrorKind::WouldBlock {
301 debug!("send() would block");
302 break;
303 }
304
305 panic!("send() failed: {:?}", e);
306 }
307
308 debug!("written {}", write);
309 }
310
311 if conn.is_closed() {
312 info!("connection closed, {:?}", conn.stats());
313 break;
314 }
315 }
316 }
317
hex_dump(buf: &[u8]) -> String318 fn hex_dump(buf: &[u8]) -> String {
319 let vec: Vec<String> = buf.iter().map(|b| format!("{:02x}", b)).collect();
320
321 vec.join("")
322 }
323