Http2 protocol (http2)

  • Establishment of HTTP 2 connections

  • Frame and stream relationships in HTTP 2

  • The secret to traffic saving in HTTP 2: the HPACK algorithm

  • Server Push capability in HTTP 2 protocol

  • Why should HTTP 2 implement traffic control?

  • Problems with THE HTTP 2 protocol

HTTP 2 connection establishment

Contrary to popular belief, the HTTP 2 protocol itself does not require TLS/SSL to be established. In fact, it is possible to establish HTTP 2 connections using ordinary TCP connections. However, for security purposes, all browsers on the market now only support HTTP 2 based on TLS/SSL by default. In short, HTTP 2 built on top of TCP is called H2C, while TLS/SSL is known as H2.

Enter the command:

tcpdump -i eth0 port 80 and host nghttp2.org -w h2c.pcap &
Copy the code

Use curl to access a TCP connection (port 80) using HTTP 2.

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

In fact, the log can also be seen in the process of establishing the connection:

We copied the pCAP file generated by TCPDump to the local PC and opened it with Wireshark to restore the entire HTTP 2 connection packet.

The first is the HTTP 1.1 upgrade to the HTTP 2 protocol

Then the client also needs to send a “magic frame” :

Finally, you need to send a Settings frame:

After that, we’ll take a look at how to set up HTTP 2 connections based on TLS, taking into account encryption and other factors that need to be done in advance. You can download the plugin in Chrome.

And then if you go to any web page and you see the lightning bolt in blue it means that the site supports HTTP 2; Otherwise, it is not supported. The diagram below:

To output information like TLS/SSL from Chrome to a log file, you need to configure additional system variables, as shown in the following figure:

Then configure SSL related Settings in our Wireshark as well.

In this way, all encryption and decryption information is stored in the log file when the browser communicates with the TLS protocol, and the Wireshark uses the log file to decrypt TLS packets.

With that in mind, we can start analyzing the HTTP 2 protocol based on TLS connections. For example, we visited tmall site www.tmall.com/ and opened our Wireshark.

After the TLS connection has been established and the magic and Settings frames continue to be sent, the HTTP 2 connection has been established. Let’s look at the TLS client hello message:

The ALPN protocol information represents which two protocols the client can accept. Server Hello explicitly tells us to use the H2 protocol.

This is one of the most important advantages of HTTP 2 over SPDY: THE SPDY protocol relies heavily on TLS/SSL, leaving the server with no choice. The HTTP 2 protocol carries the ALPN extension when the client makes a request, which means that the client tells the server which protocol it supports when making a request. So I can let the server choose if I want to go to TLS/SSL.

HTTP 2 frame and stream relationship

X protocol, HTTP protocol is composed of one message after another. On the same TCP connection, the response of the previous message does not return. Subsequent messages cannot be sent. In HTTP 2, this restriction is removed and a so-called “message” is defined as a “stream.” The order between streams can be disordered, but the order of frames in a stream cannot be disordered. As shown in figure:

In other words, there can be multiple streams on the same TCP connection, which are composed of frame frames one by one. There is no sequential relationship between streams, but the frames in each stream are in sequence. Note that 135 and other numbers in this figure are actually stream ids. Although WebSocket has the concept of a frame, WebSocket does not have a stream ID, so it has no multiplexing function. HTTP 2 has multiplexing capabilities because of the stream ID. The fact that n streams can exist on a TCP connection means that a server can process n requests concurrently and then respond to them all simultaneously on the same TCP connection. Of course, the ability to transmit n streams over the same TCP connection is limited. The setting frame contains this information when the HTTP 2 connection is established. For example, in the following figure, when visiting the site of Tmall, the message of the setting frame carried by the browser indicates that the maximum concurrent stream supported by the browser, the HTTP 2 client, is 1000.

When the Tmall server returns the response for this setting frame, it tells the browser that the maximum concurrent stream I can support is 128.

At the same time, we should also know that in HTTP 2 protocol, the stream ID is singular, indicating that the stream is initiated by the client, and the even number indicates that the server actively initiates the stream (which can be understood as the server actively pushes the stream).

HTTP 2 traffic saving mystery: HPACK algorithm

The HTTP 2 protocol also offers significant improvements in traffic consumption compared to HTTP 1.x. It is mainly divided into three parts: static dictionary, dynamic dictionary, and Huffman coding. You can install the following tools to explore the traffic savings:

