Author: Vigorously intelligent server team HSF

The introduction

HTTP protocol can be seen everywhere in our daily life. No matter we use computers, mobile phones, tablets, browsers or mobile apps, there is always HTTP protocol behind the transmission of the content we browse. Among THE HTTP protocols, HTTP/1.1 is the most widely used at present. However, HTTP/1.1 also has its own shortcomings and deficiencies. HTTP/2 protocol has been extensively optimized on the basis of HTTP/1.1 protocol and was officially released in 2015. There are more and more services that support HTTP/2. We vigorously intelligent server team on THE HTTP/2 protocol and the principle of some analysis and capture the actual combat, I hope this article can help readers to have a certain understanding of the HTTP/2 protocol foundation.

What’s wrong with HTTP/1.1?

Unable to adapt to the continuous increase in the number and volume of Internet page content

After 2010, with the booming development of mobile Internet, the number and volume of resources on web pages have increased significantly. However, the concurrency of browser requests under the same domain name is limited, which leads to the increase of the latency of web page rendering.

The chart below shows the average number and volume of resources per page on the Internet from 2011 to 2015.

For example, the following figure shows the result of a packet capture on a news page. It can be seen that 242 requests are sent when all loads are completed, with 7.2 MB of resources, and the total completion time reaches 23.77 seconds.

Why do browsers limit the concurrency of requests within the same domain name?

  • For client browsers, excessive concurrency involves the number of ports consumed and the overhead of switching between threads. For example, there is a performance overhead that cannot be ignored if 242 threads are used concurrently to request resources, as shown in the news page above.

  • HTTP/1.1 uses the TCP Keep Alive property to reuse the existing TCP connection, wait for the server to return the data and then reuse the TCP connection to send the next HTTP request. Already much faster than HTTP/1.0 disconnecting TCP connections on every request.

  • Even if the client does not restrict concurrency, sending all requests to the server at once is likely to trigger the server’s flow control policy and be restricted.

Here’s how much concurrency is limited by browser:

The TCP connection resources are not fully utilized

TCP is a full-duplex protocol. However, in HTTP/1.1, a TCP connection can only complete one HTTP request at a time, that is, the half-duplex mode of questioning and answering, which fails to maximize bandwidth resources. The current network environment is mostly long fertilizer network, that is, the network with large latency * bandwidth. We can imagine two long and thick water pipes. At one moment, the water flowing in the pipes is a TCP message in flight. Ideally, the two pipes are continuously transmitting water to each other.

The large proportion of headers to common data

The stateless nature of HTTP/1.1 brings huge HTTP headers and public parameters, and each request needs to carry a lot of header information and public parameters. For example, the following table shows the statistics of an HTTP/1.1 request for an App, in which the public participation header data accounts for more than 90% of the total request data. It is likely that the data will not change during the user’s use, which is common in many applications.

content The number of bytes The proportion
Header 2559 73.6%
Query 672 19.3%
Body 245 7.1%
A total of 3476 100%

First experience of HTTP/2 speed

Readers can visit the website: http2.akamai.com/demo to get an intuitive sense of HTTP/2’s speed improvement over HTTP/1.1.

As you can see, during the HTTP/1.1 request, the browser used a total of six concurrent TCP links to request the image data (complying with Chrome’s concurrency limit of six mentioned above), and there was a significant queuing delay in the HTTP/1.1 request Waterfall for many of the images.

HTTP/2 uses multiplexing to transfer data over only one TCP connection, but it is still significantly faster.

What improvements have been made to HTTP/2?

Setup of an HTTP/2 connection

Similar to Websocket, HTTP/2 is based on HTTP/1.1 and can be formally established to send and receive data only after the handshake through protocol upgrade. In addition, the HTTP/2 protocol uses TLS for data encryption by default, whereas H2C indicates that the plaintext protocol is used without TLS.

To facilitate the following packet capture analysis, we use Wireshark to capture packets on the NGHttp2.org page that supports H2C plaintext protocol data, and run the following command to send HTTP/2 requests:

