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