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