curl --http2 -v http://nghttp2.org 
Copy the code

Readers can use the Chrome plug-ins to check whether the current access page support HTTP/2:chrome.google.com/webstore/de… .

The process of packet capture in THE H2C upgrade is as follows:

  1. The client first sends an HTTP/1.1 protocol packet, where “Connection: Upgrade” in the Header indicates that the protocol is to be upgraded, specifically to “Upgrade: H2C “.

  1. When the server receives the request, the status code in the return packet is 101 Switching Protocols. The “Upgrade: H2C” in the Header indicates that the server supports HTTP/2 H2C. Let’s use HTTP/2 for data transfer.

  1. After the client will send a fixed Magic message to the server, its content is fixed: PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n, after the two sides will use HTTP/2 related protocols for communication.

The head of compression

As mentioned in question 3 of HTTP/1.1, headers in an HTTP packet typically carry a number of fixed Header fields such as “User Agent”, “accept-encoding”, “Accept”, The Header data is often hundreds or even thousands of bytes, but the request body is often tens or hundreds of bytes. In addition, for a user, most of the requests they make have many field values that are repeated every time. For example, in many apps, the common participation Header for each request is basically the same, which leads to a lot of bandwidth consumption on these very repetitive data.

As a result, HTTP/2 focused on header compression, but instead of using the traditional string compression algorithm, HTTP/2 developed a dedicated HPACK algorithm. Why develop a new algorithm? Because the traditional string compression algorithm did not consider many times requested data between client and server interaction data a large number of repeated problems, if through the establishment of two on both ends of the client and the server as “dictionary” to store the Header of the Key – Value data, so the next time when using this data just transmission index number, In addition, the Huffman code is used to further compress the integer and string, and the whole can reach a high compression rate of 50%~90%.

HPACK is a stateful algorithm that requires both the client and server to maintain an index table. HTTP/2 also translates the method name, request path, status code, and so on in the starting line of HTTP/1.1 into header fields, naming them: Pseudo-header fields, preceded by a “:”, such as “:method” and “:status” to indicate the request method and status code, respectively.

For those header fields most commonly used in HTTP/1.1, HTTP/2 defines a read-only static table: httpwg.org/specs/rfc75… For example, index 3 represents the POST method and index 8 represents the status code 200.

.

You can see that a total of 61 static table contents are defined. Part of the definition is the KV pair definition (for example, no. 8 definition: Status 200), and part only has Key definition (for example, no. 19 definition: Accept). For contents with only Key definition, its Value is usually unenumerable and needs to be filled in dynamically.

What about static tables with no corresponding values, or user-defined fields? The protocol specifies the use of a dynamic table to process this data, which is added after the static table and is updated in the dictionaries of both parties during transmission. As more requests are sent over the HTTP/2 connection, there will be more key-values in the “dictionary” on both sides, and eventually each header field of the request will become an index number of one or two bytes. In HTTP/1.1, thousands of bytes of header data can be represented in dozens of bytes. That’s why you don’t use traditional string compression algorithms.

In addition, strings are transmitted in uncompressed plaintext in HTTP/1.1, whereas HTTP/2 supports the encoding of these strings using Huffman, with a flag bit indicating whether they are ASCII or Huffman encoded. The students who have learned Huffman coding know that the coding results need a static mapping table to decode, so the standard setters collected a huge amount of requests on the Internet, used massive data to carry out Huffman coding statistics, and developed a Huffman coding table: httpwg.org/specs/rfc75… , and here’s a partial screenshot:

Binary message format

When HTTP/1.1 transmits data in plain text, the Wireshark and Tcpdump can be used to capture packets. HTTP/2, however, no longer uses plaintext and is entirely in binary format. This is not friendly to human reading, but it is convenient for computers to parse and code writing. HTTP/2 divides the original header and message body into a number of binary frames, and defines various Frame types. HEADERS frames are used to store header DATA, and DATA frames are used to store request body DATA.

