QUIC is a new communication protocol, a UDP-based transport protocol that is expected to eventually replace all HTTP requests based on TCP. Anyone familiar with UDP should know why to use QUIC. UDP is characterized by unreliability, frequent loss of packets, reordering, duplication, and so on. UDP does not include the reliability and sequential guarantees of TCP that are strictly required by any higher-level protocol (such as HTTP), and this is where QUIC comes in.

This article briefly introduces what QUIC is, and NodeJs implements a simple example, the GitHub repository.

Introduction of QUIC

To review UDP, UDP (User Datagram Protocol) is a connectionless transport layer protocol in the ISO reference model that provides a transaction-oriented simple unreliable message delivery service. UDP is basically an interface between IP and upper-layer protocols.

The QUIC protocol defines a layer on top of UDP that brings error handling, reliability, flow control, and built-in security (via TLS 1.3) to UDP. In fact, it reimplements most of TCP’s functionality on top of UDP, but with one key difference: Unlike TCP, it can still transmit packets out of order.

QUIC solves this problem by reimplementing basic transport services inside encrypted envelopes, using UDP to cross the Internet. Google first announced Google QUIC eight years ago to use it between Chrome and Google services. This enables them to make improvements independent of the operating system or operating system update schedule.

Google QUIC provides many performance benefits for Chrome, and other companies are starting to contribute to the protocol.

Characteristics of QUIC

No queue head is blocked

Multiple streams connected to a QUIC connection are independent and have no underlying protocol restrictions. Packet loss in one Stream affects only that Stream, and other streams are not affected. For the definition of a header blocking, see TCP Header Blocking.

Flexibility, security, and reduced latency

QUIC introduces a number of other important features:

  • QUIC connections operate independently of the network topology: once a QUIC connection is established, both the source and destination IP addresses and ports can be changed without the need to re-establish the connection. This becomes particularly useful when switching from one type of network to another type of mobile device, such as LTE to WiFi.
  • QUIC connections are secure and encrypted: TLS 1.3 supports direct integration into the protocol, and all QUIC traffic is encrypted.
  • QUIC adds critical flow control and error handling to UDP, and includes important security mechanisms to prevent a range of denial of service attacks.
  • QUIC adds support for zero round-trip HTTP requests: That is, unlike HTTP over TLS over TCP, which requires multiple data exchanges between the client and server to establish a TLS session before any HTTP request data can be transmitted, QUIC allows HTTP request headers to be sent as part of a TLS handshake. QUIC connections are being established, significantly reducing initial latency for new connections.

Applicability and manageability

Encrypted transport protocols have an impact on both the engineer’s choice of transport protocol and network operator monitoring activities.

Applicability of the QUIC transport protocol describes considerations for applications that may wish to use QUIC as a transport layer protocol. The features of QUIC differ from those of TCP in interesting and subtle ways, such as the availability of data until the handshake is complete, or the availability of multiple simultaneous streams.

Manageability of THE QUIC transport protocol describes the impact of the encrypted transport layer on network monitoring and management. Many networks assume that they can examine the state of TCP for signs of network problems (poor performance, high packet loss rates) or traffic abuse (data leaks, attacks).

Browser support

Six years ago, Google introduced QUIC to IETF to start the standard process, and IETF contributors expressed interest in developing a new transport from their experimental protocol, resulting in a proprietary version often referred to as “Google QUIC” or “gQUIC.”

The IETF formed a QUIC working group in 2016 to take Google’s Web-specific implementation and adapt it to a generic transport protocol. This led to HTTP/3, which uses IETF QUIC for transport.

So when considering browser support, take HTTP3 support as the standard:

QUIC implementation

While implementing the new QUIC support, we need to use a new top-level built-in QUIC module to expose the API, but we need to compile it ourselves. In order to avoid the impact on our own development environment, we recommend using Docker. If you are interested, you can view the project Docker-nodejs, which has the complete code and docker runtime.

The QuIC module exposes a createSocket function that creates instances of QuicSocket objects that can be used as quIC servers or clients.

Create a server as follows:

const { createQuicSocket } = require("net"); const { readFileSync } = require("fs"); const port = process.env.NODE_PORT || 3005; // Define the default HTTP port or get const key = readFileSync("./ssl_certs/server.key"); const cert = readFileSync("./ssl_certs/server.crt"); const ca = readFileSync("./ssl_certs/server.csr"); const servername = "localhost"; const alpn = "hello"; Const server = createQuicSocket({endpoint: {port}}); // Create a QUIC UDP IPv4 socket bound to port 3005 const server = createQuicSocket({endpoint: {port}}); // Keys and certificates to protect new connections, using the virtual Hello application protocol. server.listen({ key, cert, alpn }); server.on("session", (session) => { session.on("stream", (stream) => { stream.pipe(stream); }); }); server.on("listening", () => { console.info(`listening on ${port}... `); console.info("input content!" ); });Copy the code

Next, create the client, which emulates the client request in the same file, as follows:

const socket = createQuicSocket({ client: { key, cert, ca, requestCert: true, alpn, servername, }, }); const req = socket.connect({ address: servername, port, }); req.on("secure", () => { const stream = req.openStream(); // stdin -> stream process.stdin.pipe(stream); stream.on("data", (chunk) => console.success("client(on-secure): ", chunk.toString()) ); stream.on("end", () => console.info("client(on-secure): end")); stream.on("close", () => { console.warn("stream is closed!" ); socket.close(); }); stream.on("error", (err) => console.error(err)); });Copy the code

If the local nodeJS version supports QUic, you can start it directly:

npm run start
Copy the code

If not, you are advised to start from Dokcer as follows:

docker run -it --rm  --name quichello  -p 3005:3005  -e NODE_ENV=development  -v $PWD:/data/node/app --entrypoint '/bin/sh'  node-quic  -c 'npm install && npm run start
Copy the code

The running effect is as follows: