1  // Copyright (C) 2018-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  
36  const HTTP_REQ_STREAM_ID: u64 = 4;
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(&[
85              b"hq-interop",
86              b"hq-29",
87              b"hq-28",
88              b"hq-27",
89              b"http/0.9",
90          ])
91          .unwrap();
92  
93      config.set_max_idle_timeout(5000);
94      config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE);
95      config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE);
96      config.set_initial_max_data(10_000_000);
97      config.set_initial_max_stream_data_bidi_local(1_000_000);
98      config.set_initial_max_stream_data_bidi_remote(1_000_000);
99      config.set_initial_max_streams_bidi(100);
100      config.set_initial_max_streams_uni(100);
101      config.set_disable_active_migration(true);
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      // Get local address.
110      let local_addr = socket.local_addr().unwrap();
111  
112      // Create a QUIC connection and initiate handshake.
113      let mut conn =
114          quiche::connect(url.domain(), &scid, local_addr, peer_addr, &mut config)
115              .unwrap();
116  
117      info!(
118          "connecting to {:} from {:} with scid {}",
119          peer_addr,
120          socket.local_addr().unwrap(),
121          hex_dump(&scid)
122      );
123  
124      let (write, send_info) = conn.send(&mut out).expect("initial send failed");
125  
126      while let Err(e) = socket.send_to(&out[..write], send_info.to) {
127          if e.kind() == std::io::ErrorKind::WouldBlock {
128              debug!("send() would block");
129              continue;
130          }
131  
132          panic!("send() failed: {:?}", e);
133      }
134  
135      debug!("written {}", write);
136  
137      let req_start = std::time::Instant::now();
138  
139      let mut req_sent = false;
140  
141      loop {
142          poll.poll(&mut events, conn.timeout()).unwrap();
143  
144          // Read incoming UDP packets from the socket and feed them to quiche,
145          // until there are no more packets to read.
146          'read: loop {
147              // If the event loop reported no events, it means that the timeout
148              // has expired, so handle it without attempting to read packets. We
149              // will then proceed with the send loop.
150              if events.is_empty() {
151                  debug!("timed out");
152  
153                  conn.on_timeout();
154                  break 'read;
155              }
156  
157              let (len, from) = match socket.recv_from(&mut buf) {
158                  Ok(v) => v,
159  
160                  Err(e) => {
161                      // There are no more UDP packets to read, so end the read
162                      // loop.
163                      if e.kind() == std::io::ErrorKind::WouldBlock {
164                          debug!("recv() would block");
165                          break 'read;
166                      }
167  
168                      panic!("recv() failed: {:?}", e);
169                  },
170              };
171  
172              debug!("got {} bytes", len);
173  
174              let recv_info = quiche::RecvInfo {
175                  to: socket.local_addr().unwrap(),
176                  from,
177              };
178  
179              // Process potentially coalesced packets.
180              let read = match conn.recv(&mut buf[..len], recv_info) {
181                  Ok(v) => v,
182  
183                  Err(e) => {
184                      error!("recv failed: {:?}", e);
185                      continue 'read;
186                  },
187              };
188  
189              debug!("processed {} bytes", read);
190          }
191  
192          debug!("done reading");
193  
194          if conn.is_closed() {
195              info!("connection closed, {:?}", conn.stats());
196              break;
197          }
198  
199          // Send an HTTP request as soon as the connection is established.
200          if conn.is_established() && !req_sent {
201              info!("sending HTTP request for {}", url.path());
202  
203              let req = format!("GET {}\r\n", url.path());
204              conn.stream_send(HTTP_REQ_STREAM_ID, req.as_bytes(), true)
205                  .unwrap();
206  
207              req_sent = true;
208          }
209  
210          // Process all readable streams.
211          for s in conn.readable() {
212              while let Ok((read, fin)) = conn.stream_recv(s, &mut buf) {
213                  debug!("received {} bytes", read);
214  
215                  let stream_buf = &buf[..read];
216  
217                  debug!(
218                      "stream {} has {} bytes (fin? {})",
219                      s,
220                      stream_buf.len(),
221                      fin
222                  );
223  
224                  print!("{}", unsafe {
225                      std::str::from_utf8_unchecked(stream_buf)
226                  });
227  
228                  // The server reported that it has no more data to send, which
229                  // we got the full response. Close the connection.
230                  if s == HTTP_REQ_STREAM_ID && fin {
231                      info!(
232                          "response received in {:?}, closing...",
233                          req_start.elapsed()
234                      );
235  
236                      conn.close(true, 0x00, b"kthxbye").unwrap();
237                  }
238              }
239          }
240  
241          // Generate outgoing QUIC packets and send them on the UDP socket, until
242          // quiche reports that there are no more packets to be sent.
243          loop {
244              let (write, send_info) = match conn.send(&mut out) {
245                  Ok(v) => v,
246  
247                  Err(quiche::Error::Done) => {
248                      debug!("done writing");
249                      break;
250                  },
251  
252                  Err(e) => {
253                      error!("send failed: {:?}", e);
254  
255                      conn.close(false, 0x1, b"fail").ok();
256                      break;
257                  },
258              };
259  
260              if let Err(e) = socket.send_to(&out[..write], send_info.to) {
261                  if e.kind() == std::io::ErrorKind::WouldBlock {
262                      debug!("send() would block");
263                      break;
264                  }
265  
266                  panic!("send() failed: {:?}", e);
267              }
268  
269              debug!("written {}", write);
270          }
271  
272          if conn.is_closed() {
273              info!("connection closed, {:?}", conn.stats());
274              break;
275          }
276      }
277  }
278  
hex_dump(buf: &[u8]) -> String279  fn hex_dump(buf: &[u8]) -> String {
280      let vec: Vec<String> = buf.iter().map(|b| format!("{b:02x}")).collect();
281  
282      vec.join("")
283  }
284