apt-get install nghttp2-client
Copy the code

Then check out some of the sites that have HTTP 2 enabled, and the traffic savings are basically 25 percent or more if you visit them frequently:

In terms of traffic consumption, the biggest improvement over HTTP 1.x is that HTTP 2 can compress HTTP headers. However, gzip and other protocols cannot compress headers in HTTP 1.x, especially for most requests. Actually, the header is the largest percentage.

Let’s start with a static dictionary, as shown below:

This is not difficult to understand, nothing more than we commonly used HTTP headers, with a fixed number, that of course can play a traffic saving role. The important thing to note here is that there are some headers that have more complicated values, and they don’t have a static dictionary for their values. For example, cache-control, the cache control field, has too many values to be solved with a static dictionary, and has to be Huffman encoded. The following figure shows the role of HPACK compression algorithm in saving traffic:

For example, if we look at the 62 header, user-agent refers to the browser. Generally, the header information will not change when we request it. Therefore, after the optimization of HPACK algorithm, we only need to transmit the number 62 to represent its meaning.

Here’s another example:

Similarly, when multiple requests are sent consecutively, in most cases only the path header will change and the rest of the header will remain unchanged. Therefore, based on this scenario, only the PATH header will be transmitted eventually.

Finally, we take a look at the core of the HPACK algorithm: Huffman coding. The core idea of Huffman coding is to use shorter coding for the ones with higher frequency, and longer coding for the ones with lower frequency (SPDY protocol, the predecessor of HTTP 2 protocol, uses dynamic Huffman coding, while HTTP 2 protocol chooses static Huffman coding).

Let’s look at a few examples:

For this header frame, notice the method:get header. If the value of method:get is 2, the value of method:get is 2, the value of method:get is 2, the value of method:get is 2, the value of method:get is 2, the value of method:get is 2, the value of method:get is 2. So this value is 1000, 0010, which in hexadecimal is 0x82.

Let’s look at another set of header examples where key is in the index table and value is not in the index table.

If key is in the index table and value is not in the index table, the following six bits (111010 is 58 in decimal notation) are the static index values. The index value of user-agent is 58 and the two bits starting with 01 are converted into binary 01111010, which is 7A in hexadecimal. Then look at the second byte, 0xD4, 0xD4 translated into binary is 1 101 0100, where the first bit represents the Huffman encoding followed by the following 7 bits. The value of the key-value requires several bytes to represent, This is 101, 0100 is 84 in base 10, which means that the value after the user-agent needs 84 bytes. Let’s count the number of bytes in the figure 16*5+ 4 bytes after the first row d4, which is exactly 84 bytes.

Finally, look at an example where neither key nor value is in the index table.

Server Push capability in HTTP 2

As mentioned above, the biggest improvement of H2 over H1.x protocol is that H2 can simultaneously transmit N streams on the basis of a single TCP connection. Thus avoiding the problem of congestion in the h1.x protocol squadron head. In fact, in most of the front-end pages, we can use the Server Push capability of the H2 protocol to further improve page loading speed. For example, when we use the browser to visit an Html page, only when the Html page is returned to the browser and the browser kernel resolves that there are CSS or JS resources in the Html page, the browser will send the corresponding CSS or JS request. When the CSS and JS come back, the browser will render further. Such a process usually results in the browser being blank for a period of time, which degrades the user experience. With H2, when the browser accesses an Html page to the server, the server can actively push the corresponding CSS and JS content to the browser, eliminating the need for the browser to re-send the CSS and JS request later.

Some people misinterpret Server Push as a technology that allows the Server to send “notifications” to the browser, and even compare it to WebSockets. Server Push simply saves the browser the process of sending the request. The browser will only use the pushed content if “if you don’t push this resource, the browser will request this resource.” Otherwise, pushing a resource is a waste of bandwidth if the browser doesn’t already request it. Of course, if the server is communicating with a client rather than a browser, then the HTTP 2 protocol can do the push function naturally. Therefore, in the case of both using HTTP 2 protocol, whether the client or browser is communicating with the server is functionally different.

To demonstrate this process, let’s write a piece of code. Given that the browser must establish a TLS connection to access the HTTP 2 site, we first generate the corresponding certificate and secret key.

Then turn on HTTP 2 and actively push the CSS file referenced in the Html when an Html request is received.

package main import ( "fmt" "net/http" "github.com/labstack/echo" ) func main() { e := echo.New() e.Static("/", "HTML ") // verify whether the http2 environment is enabled successfully. LLDB ("/request", func(c echo.context) error {req := c.equest () format := '<code>
            Protocol: %s<br>
            Host: %s<br>
            Remote Address: %s<br>
            Method: %s<br>
            Path: %s<br>
          </code>` return c.HTML(http.StatusOK, fmt.Sprintf(format, req.Proto, req.Host, req.RemoteAddr, req.Method, Req.url.path))}) // Push the CSS file referenced in HTML while receiving the HTML request, TLLDB ("/h2.html", func(c echo.Context) (err error) {TLLDB (' /h2.html ') {TLLDB (' c echo. ok := c.Response().Writer.(http.Pusher) if ok { if err = pusher.Push("/app.css", nil); err ! = nil { println("error push") return } } return c.File("html/h2.html") }) // e.StartTLS(":1323", "cert.pem", "key.pem") }Copy the code

Then when Chrome visits the page, look at the NetWork panel:

You can see that this CSS file was pushed by us. Take a look at Wireshark.

A push_PROMISE frame is sent by the server to the browser. See if the server sends a push_PROMISE frame to the browser.

You can see that this frame is used to tell the browser which resource I’m actively pushing to you, and the stream id of this resource is 6. In the figure, we can also see that there is a data with stream-ID 6 being transferred. This is the CSS file pushed by the server actively. At this point, a complete Server Push interaction is complete.

But the challenge of actually applying Server Push online is far more complex than what we’ve shown in this demo. The first is that most CDN vendors (unless they build their own CDN) have limited support for Server Push. We can not make every resource request directly to our source server, most static resources are front-loaded in THE CDN. Secondly, for static resources, we also need to consider the impact of cache. If the browser sends a static resource request by itself, the browser can decide whether it really needs to request the resource according to the cache status, while the Server Push is initiated by the Server. The server most often does not know if the cache of this resource is expired. Of course, after the browser receives a push Promise frame, it can query its cache status and send an RST_STREAM frame, telling the server that I have cached the resource and I don’t need to send any more, but there’s no way to guarantee that when the RST_STREAM reaches the server, The data frame pushed by the server has not been sent yet. So there will still be a certain amount of bandwidth waste. In general, Server Push is a very effective way to improve the front-end user experience. The idle performance indicators of browsers can be improved 3-5 times with Server Push (after all, browsers don’t have to wait to parse Html before requesting CSS and JS).

Why should HTTP 2 implement traffic control?

Many people wonder why our application layer, HTTP 2, should implement traffic control when the TCP transport layer already implements traffic control. Let’s look at a picture.

In HTTP 2, because we support multiplexing, which means we can send multiple streams simultaneously in the same TCP connection. In the figure above, each color represents a stream. You can see that we have four streams in total, and each stream has n frames. If multiplexing is used in the application layer, n frames will be continuously sent to the target server at the same time. At this time, the traffic peak will trigger TCP congestion control, which will block all subsequent frames, resulting in slow response of the server. HTTP 1.x does not have this problem because it does not support multiplexing. And we have mentioned many times before, a request from the client to the server side through a lot of proxy servers, the size of the proxy server memory and network conditions may be different, so in the application layer to do a flow control to avoid triggering TCP flow control is very necessary. Traffic control policies in HTTP 2 comply with the following principles:

  1. Both clients and servers have traffic control capabilities.

  2. The sender and receiver can set the flow control capability independently.

  3. Only data frames need flow control, not header frames, push promise frames, etc.

  4. The flow control function applies only to both ends of the TCP connection. Even if there is a proxy server in the middle, the flow control function is not transparently transmitted to the source server.

Visit zhihu’s site to see the packet capture.

These identifying Window_UPDATE frames are called stream frames. So if we click on a random one and look at it, we can see what the flow control frame is telling us.

As smart as you can imagine, HTTP 2 can do flow control, so it can also do prioritization. For example in the HTTP 1 x in the agreement, we visit an Html page, there will be JS and CSS) resources such as images, we send these requests at the same time, but the request is not the concept of priority, who first go out who first come back is unknown (because you don’t know whether these CSS and JS requests on the same TCP connection, It’s not always clear which is fast and which is slow, since it’s spread across different TCP), but from a user experience perspective, make sure CSS takes priority, then JS, and then images, which greatly reduces the amount of time a browser spends on a white screen. This capability is implemented in HTTP 2. For example, we visit sina’s site, and then capture the package can see:

We can look at the priority of the CSS frame:

The priority of JS

Finally, the priority of the GIF image is the lowest.

With the weight keyword to identify priority, the server knows which requests need to be responded to first and which requests can be sent later. The overall browser experience will be better.

Six, HTTP 2 protocol encountered problems

The HTTP 2 protocol based on TCP or TCP+TLS still encounters many problems, such as: If HTTP 2 protocol is based on TCP, at least three handshakes are required. If HTTP 2 protocol is TCP+TLS, in addition to THE TCP handshake, TLS must undergo multiple handshakes (TLS1.3 can achieve only one handshake). Each handshake needs to send a packet and receive the ACK of the packet before the next handshake. In the weak network environment, it can be imagined that the efficiency of establishing this connection is extremely low. In addition, TCP’s inherent header congestion problem has been plaguing HTTP 21.x and HTTP 2 protocols. Take a look at Google’s spDY promotional image to better understand the nature of congestion:

Figure 1 is easy to understand. We send three streams at the same time with the support of multiplexing, and then send them to the server through TCP/IP, and then TCP sends these packets to our application layer. Note that there is a condition here that the order of sending packets should be consistent with the order of receiving packets. As you can see in the figure above, the order of the squares is the same. But if, for example, the first red packet is lost, the subsequent packets cannot be immediately transmitted to our application layer protocol even if they reach the server machine. According to the TCP protocol, the sequence of receiving and sending packets must be the same. If the red packets are lost, the subsequent packets are blocked in the server until the red packets are successfully sent to the server. Then the packets are transferred to the application layer protocol.

TCP protocol in addition to some of the above defects, there is another problem is that the implementation of TCP protocol is at the level of the operating system, any of our languages, including Java, C, C++, Go and so on exposed to the so-called Socket programming interface implementer is actually the operating system itself. It is very, very difficult for the operating system to upgrade the IMPLEMENTATION of TCP itself, and it is unrealistic for so many devices in the entire Internet to upgrade TCP as a whole (IPV6 is also slow to upgrade for this reason). Based on the above problems, Google encapsulates a LAYER of QUIC protocol based on UDP (in fact, many udP-based application layer protocols partially implement some functions of TCP on the application layer) to replace THE TCP protocol in HTTP 21. X-http 2.

Let’s turn on the QUIC protocol switch in Chrome:

Then visit YouTube (which is actually supported by China’s B site).

You can see that the QUIC protocol is already supported. It’s easy to understand why this option is turned off by default in Chrome. The QUIC protocol is actually Google’s own creation and has not yet been officially incorporated into HTTP 3. Everything is still in draft. So this option is turned off by default. What are the main improvements of QUIC protocol compared with the original TCP protocol? In fact, the original queue transmission message is changed to no queue transmission, then naturally there is no queue congestion problem.

In addition, HTTP 3 also provides the ability to change the port number or IP address can also reuse the previous connection, I understand that this protocol support features may be more for the Internet of Things. The IP of many devices in the Internet of Things can change all the time. Being able to reuse previous connections will greatly improve the efficiency of network transmission. In this way, we can avoid the disadvantage that at least 1-3 RTT is required to continue data transmission after reconnecting to the network after disconnection.

Finally, HTTP 2 May not perform as well as HTTP 1.x in an extremely weak network, because there is only one TCP connection under HTTP 2. In a weak network, if the packet loss rate is very high, timeout retransmission at the TCP layer will be triggered continuously, resulting in the backlog of TCP packets, and the packets cannot be transmitted to the upper application layer. However, in HTTP 1.x, because you can use multiple TCP connections, the packet backlog is not as severe as HTTP 2 to some extent. This is the only way that HTTP 2 is worse than HTTP 1.x. Of course, this is TCP, not HTTP 2 itself.

Read more:

  • In-depth understanding of Web protocols (2) : DNS, WebSocket

  • In-depth understanding of Web protocols (I) : HTTP packet transport

Author: Vivo Internet -WuYue