
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Mon, 13 Apr 2026 21:41:59 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Road to gRPC]]></title>
            <link>https://blog.cloudflare.com/road-to-grpc/</link>
            <pubDate>Mon, 26 Oct 2020 16:40:02 GMT</pubDate>
            <description><![CDATA[ Cloudflare launched support for gRPC during our 2020 Birthday Week. In this post, we’ll do a deep-dive into the technical details of how we implemented support. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/8yumfi3N8b2OaUtMbTeLj/fa69604b79cf96671d6f1d798fba8621/image1-38.png" />
            
            </figure><p>Cloudflare launched support for <a href="/announcing-grpc/">gRPC</a>® during our 2020 Birthday Week. We’ve been humbled by the immense interest in the beta, and we’d like to thank everyone that has applied and tried out gRPC! In this post we’ll do a deep-dive into the technical details on how we implemented support.</p>
    <div>
      <h3>What is gRPC?</h3>
      <a href="#what-is-grpc">
        
      </a>
    </div>
    <p><a href="https://grpc.io/">gRPC</a> is an open source RPC framework running over HTTP/2. RPC (remote procedure call) is a way for one machine to tell another machine to do something, rather than calling a local function in a library. RPC has been around in the history of distributed computing, with different implementations focusing on different areas, for a long time. What makes gRPC unique are the following characteristics:</p><ul><li><p>It requires the modern HTTP/2 protocol for transport, which is now widely available.</p></li><li><p>A full client/server reference implementation, demo, and test suites are available as <a href="https://github.com/grpc">open source</a>.</p></li><li><p>It does not specify a message format, although <a href="https://developers.google.com/protocol-buffers">Protocol Buffers</a> are the preferred serialization mechanism.</p></li><li><p>Both clients and servers can stream data, which avoids having to poll for new data or create new connections.</p></li></ul><p>In terms of the protocol, <a href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md">gRPC uses HTTP/2</a> frames extensively: requests and responses look very similar to a normal HTTP/2 request.</p><p>What’s unusual, however, is gRPC’s usage of the HTTP trailer. While it’s not widely used in the wild, <a href="https://tools.ietf.org/html/rfc2616#section-3.6.1">HTTP trailers have been around since 1999, as defined in original HTTP/1.1 RFC2616</a>. HTTP message headers are defined to come before the HTTP message body, but HTTP trailer is a set of HTTP headers that can be appended <i>after</i> the message body. However, because there are not many use cases for trailers, many server and client implementations don't fully support them. While HTTP/1.1 needs to use chunked transfer encoding for its body to send an HTTP trailer, in the case of HTTP/2 the trailer is in HEADER frame after the DATA frame of the body.</p><p>There are some cases where an HTTP trailer is useful. For example, we use an HTTP response code to indicate the status of request, but the response code is the very first line of the HTTP response, so we need to decide on the response code very early. A trailer makes it possible to send some metadata after the body. For example, let’s say your web server sends a stream of large data (which is not a fixed size), and in the end you want to send a SHA256 checksum of the data you sent so that the client can verify the contents. Normally, this is not possible with an HTTP status code or the response header which should be sent at the beginning of the response. Using a HTTP trailer header, you can send another header (e.g. <a href="https://tools.ietf.org/html/draft-ietf-httpbis-digest-headers-04#section-10.11">Digest</a>) after having sent all the data.</p><p>gRPC uses HTTP trailers for two purposes. To begin with, it sends its final status (grpc-status) as a trailer header after the content has been sent. The second reason is to support streaming use cases. These use cases last much longer than normal HTTP requests. The HTTP trailer is used to give the post processing result of the request or the response. For example if there is an error during streaming data processing, you can send an error code using the trailer, which is not possible with the header before the message body.</p><p>Here is a simple example of a gRPC request and response in HTTP/2 frames:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1MTBNtDhBqIvqND3l0Fv1w/95fdfb416852a592ce0b05d66dec5865/image3-24.png" />
            
            </figure>
    <div>
      <h3>Adding gRPC support to the Cloudflare Edge</h3>
      <a href="#adding-grpc-support-to-the-cloudflare-edge">
        
      </a>
    </div>
    <p>Since gRPC uses HTTP/2, it may sound easy to natively support gRPC, because Cloudflare already supports <a href="/introducing-http2/">HTTP/2</a>. However, we had a couple of issues:</p><ul><li><p>The HTTP request/response trailer headers were not fully supported by our edge proxy: Cloudflare uses NGINX to accept traffic from eyeballs, and it has limited support for trailers. Further complicating things, requests and responses flowing through Cloudflare go through a set of other proxies.</p></li><li><p>HTTP/2 to origin: our edge proxy uses HTTP/1.1 to fetch objects (whether dynamic or static) from origin. To proxy gRPC traffic, we need support connections to customer gRPC origins using HTTP/2.</p></li><li><p>gRPC streaming needs to allow bidirectional request/response flow: gRPC has two types of protocol flow; one is unary, which is a simple request and response, and another is streaming, which allows non-stop data flow in each direction. To fully support the streaming, the HTTP message body needs to be sent after receiving the response header on the other end. For example, <a href="https://grpc.io/docs/what-is-grpc/core-concepts/#client-streaming-rpc">client streaming</a> will keep sending a request body after receiving a response header.</p></li></ul><p>Due to these reasons, gRPC requests would break when proxied through our network. To overcome these limitations, we looked at various solutions. For example, NGINX has <a href="https://www.nginx.com/blog/nginx-1-13-10-grpc/">a gRPC upstream module</a> to support HTTP/2 gRPC origin, but it’s a separate module, and it also requires HTTP/2 downstream, which cannot be used for our service, as requests cascade through multiple HTTP proxies in some cases. Using HTTP/2 everywhere in the pipeline is not realistic, because of the characteristics of <a href="/keepalives-considered-harmful/">our internal load balancing architecture</a>, and because it would have taken too much effort to make sure all internal traffic uses HTTP/2.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3p9teukTmZfs4wcyWggUZr/07ab8bf40f0bfe57338d3ae770a8bb93/image2-25.png" />
            
            </figure>
    <div>
      <h3>Converting to HTTP/1.1?</h3>
      <a href="#converting-to-http-1-1">
        
      </a>
    </div>
    <p>Ultimately, we discovered a better way: convert gRPC messages to HTTP/1.1 messages without a trailer <i>inside our network,</i> and then convert them back to HTTP/2 before sending the request off to origin. This would work with most HTTP proxies inside Cloudflare that don't support HTTP trailers, and we would need minimal changes.</p><p>Rather than inventing our own format, the gRPC community has already come up with an HTTP/1.1-compatible version: <a href="https://github.com/grpc/grpc-web">gRPC-web</a>. gRPC-web is a modification of the original HTTP/2 based gRPC specification. The original purpose was to be used with the web browsers, which lack direct access HTTP/2 frames. With gRPC-web, the HTTP trailer is moved to the body, so we don’t need to worry about HTTP trailer support inside the proxy. It also comes with streaming support. The resulting HTTP/1.1 message can be still inspected by our security products, such as WAF and Bot Management, to provide the same level of security that Cloudflare brings to other HTTP traffic.</p><p>When an HTTP/2 gRPC message is received at Cloudflare’s edge proxy, the message is “converted” to HTTP/1.1 gRPC-web format. Once the gRPC message is converted, it goes through our pipeline, applying services such as WAF, Cache and Argo services the same way any normal HTTP request would.</p><p>Right before a gRPC-web message leaves the Cloudflare network, it needs to be “converted back” to HTTP/2 gRPC again. Requests that are converted by our system are marked so that our system won’t accidentally convert gRPC-web traffic originated from clients.</p>
    <div>
      <h3>HTTP/2 Origin Support</h3>
      <a href="#http-2-origin-support">
        
      </a>
    </div>
    <p>One of the engineering challenges was to support using HTTP/2 to connect to origins. Before this project, Cloudflare didn't have the ability to connect to origins via HTTP/2.</p><p>Therefore, we decided to build support for HTTP/2 origin support in-house. We built a standalone origin proxy that is able to connect to origins via HTTP/2. On top of this new platform, we implemented the conversion logic for gRPC. gRPC support is the first feature that takes advantage of this new platform. Broader support for HTTP/2 connections to origin servers is on the roadmap.</p>
    <div>
      <h3>gRPC Streaming Support</h3>
      <a href="#grpc-streaming-support">
        
      </a>
    </div>
    <p>As explained above, gRPC has a streaming mode that request body or response body can be sent in stream; in the lifetime of gRPC requests, gRPC message blocks can be sent at any time. At the end of the stream, there will be a HEADER frame indicating the end of the stream. When it’s converted to gRPC-web, we will send the body using chunked encoding and keep the connection open, accepting both sides of the body until we get a gRPC message block, which indicates the end of the stream. This requires our proxy to support bidirectional transfer.</p><p>For example, client streaming is an interesting mode where the server already responds with a response code and its header, but the client is still able to send the request body.</p>
    <div>
      <h3>Interoperability Testing</h3>
      <a href="#interoperability-testing">
        
      </a>
    </div>
    <p>Every new feature at Cloudflare needs proper testing before release. During initial development, we used the <a href="https://www.envoyproxy.io/">envoy</a> proxy with its <a href="https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/grpc_web_filter">gRPC-web filter</a> feature and official examples of gRPC. We prepared a test environment with envoy and a gRPC test origin to make sure that the edge proxy worked properly with gRPC requests. Requests from the gRPC test client are sent to the edge proxy and converted to gRPC-web, and forwarded to the envoy proxy. After that, envoy converts back to gRPC request and sends to gRPC test origin. We were able to verify the basic behavior in this way.</p><p>Once we had basic functionality ready, we also needed to make sure both ends’ conversion functionality worked properly. To do that, we built deeper interoperability testing.</p><p>We referenced the existing <a href="https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md">gRPC interoperability test cases</a> for our test suite and ran the first iteration of tests between the edge proxy and the new origin proxy locally.</p><p>For the second iteration of tests we used different gRPC implementations. For example, some servers sent their final status (grpc-status)  in a <a href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#responses">trailers-only</a> response when there was an immediate error. This response would contain the HTTP/2 response headers and trailer in a single HEADERS frame block with both the END_STREAM and END_HEADERS flags set. Other implementations sent the final status as trailer in a separate HEADERS frame.</p><p>After verifying interoperability locally we ran the test harness against a development environment that supports all the services we have in production. We were then able to ensure no unintended side effects were impacting gRPC requests.</p><p>We love dogfooding! One of the first services we successfully deployed edge gRPC support to is the <a href="/inside-the-entropy/">Cloudflare drand randomness beacon</a>. Onboarding was easy and we’ve been running the beacon in production for the last few weeks without a hitch.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Supporting a new protocol is exciting work! Implementing support for new technologies in existing systems is exciting <i>and</i> intricate, often involving tradeoffs between speed of implementation and overall system complexity. In the case of gRPC, we were able to build support quickly and in a way that did not require significant changes to the Cloudflare edge. This was accomplished by carefully considering implementation options before settling on the idea of converting between HTTP/2 gRPC and HTTP/1.1 gRPC-web format. This design choice made service integration quicker and easier while still satisfying our user’s expectations and constraints.</p><p>If you are interested in using Cloudflare to secure and accelerate your gRPC service, you can read more <a href="/announcing-grpc/">here</a>. And if you want to work on interesting engineering challenges like the one described in this post, <a href="https://www.cloudflare.com/careers/">apply</a>!</p><p><i>gRPC® is a registered trademark of The Linux Foundation.</i></p> ]]></content:encoded>
            <category><![CDATA[gRPC]]></category>
            <guid isPermaLink="false">2aHUwBYwekbmiMXHlzwUDf</guid>
            <dc:creator>Junho Choi</dc:creator>
            <dc:creator>Yuchen Wu</dc:creator>
            <dc:creator>Sangjo Lee</dc:creator>
            <dc:creator>Andrew Hauck</dc:creator>
        </item>
        <item>
            <title><![CDATA[Delivering HTTP/2 upload speed improvements]]></title>
            <link>https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/</link>
            <pubDate>Mon, 24 Aug 2020 11:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare recently shipped improved upload speeds across our network for clients using HTTP/2. This post describes our journey from troubleshooting an issue to fixing it and delivering faster upload speeds to the global Internet. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Cloudflare recently shipped improved <i>upload</i> speeds across our network for clients using HTTP/2. This post describes our journey from troubleshooting an issue to fixing it and delivering faster upload speeds to the global Internet.</p><p>We <a href="/test-your-home-network-performance/">launched</a> <a href="/test-your-home-network-performance/">speed.cloudflare.com</a> in May 2020 to give our users insight into how well their networks perform. The test provides download, upload and latency tests. Soon after release, we received reports from a small number of users that sometimes upload speeds were underreported. Our investigation determined that it seemed to happen with end users that had high upload bandwidth available (several hundreds Mbps class cable modem or fiber service). Our speed tests are performed via browser JavaScript, and most browsers use HTTP/2 by default. We found that HTTP/2 upload speeds were sometimes much slower than HTTP/1.1 (assuming all TLS) when the user had high available upload bandwidth.</p><p>Upload speed is more important than ever, especially for people using home broadband connections. As many people have been forced to work from home they’re using their broadband connections differently than before. Prior to the pandemic broadband traffic was very asymmetric (you downloaded way more than you uploaded… think listening to music, or streaming a movie), but now we’re seeing an increase in uploading as people video conference from home or create content from their home office.</p>
    <div>
      <h3>Initial Tests</h3>
      <a href="#initial-tests">
        
      </a>
    </div>
    <p>User reports were focused on particularly fast home networks. I set up a <code>dummynet</code> network simulator to test upload speed in a controlled environment. I launched a linux VM running our code inside my Macbook Pro and set up a <a href="https://www.saturnsoft.net/tech/2020/06/07/macosx-dummynet/">dummynet between the VM and Mac host</a>.  Measuring upload speed is simple – create a file and upload using curl to an endpoint which accepts a request body. I ran the same test 20 times and took a median upload speed (Mbps).</p>
            <pre><code>% dd if=/dev/urandom of=test.dat bs=1M count=10
% curl --http1.1 -w '%{speed_upload}\n' -sf -o/dev/null --data-binary @test.dat https://edge/upload-endpoint
% curl --http2 -w '%{speed_upload}\n' -sf -o/dev/null --data-binary @test.dat https://edge/upload-endpoint</code></pre>
            <p>Stepping up to uploading a 10MB object over a network which has 200Mbps available bandwidth and 40ms <a href="https://www.cloudflare.com/learning/cdn/glossary/round-trip-time-rtt/">RTT</a>, the result was surprising. Using our production configuration, HTTP/2 upload speed tested at almost half of the same test conditions using HTTP/1.1 (higher is better).</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5MPf14YbzqYkqUtiu4lNyH/a869cff834ea16aeb7d8ce659b7b352c/Screen-Shot-2020-08-18-at-4.44.56-PM.png" />
            
            </figure><p>The result may differ depending on your network, but the gap is bigger when the network is fast. On a slow network, like my home cable connection (5Mbps upload and 20ms RTT), HTTP/2 upload speed was almost identical to the performance observed with HTTP/1.1.</p>
    <div>
      <h3>Receiver Flow Control</h3>
      <a href="#receiver-flow-control">
        
      </a>
    </div>
    <p>Before we get into more detail on this topic, my intuition suggested the issue was related to receiver flow control. Usually the client (browser or any HTTP client) is the receiver of data, but in the case the client is uploading content to the server, the server is the receiver of data. And the receiver needs some type of flow control of the receive buffer.</p><p>How we handle receiver flow control differs between HTTP/1.1 and HTTP/2. For example, HTTP/1.1 doesn’t define protocol-level receiver flow control since there is no multiplexing of requests in the connection and it’s up to the TCP layer which handles receiving data. Note that most of the modern OS TCP stacks have auto tuning of the receive buffer (we will revisit that later) and they tune based on the current <a href="https://en.wikipedia.org/wiki/Bandwidth-delay_product">BDP (bandwidth-delay product)</a>.</p><p>In the case of HTTP/2, there is a <a href="https://tools.ietf.org/html/rfc7540#section-5.2">stream-level flow control</a> mechanism because the protocol supports multiplexing of streams. Each HTTP/2 stream has its own flow control window and there is connection level flow control for all streams in the connection. If it’s too tight, the sender will be blocked by the flow control. If it’s too loose we may end up wasting memory for buffering. So keeping it optimal is important when implementing flow control and the most optimal strategy is to keep the receive buffer matching the current BDP. BDP represents the maximum bytes in flight in the given network and can be used as an optimal buffer size.</p>
    <div>
      <h3>How NGINX handles the request body buffer</h3>
      <a href="#how-nginx-handles-the-request-body-buffer">
        
      </a>
    </div>
    <p>Initially I tried to find a parameter which controls NGINX upload buffering and tried to see if tuning the values improved the result. There are a couple of parameters which are related to uploading a request body.</p><ul><li><p><code>[proxy_buffering](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering)</code></p></li><li><p><code>[client_body_buffer_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size)</code></p></li></ul><p>And this is HTTP/2 specific:</p><ul><li><p><code>[http2_body_preread_size](http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_body_preread_size)</code></p></li></ul><p>Cloudflare does not use the <code>proxy_request_buffering</code> directive, so it can be immediately discounted.  <code>client_body_buffer_size</code> is the size of the request body buffer which is used regardless of the protocol, so this one applies to HTTP/1.1 and HTTP/2 as well.</p><p>When looking into the code, here is how it works:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5FFejc0ITjeuf1tZChITtW/21d23eb449794b824c944062dea9d087/Screen-Shot-2020-08-10-at-3.51.05-PM.png" />
            
            </figure><ul><li><p>HTTP/1.1: use <code>client_body_buffer_size</code> buffer as a buffer between upstream and the client, simply repeating reading and writing using this buffer.</p></li><li><p>HTTP/2: since we need a flow control window update for the HTTP/2 DATA frame, there are two parameters:</p><ul><li><p><code>http2_body_preread_size</code>: it specifies the size of the initial request body read before it starts to send to the upstream.</p></li><li><p><code>client_body_buffer_size</code>: it specifies the size of the request body buffer.</p></li><li><p>Those two parameters are used for allocating a request body buffer during uploading. Here is a brief summary of how unbuffered upload works:</p><ul><li><p>Allocate a single request body buffer which size is a maximum of <code>http2_body_preread_size</code> and <code>client_body_buffer_size</code>. This means if <code>http2_body_preread_size</code> is 64KB and <code>client_body_buffer_size</code> is 128KB, then a 128KB buffer is allocated. We use 128KB for <code>client_body_buffer_size</code>.</p></li><li><p>HTTP/2 Settings <code>INITIAL_WINDOW_SIZE</code> of the stream is set to <code>http2_body_preread_size</code> and we use 64KB as a default (<a href="https://tools.ietf.org/html/rfc7540#section-6.9.2">the RFC7540 default value</a>).</p></li><li><p>HTTP/2 module reads up to <code>http2_body_preread_size</code> before sending it to upstream.</p></li><li><p>After flushing the preread buffer, keep reading from the client and write to upstream and send <code>WINDOW_UPDATE</code> frame back to the client when necessary until the request body is fully received.</p></li></ul></li></ul></li></ul><p>To summarise what this means: HTTP/1.1 simply uses a single buffer, so TCP socket buffers do the flow control. However with HTTP/2, the application layer also has receiver flow control and NGINX uses a fixed size buffer for the receiver. This limits upload speed when the current link has a BDP larger than the current request body buffer size. So the bottleneck is HTTP/2 flow control when the buffer size is too tight.</p>
    <div>
      <h3>We're going to need a bigger buffer?</h3>
      <a href="#were-going-to-need-a-bigger-buffer">
        
      </a>
    </div>
    <p>In theory, bigger buffer sizes should avoid upload bottlenecks, so I tried a few out by running my tests again. The previous chart result is now labelled "prod" and plotted alongside HTTP/2 tests with <code>client_body_buffer_size</code> set to 256KB, 512KB and 1024KB:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3ViLVb0T1Ku13wI6wjCicy/3c722510d2eb466c5685f698cf66a73c/10MB-1.png" />
            
            </figure><p>It appears 512KB is an optimal value for <code>client_body_buffer_size</code>.</p><p>What if I test with some other network parameter? Here is when RTT is 10ms, in this case, 256KB looks optimal.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5nxstBv8ApAJdbBCOhXFSQ/5d33c532b593beb759fcece97843ec5c/10MB-2.png" />
            
            </figure><p>Both cases look much better than current 128KB and get a similar performance to HTTP/1.1 or even better. However, it seems like the optimal buffer size is a moving target and furthermore having too large a buffer size can hurt the performance: we need a smart way to find the optimal buffer size.</p>
    <div>
      <h2>Autotuning request body buffer size</h2>
      <a href="#autotuning-request-body-buffer-size">
        
      </a>
    </div>
    <p>One of the ideas that can help this kind of situation is autotuning. For example, modern TCP stacks autotune their receive buffers automatically. In production, our edge also has <a href="https://sysctl-explorer.net/net/ipv4/tcp_moderate_rcvbuf/">TCP receiver buffer autotuning</a> enabled by default.</p>
            <pre><code>net.ipv4.tcp_moderate_rcvbuf = 1</code></pre>
            <p>But in case of HTTP/2, TCP buffer autotuning is not very effective because the HTTP/2 layer is doing its own flow control and the existing 128KB was too small for a high BDP link. At this point, I decided to pursue autotuning HTTP/2 receive buffer sizing as well, similar to what TCP does.</p><p>The basic idea is that NGINX doubles the size of HTTP/2 request body buffer size based on its BDP. Here is an algorithm currently implemented in our version of NGINX:</p><ul><li><p>Allocate a request body buffer as explained above.</p></li><li><p>For every RTT (using linux <code>tcp_info</code>), update the current BDP.</p></li><li><p>Double the request body buffer size when the current BDP &gt; (receiver window / 4).</p></li></ul>
    <div>
      <h2>Test Result</h2>
      <a href="#test-result">
        
      </a>
    </div>
    
    <div>
      <h3><b>Lab Test</b></h3>
      <a href="#lab-test">
        
      </a>
    </div>
    <p>Here is a test result when HTTP/2 autotune upload is enabled (still using <code>client_body_buffer_size</code> 128KB). You can see "h2 autotune" is doing pretty well – similar or even slightly faster than HTTP/1.1 speed (that's the initial goal). It might be slightly worse than a hand-picked optimal buffer size for given conditions, but you can see now NGINX picks up optimal buffer size automatically based on network conditions.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Sl1rRUKOPiCoT5YeDqU6g/19231407e0b61e6b0ddbc8a2790a772d/10MB-3.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3keSehjT2WOcQVXA4QmDiy/bad3fdb88497492a7c4bd21ac5e8cb87/10MB-4.png" />
            
            </figure>
    <div>
      <h3>Production test</h3>
      <a href="#production-test">
        
      </a>
    </div>
    <p>After we deployed this feature, I ran similar tests against our production edge, uploading a 10MB file from well connected client nodes to our edge. I created a Linux VM instance in Google Cloud and ran the upload test where the network is very fast (a few Gbps) and low latency (&lt;10ms).</p><p>Here is when I run the test in the Google Cloud Belgium region to our CDG (Paris) PoP which has 7ms RTT. This looks very good with almost 3x improvement.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/MgWAhBWB91UiGKOWULns6/5faa240c54fd96fbc8d1b0c8ba7dd5c5/10MB-5.png" />
            
            </figure><p>I also tested between the Google Cloud Tokyo region and our NRT (Tokyo) PoP, which had a 2.3ms RTT. Although this is not realistic for home users, the results are interesting. A 128KB fixed buffer performs well, but HTTP/2 with buffer autotune outperforms even HTTP/1.1.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/33aKI4Dguh46tXuIV9INyv/c6e7a8146e32c70a2cb09cf1c009d664/10MB-6.png" />
            
            </figure>
    <div>
      <h2>Summary</h2>
      <a href="#summary">
        
      </a>
    </div>
    <p>HTTP/2 upload buffer autotuning is now fully deployed in the Cloudflare edge. Customers should now benefit from improved upload performance for all HTTP/2 connections, including speed tests on speed.cloudflare.com. Autotuning upload buffer size logic works well for most cases, and now HTTP/2 upload is much faster than before! When we think about the performance we usually tend to think about download speed or latency reduction, but faster uploading can also help users working from home when they need a large amount of upload, such as photo/video sharing apps, content creation, video conferencing or self broadcasting.</p><p>Many thanks to <a href="/author/lucas/">Lucas Pardue</a> and <a href="/author/rustam-lalkaka/">Rustam Lalkaka</a> for great feedback and suggestions on the article.</p><p>Update: we made this patch available to public and sent to NGINX upstream. You can find it <a href="http://mailman.nginx.org/pipermail/nginx-devel/2020-August/013436.html">here</a>.</p> ]]></content:encoded>
            <category><![CDATA[HTTP2]]></category>
            <category><![CDATA[Cloudflare Network]]></category>
            <category><![CDATA[Network]]></category>
            <guid isPermaLink="false">77KoGnJkqSvxBbUjDx9PhH</guid>
            <dc:creator>Junho Choi</dc:creator>
        </item>
        <item>
            <title><![CDATA[CUBIC and HyStart++ Support in quiche]]></title>
            <link>https://blog.cloudflare.com/cubic-and-hystart-support-in-quiche/</link>
            <pubDate>Fri, 08 May 2020 12:46:12 GMT</pubDate>
            <description><![CDATA[ Congestion control and loss recovery play a big role in the QUIC transport protocol performance. We recently added support for CUBIC and HyStart++ to quiche, the library powering Cloudflare's QUIC, and lab-based testing shows promising results for performance in lossy network conditions. ]]></description>
            <content:encoded><![CDATA[ <p><a href="https://github.com/cloudflare/quiche">quiche</a>, Cloudflare's IETF QUIC implementation has been running <a href="https://tools.ietf.org/html/rfc8312">CUBIC congestion control</a> for a while in our production environment as mentioned in <a href="/http-3-vs-http-2/">Comparing HTTP/3 vs. HTTP/2 Performance</a>. Recently we also added <a href="https://tools.ietf.org/html/draft-balasubramanian-tcpm-hystartplusplus-03">HyStart++</a>  to the congestion control module for further improvements.</p><p>In this post, we will talk about QUIC congestion control and loss recovery briefly and CUBIC and HyStart++ in the quiche congestion control module. We will also discuss lab test results and how to visualize those using <a href="https://tools.ietf.org/html/draft-marx-qlog-event-definitions-quic-h3-01">qlog</a> which was recently added to the quiche library as well.</p>
    <div>
      <h3>QUIC Congestion Control and Loss Recovery</h3>
      <a href="#quic-congestion-control-and-loss-recovery">
        
      </a>
    </div>
    <p>In the network transport area, congestion control is how to decide how much data the connection can send into the network. It has an important role in networking so as not to overrun the link but also at the same time it needs to play nice with other connections in the same network to ensure that the overall network, the Internet, doesn’t collapse. Basically congestion control is trying to detect the current capacity of the link and tune itself in real time and it’s one of the core algorithms for running the Internet.</p><p>QUIC congestion control has been written based on many years of TCP experience, so it is little surprise that the two have mechanisms that bear resemblance. It’s based on the CWND (congestion window, the limit of how many bytes you can send to the network) and the SSTHRESH (slow start threshold, sets a limit when slow start will stop). Congestion control mechanisms can have complicated edge cases and can be hard to tune. Since QUIC is a new transport protocol that people are implementing from scratch, the current draft recommends Reno as a relatively simple mechanism to get people started. However, it has known limitations and so QUIC is designed to have pluggable congestion control; it’s up to implementers to adopt any more advanced ones of their choosing.</p><p>Since Reno became the standard for TCP congestion control, many congestion control algorithms have been proposed by academia and industry. Largely there are two categories: loss-based congestion control such as Reno and CUBIC, where the congestion control responds to a packet loss event, and delay-based congestion control, such as <a href="https://www.cs.princeton.edu/courses/archive/fall06/cos561/papers/vegas.pdf">Vegas</a> and <a href="https://queue.acm.org/detail.cfm?id=3022184">BBR</a> , which the algorithm tries to find a balance between the bandwidth and <a href="https://www.cloudflare.com/learning/cdn/glossary/round-trip-time-rtt/">RTT</a> increase and tune the packet send rate.</p><p>You can port TCP based congestion control algorithms to QUIC without much change by implementing a few hooks. quiche provides a modular API to add a new congestion control module easily.</p><p>Loss detection is how to detect packet loss at the sender side. It’s usually separated from the congestion control algorithm but helps the congestion control to quickly respond to the congestion. Packet loss can be a result of the congestion on the link, but the link layer may also drop a packet without congestion due to the characteristics of the physical layer, such as on a WiFi or mobile network.</p><p>Traditionally TCP uses 3 DUP ACKs for ACK based detection, but delay-based loss detection such as <a href="https://tools.ietf.org/html/draft-ietf-tcpm-rack-08">RACK</a>  has also been used over the years. QUIC combines the lesson from TCP into <a href="https://tools.ietf.org/html/draft-ietf-quic-recovery-27#section-5">two categories</a> . One is based on the packet threshold (similar to 3 DUP ACK detection) and the other is based on a time threshold (similar to RACK). QUIC also has <a href="https://tools.ietf.org/html/draft-ietf-quic-recovery-27#section-3.1.5">ACK Ranges</a> similar to TCP SACK to provide a status of the received packets but ACK Ranges can keep a longer list of received packets in the ACK frame than TCP SACK. This simplifies the implementation overall and helps provide quick recovery when there is multiple loss.</p>
    <div>
      <h3>Reno</h3>
      <a href="#reno">
        
      </a>
    </div>
    <p>Reno (often referred as NewReno) is a standard <a href="https://tools.ietf.org/html/rfc5681">congestion control for TCP</a> and <a href="https://tools.ietf.org/id/draft-ietf-quic-recovery-27.html#section-6">QUIC</a> .</p><p>Reno is easy to understand and doesn't need additional memory to store the state so can be implemented in low spec hardware too. However, its slow start can be very aggressive because it keeps increasing the CWND quickly until it sees congestion. In other words, it doesn’t stop until it sees the packet loss.</p><p>Note that there are multiple states for Reno; Reno starts from "slow start" mode which increases the CWND very aggressively, roughly 2x for every RTT until the congestion is detected or CWND &gt; SSTHRESH. When packet loss is detected, it enters into the “recovery” mode until packet loss is recovered.</p><p>When it exits from recovery (no lost ranges) and CWND &gt; SSTHRESH, it enters into the "congestion avoidance" mode where the CWND grows slowly (roughly a full packet per RTT) and tries to converge on a stable CWND. As a result you will see a “sawtooth” pattern when you make a graph of the CWND over time.</p><p>Here is an example of Reno congestion control CWND graph. See the “Congestion Window” line.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7oTUuWLimS5jC3imLTcZY1/c7008deac0b62ac774735f2dda0a0324/reno-nohs.png" />
            
            </figure>
    <div>
      <h3>CUBIC</h3>
      <a href="#cubic">
        
      </a>
    </div>
    <p>CUBIC was announced in 2008 and became the default congestion control in the Linux kernel. Currently it's defined in <a href="https://tools.ietf.org/html/rfc8312">RFC8312</a>  and implemented in many OS including Linux, BSD and Windows. quiche's CUBIC implementation follows RFC8312 with a fix made by <a href="https://github.com/torvalds/linux/commit/30927520dbae297182990bb21d08762bcc35ce1d">Google in the Linux kernel</a> .</p><p>What makes the difference from Reno is during congestion avoidance  its CWND growth is based on a cubic function as follows:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1KI70rYLDzS02VWarowfyi/981f066dfbf86f11c0a2cc5c697d8d84/1J22foaznVbLs-waPSxKZusUf88svZfepM2QeTeDGe3kvjnkYKN73L861cTQu2eaxLBNtQhW-WEvmlqv3YWf_tRdfTLFXMcWDmUuNzSFanrz53N-9c4insL5kXnj.png" />
            
            </figure><p>(from the CUBIC paper: <a href="https://www.cs.princeton.edu/courses/archive/fall16/cos561/papers/Cubic08.pdf">https://www.cs.princeton.edu/courses/archive/fall16/cos561/papers/Cubic08.pdf</a>)</p><p><i>Wmax</i> is the value of CWND when the congestion is detected. Then it will reduce the CWND by 30% and then the CWND starts to grow again using a cubic function as in the graph, approaching <i>Wmax</i> aggressively in the beginning in the first half but slowly converging to <i>Wmax</i> later. This makes sure that CWND growth approaches the previous point carefully and once we pass <i>Wmax</i>, it starts to grow aggressively again after some time to find a new CWND (this is called "Max Probing").</p><p>Also it has a "TCP-friendly" (actually a Reno-friendly) mode to make sure CWND growth is always bigger than Reno. When the congestion event happens, CUBIC reduces its CWND by 30%, where Reno cuts down CWND by 50%. This makes CUBIC a little more aggressive on packet loss.</p><p>Note that the original CUBIC only defines how to update the CWND during congestion avoidance. Slow start mode is exactly the same as Reno.</p>
    <div>
      <h3>HyStart++</h3>
      <a href="#hystart">
        
      </a>
    </div>
    <p>The authors of CUBIC made a separate effort to improve slow start because CUBIC only changed the way the CWND grows during congestion avoidance. They came up with the idea of <a href="https://www.sciencedirect.com/science/article/abs/pii/S1389128611000363">HyStart</a> .</p><p>HyStart is based on two ideas and basically changes how the CWND is updated during slow start:</p><ul><li><p>RTT delay samples: when the RTT is increased during slow start and over the threshold, it exits slow start early and enters into congestion avoidance.</p></li><li><p>ACK train: When ACK inter-arrival time gets higher and over the threshold, it exits slow start early and enters into congestion avoidance.</p></li></ul><p>However in the real world, ACK train may not be very useful because of ACK compression (merging multiple ACKs into one). Also RTT delay may not work well when the network is unstable.</p><p>To improve such situations there is a new IETF draft proposed by Microsoft engineers named <a href="https://tools.ietf.org/html/draft-balasubramanian-tcpm-hystartplusplus-03">HyStart++</a> . HyStart++ is included in the Windows 10 TCP stack with CUBIC.</p><p>It's a little different from original HyStart:</p><ul><li><p>No ACK Train, only RTT sampling.</p></li><li><p>Add a LSS (Limited Slow Start) phase after exiting slow start. LSS grows the CWND faster than congestion avoidance but slower than Reno slow start. Instead of going into congestion avoidance directly, slow start exits to LSS and LSS exits to congestion avoidance when packet loss happens.</p></li><li><p>Simpler implementation.</p></li></ul><p>In quiche, HyStart++ is turned on by default for both Reno and CUBIC congestion control and can be configured via API.</p>
    <div>
      <h3>Lab Test</h3>
      <a href="#lab-test">
        
      </a>
    </div>
    <p>Here is a test result using <a href="/a-cost-effective-and-extensible-testbed-for-transport-protocol-development/">the test lab</a> . The test condition is as follows:</p><ul><li><p>5Mbps bandwidth, 60ms RTT with a different packet loss from 0% to 8%</p></li><li><p>Measure download time of 8MB file</p></li><li><p>NGINX 1.16.1 server with the <a href="https://github.com/cloudflare/quiche/tree/master/extras/nginx">HTTP3 patch</a></p></li><li><p>TCP: CUBIC in Linux kernel 4.14</p></li><li><p>QUIC: Cloudflare quiche</p></li><li><p>Download 20 times and take a median download time</p></li></ul><p>I run the test with the following combination:</p><ul><li><p>TCP CUBIC (TCP-CUBIC)</p></li><li><p>QUIC Reno (QUIC-RENO)</p></li><li><p>QUIC Reno with Hystart++ (QUIC-RENO-HS)</p></li><li><p>QUIC CUBIC (QUIC-CUBIC)</p></li><li><p>QUIC CUBIC with Hystart++ (QUIC-CUBIC-HS)</p></li></ul>
    <div>
      <h3>Overall Test Result</h3>
      <a href="#overall-test-result">
        
      </a>
    </div>
    <p>Here is a chart of overall test results:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/9p0dppTGft9jL6vBZrpI7/edf89c790e406e83b0df0c556b3c45fb/image6-2.png" />
            
            </figure><p>In these tests, TCP-CUBIC (blue bars) is the baseline to which we compare the performance of QUIC congestion control variants. We include QUIC-RENO (red and yellow bars) because that is the default QUIC baseline. Reno is simpler so we expect it to perform worse than TCP-CUBIC. QUIC-CUBIC (green and orange bars) should perform the same or better than TCP-CUBIC.</p><p>You can see with 0% packet loss TCP and QUIC are almost doing the same (but QUIC is slightly slower). As  packet loss increases QUIC CUBIC performs better than TCP CUBIC. QUIC loss recovery looks to work well, which is great news for real-world networks that do encounter loss.</p><p>With HyStart++, overall performance doesn’t change but that is to be expected, because the main goal of HyStart++ is to prevent overshooting the network. We will see that in the next section.</p>
    <div>
      <h3>The impact of HyStart++</h3>
      <a href="#the-impact-of-hystart">
        
      </a>
    </div>
    <p>HyStart++ may not improve the download time but it will reduce packet loss while maintaining the same performance without it. Since slow start will exit to congestion avoidance when packet loss is detected, we focus on 0% packet loss where only network congestion creates packet loss.</p>
    <div>
      <h3>Packet Loss</h3>
      <a href="#packet-loss">
        
      </a>
    </div>
    <p>For each test, the number of detected packets lost (not the retransmit count) is shown in the following chart. The lost packets number is the average of 20 runs for each test.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3IMpVqPBHkCvQhDQnrJE81/b23f89e3a15cb314700532b371898a46/lost_pkt_hs.png" />
            
            </figure><p>As shown above, you can see that HyStart++ reduces a lot of packet loss.</p><p>Note that compared with Reno, CUBIC can create more packet loss in general. This is because the CUBIC CWND can grow faster than Reno during congestion avoidance and also reduces the CWND less (30%) than Reno (50%) at the congestion event.</p>
    <div>
      <h3>Visualization using qlog and qvis</h3>
      <a href="#visualization-using-qlog-and-qvis">
        
      </a>
    </div>
    <p><a href="https://qvis.edm.uhasselt.be">qvis</a>  is a visualization tool based on <a href="https://tools.ietf.org/html/draft-marx-qlog-event-definitions-quic-h3-01">qlog</a> . Since quiche has implemented <a href="https://github.com/cloudflare/quiche/pull/379">qlog support</a> , we can take qlogs from a QUIC connection and use the qvis tool to visualize connection stats. This is a very useful tool for protocol development. We already used qvis for the Reno graph but let’s see a few more examples to understand how HyStart++ works.</p>
    <div>
      <h3>CUBIC without HyStart++</h3>
      <a href="#cubic-without-hystart">
        
      </a>
    </div>
    <p>Here is a qvis congestion chart for a 16MB transfer in the same lab test conditions, with 0% packet loss. You can see a high peak of CWND in the beginning due to slow start. After some time, it starts to show the CUBIC window growth pattern (concave function).</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/147wv6vAfFBfVunzmgqi4Z/037337856e3edf6983e7d88f27f21d76/cubic-nohs.png" />
            
            </figure><p>When we zoom into the slow start section (the first 0.7 seconds), we can see there is a linear increase of CWND during slow start. This continues until we see a packet lost around 500ms and enters into congestion avoidance after recovery, as you can see in the following chart:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2N7Oiud9tfiQq8JQ1ReRxV/a656abfba8f9dc62778600274861d83b/cubic-nohs-zoom-legend.png" />
            
            </figure>
    <div>
      <h3>CUBIC with HyStart++</h3>
      <a href="#cubic-with-hystart">
        
      </a>
    </div>
    <p>Let’s see the same graph when HyStart++ is enabled. You can see the slow start peak is smaller than without HyStart++, which will lead to less overshooting and packet loss:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3g0T9fYiwD4biGawOh7LTS/ed5f2dfe915751e0fbad12ffd532624c/cubic-hs.png" />
            
            </figure><p>When we zoom in the slow start part again, now we can see that the slow start exits to Limited Slow Start (LSS) around 390ms and exit to congestion avoidance at the congestion event around 500ms.</p><p>As a result you can see the slope is less steep until congestion is detected. It will lead to less packet loss due to less overshooting the network and faster convergence to a stable CWND.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/49le5NmySGIuvRcZETrR8Z/67236043dd84b2932e23d0ea5db3d95d/cubic-hs-zoom-legend.png" />
            
            </figure>
    <div>
      <h3>Conclusions and Future Tasks</h3>
      <a href="#conclusions-and-future-tasks">
        
      </a>
    </div>
    <p>The QUIC draft spec already has integrated a lot of experience from TCP congestion control and loss recovery. It recommends the simple Reno mechanism as a means to get people started implementing the protocol but is under no illusion that there are better performing ones out there. So QUIC is designed to be pluggable in order for it to adopt mechanisms that are being deployed in state-of-the-art TCP implementations.</p><p>CUBIC and HyStart++ are known implementations in the TCP world and give better performance (faster download and less packet loss) than Reno. We've made quiche pluggable and have added CUBIC and HyStart++ support. Our lab testing shows that QUIC is a clear performance winner in lossy network conditions, which is the very thing it is designed for.</p><p>In the future, we also plan to work on advanced features in quiche, such as packet pacing, advanced recovery and BBR congestion control for better QUIC performance. Using quiche you can switch among multiple congestion control algorithms using the config API at the connection level, so you can play with it and choose the best one depending on your need. qlog endpoint logging can be visualized to provide high accuracy insight into how QUIC is behaving, greatly helping understanding and development.</p><p>CUBIC and HyStart++ code is available in the <a href="https://github.com/cloudflare/quiche">quiche primary branch today</a>. Please try it!</p> ]]></content:encoded>
            <category><![CDATA[QUIC]]></category>
            <category><![CDATA[Performance]]></category>
            <category><![CDATA[IETF]]></category>
            <guid isPermaLink="false">525vEcQ9ys8D8JIFjgvliO</guid>
            <dc:creator>Junho Choi</dc:creator>
        </item>
        <item>
            <title><![CDATA[A cost-effective and extensible testbed for transport protocol development]]></title>
            <link>https://blog.cloudflare.com/a-cost-effective-and-extensible-testbed-for-transport-protocol-development/</link>
            <pubDate>Tue, 14 Jan 2020 16:07:15 GMT</pubDate>
            <description><![CDATA[ At Cloudflare, we develop protocols at multiple layers of the network stack. In the past, we focused on HTTP/1.1, HTTP/2, and TLS 1.3. Now, we are working on QUIC and HTTP/3, which are still in IETF draft, but gaining a lot of interest. ]]></description>
            <content:encoded><![CDATA[ <p><i>This was originally published on </i><a href="https://calendar.perfplanet.com/2019/how-to-develop-a-practical-transport-protocol/"><i>Perf Planet's 2019 Web Performance Calendar</i></a><i>.</i></p><p>At Cloudflare, we develop protocols at multiple layers of the network stack. In the past, we focused on HTTP/1.1, HTTP/2, and TLS 1.3. Now, we are working on <a href="/http3-the-past-present-and-future/">QUIC and HTTP/3</a>, which are still in IETF draft, but gaining a lot of interest.</p><p>QUIC is a secure and multiplexed transport protocol that aims to perform better than TCP under some network conditions. It is specified in a family of documents: a transport layer which specifies packet format and basic state machine, recovery and congestion control, security based on TLS 1.3, and an HTTP application layer mapping, which is now called <a href="https://www.cloudflare.com/learning/performance/what-is-http3/">HTTP/3</a>.</p><p>Let’s focus on the transport and recovery layer first. This layer provides a basis for what is sent on the wire (the packet binary format) and how we send it reliably. It includes how to open the connection, how to handshake a new secure session with the help of TLS, how to send data reliably and how to react when there is packet loss or reordering of packets. Also it includes flow control and congestion control to interact well with other transport protocols in the same network. With confidence in the basic transport and recovery layer,  we can take a look at higher application layers such as HTTP/3.</p><p>To develop such a transport protocol, we need multiple stages of the development environment. Since this is a network protocol, it’s best to test in an actual physical network to see how works on the wire. We may start the development using localhost, but after some time we may want to send and receive packets with other hosts. We can build a lab with a couple of virtual machines, using Virtualbox, VMWare or even with Docker. We also have a local testing environment with a Linux VM. But sometimes these have a limited network (localhost only) or are noisy due to other processes in the same host or virtual machines.</p><p>Next step is to have a test lab, typically an isolated network focused on protocol analysis only consisting of dedicated x86 hosts. Lab configuration is particularly important for testing various cases - there is no one-size-fits-all scenario for protocol testing. For example, EDGE is still running in production mobile networks but LTE is dominant and 5G deployment is in early stages. WiFi is very common these days. We want to test our protocol in all those environments. Of course, we can't buy every type of machine or have a very expensive network simulator for every type of environment, so using cheap hardware and an open source OS where we can configure similar environments is ideal.</p>
    <div>
      <h2>The QUIC Protocol Testing lab</h2>
      <a href="#the-quic-protocol-testing-lab">
        
      </a>
    </div>
    <p>The goal of the QUIC testing lab is to aid transport layer protocol development. To develop a transport protocol we need to have a way to control our network environment and a way to get as many different types of debugging data as possible. Also we need to get metrics for comparison with other protocols in production.</p><p>The QUIC Testing Lab has the following goals:</p><ul><li><p><b><i>Help with multiple transport protocol development</i></b>: Developing a new transport layer requires many iterations, from building and validating packets as per protocol spec, to making sure everything works fine under moderate load, to very harsh conditions such as low bandwidth and high packet loss. We need a way to run tests with various network conditions reproducibly in order to catch unexpected issues.</p></li><li><p><b><i>Debugging multiple transport protocol development</i></b>: Recording as much debugging info as we can is important for fixing bugs. Looking into packet captures definitely helps but we also need a detailed debugging log of the server and client to understand the what and why for each packet. For example, when a packet is sent, we want to know why. Is this because there is an application which wants to send some data? Or is this a retransmit of data previously known as lost? Or is this a loss probe which is not an actual packet loss but sent to see if the network is lossy?</p></li><li><p><b><i>Performance comparison between each protocol</i></b>: We want to understand the performance of a new protocol by comparison with existing protocols such as TCP, or with a previous version of the protocol under development. Also we want to test with varying parameters such as changing the congestion control mechanism, changing various timeouts, or changing the buffer sizes at various levels of the stack.</p></li><li><p><b><i>Finding a bottleneck or errors easily</i></b>: Running tests we may see an unexpected error - a transfer that timed out, or ended with an error, or a transfer was corrupted at the client side - each test needs to make sure every test is run correctly, by using a checksum of the original file to compare with what is actually downloaded, or by checking various error codes at the protocol of API level.</p></li></ul><p>When we have a test lab with separate hardware, we have benefits, as follows:</p><ul><li><p>Can configure the testing lab without public Internet access - safe and quiet.</p></li><li><p>Handy access to hardware and its console for maintenance purpose, or for adding or updating hardware.</p></li><li><p>Try other CPU architectures. For clients we use the Raspberry Pi for regular testing because this is ARM architecture (32bit or 64bit), similar to modern smartphones. So testing with ARM architecture helps for compatibility testing before going into a smartphone OS.</p></li><li><p>We can add a real smartphone for testing, such as Android or iPhone. We can test with WiFi but these devices also support Ethernet, so we can test them with a wired network for better consistency.</p></li></ul>
    <div>
      <h2>Lab Configuration</h2>
      <a href="#lab-configuration">
        
      </a>
    </div>
    <p>Here is a diagram of our QUIC Protocol Testing Lab:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1O1CdD682XoQE9Q68bPVkh/a63bd6b8a1bafa516719cfcf0a82c033/Screenshot-2019-07-01-00.35.06.png" />
            
            </figure><p>This is a conceptual diagram and we need to configure a switch for connecting each machine. Currently, we have Raspberry Pis (2 and 3) as an Origin and a Client. And small Intel x86 boxes for the Traffic Shaper and Edge server plus Ethernet switches for interconnectivity.</p><ul><li><p>Origin is simply serving HTTP and HTTPS test objects using a web server. Client may download a file from Origin directly to simulate a download direct from a customer's origin server.</p></li><li><p>Client will download a test object from Origin or Edge, using a different protocol. In typical a configuration Client connects to Edge instead of Origin, so this is to simulate an edge server in the real world. For TCP/HTTP we are using the curl command line client and for QUIC, <a href="https://github.com/cloudflare/quiche">quiche’s</a> http3_client with some modification.</p></li><li><p>Edge is running Cloudflare's web server to serve HTTP/HTTPS via TCP and also the QUIC protocol using quiche. Edge server is installed with the same Linux kernel used on Cloudflare's production machines in order to have the same low level network stack.</p></li><li><p>Traffic Shaper is sitting between Client and Edge (and Origin), controlling network conditions. Currently we are using FreeBSD and ipfw + dummynet. Traffic shaping can also be done using Linux' netem which provides additional network simulation features.</p></li></ul><p>The goal is to run tests with various network conditions, such as bandwidth, latency and packet loss upstream and downstream. The lab is able to run a plaintext HTTP test but currently our focus of testing is HTTPS over TCP and HTTP/3 over QUIC. Since QUIC is running over UDP, both TCP and UDP traffic need to be controlled.</p>
    <div>
      <h2>Test Automation and Visualization</h2>
      <a href="#test-automation-and-visualization">
        
      </a>
    </div>
    <p>In the lab, we have a script installed in Client, which can run a batch of testing with various configuration parameters - for each test combination, we can define a test configuration, including:</p><ul><li><p>Network Condition - Bandwidth, Latency, Packet Loss (upstream and downstream)</p></li></ul><p>For example using netem traffic shaper we can simulate LTE network as below,(<a href="https://www.cloudflare.com/learning/cdn/glossary/round-trip-time-rtt/">RTT</a>=50ms, BW=22Mbps upstream and downstream, with BDP queue size)</p>
            <pre><code>$ tc qdisc add dev eth0 root handle 1:0 netem delay 25ms
$ tc qdisc add dev eth0 parent 1:1 handle 10: tbf rate 22mbit buffer 68750 limit 70000</code></pre>
            <ul><li><p>Test Object sizes - 1KB, 8KB, … 32MB</p></li><li><p>Test Protocols: HTTPS (TCP) and QUIC (UDP)</p></li><li><p>Number of runs and number of requests in a single connection</p></li></ul><p>The test script outputs a CSV file of results for importing into other tools for data processing and visualization - such as Google Sheets, Excel or even a jupyter notebook. Also it’s able to post the result to a database (Clickhouse in our case), so we can query and visualize the results.</p><p>Sometimes a whole test combination takes a long time - the current standard test set with simulated 2G, 3G, LTE, WiFi and various object sizes repeated 10 times for each request may take several hours to run. Large object testing on a slow network takes most of the time, so sometimes we also need to run a limited test (e.g. testing LTE-like conditions only for a smoke test) for quick debugging.</p>
    <div>
      <h3>Chart using Google Sheets:</h3>
      <a href="#chart-using-google-sheets">
        
      </a>
    </div>
    <p>The following comparison chart shows the total transfer time in msec for TCP vs QUIC for different network conditions. The QUIC protocol used here is a development version one.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2umJkG3YBHNnxyD2tJRHQA/c96ef4601d8d20c9760b45ad321e6135/Screen-Shot-2020-01-13-at-3.09.41-PM.png" />
            
            </figure>
    <div>
      <h2>Debugging and performance analysis using of a smartphone</h2>
      <a href="#debugging-and-performance-analysis-using-of-a-smartphone">
        
      </a>
    </div>
    <p>Mobile devices have become a crucial part of our day to day life, so testing the new transport protocol on mobile devices is critically important for mobile app performance. To facilitate that, we need to have a mobile test app which will proxy data over the new transport protocol under development. With this we have the ability to analyze protocol functionality and performance in mobile devices with different network conditions.</p><p>Adding a smartphone to the testbed mentioned above gives an advantage in terms of understanding real performance issues. The major smartphone operating systems, iOS and Android, have quite different networking stack. Adding a smartphone to testbed gives the ability to understand these operating system network stacks in depth which aides new protocol designs.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/RLA6wO7vjRol9o6nlzj34/7c1a7904a379b1e8079853c35597173c/Screen-Shot-2020-01-13-at-3.52.03-PM.png" />
            
            </figure><p>The above figure shows the network block diagram of another similar lab testbed used for protocol testing where a smartphone is connected both wired and wirelessly. A Linux netem based traffic shaper sits in-between the client and server shaping the traffic. Various networking profiles are fed to the traffic shaper to mimic real world scenarios. The client can be either an Android or iOS based smartphone, the server is a vanilla web server serving static files. Client, server and traffic shaper are all connected to the Internet along with the private lab network for management purposes.</p><p>The above lab has mobile devices for both Android or iOS  installed with a test app built with a proprietary client proxy software for proxying data over the new transport protocol under development. The test app also has the ability to make HTTP requests over TCP for comparison purposes.</p><p>The Android or iOS test app can be used to issue multiple HTTPS requests of different object sizes sequentially and concurrently using TCP and QUIC as underlying transport protocol. Later, TTOTAL (total transfer time) of each HTTPS request is used to compare TCP and QUIC performance over different network conditions. One such comparison is shown below,</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Eh2Kl4C9RI40oKf8Z3CJY/a318afd23e177895137ff481bba2dfe1/Screen-Shot-2020-01-13-at-4.08.23-PM.png" />
            
            </figure><p>The table above shows the total transfer time taken for TCP and QUIC requests over an LTE network profile fetching different objects with different concurrency levels using the test app. Here TCP goes over native OS network stack and QUIC goes over Cloudflare QUIC stack.</p><p>Debugging network performance issues is hard when it comes to mobile devices. By adding an actual smartphone into the testbed itself we have the ability to take packet captures at different layers. These are very critical in analyzing and understanding protocol performance.</p><p>It's easy and straightforward to capture packets and analyze them using the tcpdump tool on x86 boxes, but it's a challenge to capture packets on iOS and Android devices. On iOS device ‘rvictl’ lets us capture packets on an external interface. But ‘rvictl’ has some drawbacks such as timestamps being inaccurate. Since we are dealing with millisecond level events, timestamps need to be accurate to analyze the root cause of a problem.</p><p>We can capture packets on internal loopback interfaces on jailbroken iPhones and rooted Android devices. Jailbreaking a recent iOS device is nontrivial. We also need to make sure that autoupdate of any sort is disabled on such a phone otherwise it would disable the jailbreak and you have to start the whole process again. With a jailbroken phone we have root access to the device which lets us take packet captures as needed using tcpdump.</p><p>Packet captures taken using jailbroken iOS devices or rooted Android devices connected to the lab testbed help us analyze  performance bottlenecks and improve protocol performance.</p><p>iOS and Android devices different network stacks in their core operating systems. These packet captures also help us understand the network stack of these mobile devices, for example in iOS devices packets punted through loopback interface had a mysterious delay of 5 to 7ms.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Cloudflare is actively involved in helping to drive forward the QUIC and HTTP/3 standards by testing and optimizing these new protocols in simulated real world environments. By simulating a wide variety of networks we are working on our mission of Helping Build a Better Internet. For everyone, everywhere.</p><p><i>Would like to thank SangJo Lee, Hiren Panchasara, Lucas Pardue and Sreeni Tellakula for their contributions.</i></p> ]]></content:encoded>
            <category><![CDATA[HTTP3]]></category>
            <category><![CDATA[QUIC]]></category>
            <category><![CDATA[TLS 1.3]]></category>
            <category><![CDATA[TCP]]></category>
            <guid isPermaLink="false">58abpUfUPAE7n3X9TDOpyt</guid>
            <dc:creator>Lohith Bellad</dc:creator>
            <dc:creator>Junho Choi</dc:creator>
        </item>
    </channel>
</rss>