The structure of the frame

An HTTP/2 frame looks like this:

+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | Type (8) | Flags (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============================================================+ | Frame Payload (0...) . +---------------------------------------------------------------+Copy the code
  • Length: 3 bytes, indicating the data Length of the frame (excluding the 9 bytes of the header).
  • Type: 1 byte, indicating the Type of the content following the frame. (There are 10 types of frames defined, which can be roughly divided into data frames and control frames.)
  • Flag: 1 byte. The definition varies with Type.
  • R: one reserved bit, currently undefined, and must be 0.
  • Stream Identifier: The ID of the “Stream” to which the frame belongs (the concept of a Stream will be explained later). This ID is used by the receiver to identify a sequence of frames with the same Stream ID from a random sequence of frames and reassemble them in that order to achieve a virtual “Stream”.
  • Frame Payload: Frame Payload corresponding to this Type.

The most common DATA and HEADER frames are analyzed below.

The DATA frame format

An HTTP/2 DATA frame is structured as follows:

 +---------------+
 |Pad Length? (8)|
 +---------------+-----------------------------------------------+
 |                            Data (\*)                         ...
 +---------------------------------------------------------------+
 |                           Padding (\*)                       ...
 +---------------------------------------------------------------+
Copy the code
  • Pad Length: Padding Length of the data,? Indicates that the presence of this field is conditional, and that the value is present only when the Present flag is set.
  • Data: Data passed.
  • Padding: Padding bytes that have no specific semantics and must all be set to 0 to obfuscate the packet length.

An example of packet capture:

  1. The data length for this frame is 62 bytes.
  2. The type is 0, which is a Data frame.
  3. Flags: The lowest bit being 1 indicates that data has been sent for the stream (equivalent to the Chunked end of HTTP/1.1 Chunked flag 0\r\n\r\n being set), and Padded False being set indicates that no bytes are Padded.
  4. The Stream Identifier is 1, indicating the Stream number 1.
  5. Data content:
User-agent: \*
Disallow: 

Sitemap: //nghttp2.org/sitemap.xml 
Copy the code

HEADERS frame format

An HTTP/2 HEADERS frame has the following structure:

 +---------------+
 |Pad Length? (8)|
 +-+-------------+-----------------------------------------------+
 |E|                 Stream Dependency? (31)                     |
 +-+-------------+-----------------------------------------------+
 |  Weight? (8)  |
 +-+-------------+-----------------------------------------------+
 |                   Header Block Fragment (\*)                 ...
 +---------------------------------------------------------------+
 |                           Padding (\*)                       ...
 +---------------------------------------------------------------+
Copy the code
  • Pad Length: Padding Length of the data,? Indicates that the presence of this field is conditional, and that the value is present only when the Present flag is set.
  • E: Stream dependencies are exclusive and only exist if the PRIORITY flag is set.
  • Stream Dependency: Indicates the ID of the Stream on which the current Stream depends. This ID exists only if the PRIORITY flag is set.
  • Weight: stream PRIORITY Weight (1 to 256). If it exists, the PRIORITY flag is set.
  • Header Block Fragment: Indicates the Header Block Fragment.
  • Padding: Padding bytes that have no semantics and must all be set to 0.
How the Header is encoded

So let’s think about, what’s the composition of the data for the Header?

  1. Keys and values can already be indexed.
  2. Keys are encoded indexed, while values are encoded literally.
  3. Keys and values are encoded literally.

In addition, for dynamic content, you need to control whether to enter the dynamic table:

  1. The dynamic table is displayed for subsequent transmission optimization.
  2. The dynamic table is not entered.
  3. Do not enter a dynamic table, and agree that this header will never enter a dynamic table.

Packet capture analysis is performed in the following two simplest cases:

Static tables have indexes

Both Key and Value are in the index table (including static table and dynamic table). The encoding mode is as follows: The first digit is 1, and the other 7 digits are index number:

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 1 |        Index (7+)         |
+---+---------------------------+
Copy the code

An example of packet capture:

Header: :status: 200 OK: 1000 1000

  1. The 1 above identifies the header as an existing KV in the static table.
  2. To review the previous static table contents, “:status: 200 OK” has a static table encoding of 8, which is 1000.

So all of this is going to add up to 1,000, 1,000.

The Key is already in the index table, and the Value needs to be encoded and passed and added to the dynamic table

The encoding mode is as follows:

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 1 |      Index (6+)       |
+---+---+-----------------------+
| H |     Value Length (7+)     |
+---+---------------------------+
| Value String (Length octets)  |
+-------------------------------+
Copy the code
  1. The first two bits pass 01, and the next six bits pass the index number.
  2. A bit of H is followed by a 7-bit length of Value.
  3. Value Indicates the data content.

An example of packet capture:

If we look at the data content, the first two bits are 01, which is “name in index table, value needs to be encoded to pass”, followed by 10 0001, which is 33. So what does 33 correspond to in static table?

The content with index 33 is “data”. The binary value is 10 0001, preceded by 01, that is, 0110 0001.

After that, the flag bit H for Huffman encoding or not is found to be 1, that is, the following string contents need to be decoded using Huffman. The length of the data displayed in the Wireshark is 29 bytes. The length of the data displayed in the Wireshark is the length of the Huffman decoded data, and the length of the undecoded data is the second byte, which is 1001 0110 bytes. The first byte is the Huffman encoding and the length is 1 0110 bytes.

Again, verify whether the following data is the corresponding content of the Huffman static table mentioned above. The first character of the content is S, and the Huffman encoding of S obtained by looking up the table corresponds to the 7-bit 1101110, which exactly corresponds to the first 7 bits of the content of the third byte.

Similarly, look up the table and find that the Huffman of the next character U is 101101, which corresponds to the following data.

Virtual flow

HTTP/2 defines the concept of a “Stream”. Each Stream has a unique ID within the HTTP/2 connection. A Stream is a binary bidirectional transmission channel, and all round trips of the same message are in a unique Stream. What is transmitted is a sequence of data frames (because TCP packets are sequential, and both the client and the server can send messages in correct order), and these data frames are assembled in sequence to form the request and response packets in HTTP/1.1.

So HTTP/2 can use a stream to simultaneously transmit message frames with different stream ids over a TCP connection. This is known as multiplexing, where multiple round trips are multiplexed by a SINGLE TCP connection.

At the stream level, a message is an ordered sequence of frames, while at the TCP connection level, a message is a frame sent or received out of order. There is no order between multiple requests and responses, so there is no queue in the browser, and there is no queue head blocking, which reduces latency and improves connection utilization.

For example, in the following captured packets, there are three streams labeled 0, 1, and 3.

The server actively pushes data

In HTTP/1.1, the server cannot actively push data to the client. In HTTP/2, server push is supported. As shown in the following figure, the index. HTML relies on some. CSS data. In HTTP/1.1, the browser needs to parse the HTML and find that it relies on some. CSS, and then send an HTTP/1.1 request to obtain the data. In HTTP/2, when the server realizes that it relies on some. CSS before sending index.html, it will proactively push a PROMISE frame to notify the client that some. CSS data is coming.

As shown in the figure below: The client is notified in flow 1 that a CSS resource is coming, and the CSS resource is sent in flow 2, which can be concurrent with flow 1.

conclusion

HTTP/2 is semantically compatible with HTTP/1.1 and is upgraded from HTTP/1.1 to HTTP/2 using a protocol upgrade. HTTP/2 uses header compression, binary frame structures, virtual streams, and other methods to optimize performance, and supports server-side push. In addition, HTTP/2 adds security, requiring the TLS 1.2 protocol by default. According to W3Techs, as of June 2019, 36.5 percent of websites worldwide supported HTTP/2.

The service side position is hot recruitment, please click on the service side (senior) development engineer for details.