Measure network performance by resource timing API

Introduction

Recently, we have been working on improving our web performance by setting up CDN (Content Delivery Networks) in mainland China. We used to rely on AWS CloudFront to deliver our static data worldwide. However, the network infrastructure is so complex in China, and there are no edge locations currently available inside of it. Thus, we have to leverage against local CDN providers to make sure our users in China have the same network latency and throughput as people outside of it. The question is how well it performs compared to AWS CloudFront.

This article will focus on what resource timing API is and how to use it to measure the network performance. In the end, we will give some key points we wish we knew when we get started. Before we get into it, we should let you know there are some pretty cool services like Webpagetest, ThousandEyes that aim to provide timing metrics of network performance or benchmark for CDN providers. But, we think it will be more interesting if we can collect real world data from our users.


Resource loading phases

From https://developer.mozilla.org/en-US/docs/Web/API/Resource_Timing_API/Using_the_Resource_Timing_API

Let’s start with resource loading phases. The figure above gives us a clear picture about what stages browser has to go through when it fetches the resource.

Redirect

If there are HTTP redirects when fetching the resource, otherwise, the length of redirect time is 0.

App Cache

Resources may be cached in browser for later use or offline support. If the resource can be obtained from cache, it will jump over the next 2 phases and go to the “Request” phase.

DNS

In this phase, the browser will start the domain name lookup. The domain name like “www.google.com” will be mapped to an IP address in this phase. If the browser has the domain information in cache, it can retrieve from that and the length of DNS lookup time will be 0.

TCP

In this phase, the browser starts to establish the TCP connection, aka three-way handshake, with the server to get the resource. To open a new TCP connection for every request/response is not efficient. A single TCP connection once established can be reused to send multiple requests/responses. If a persistent connection is reused, the length of TCP connection time is 0.

If SSL handshake is required, it will be included in this phase as well.

Request

The browser starts to send request to the server.

Response

This phase begins right at the time the browser receives the first byte of the response from the server or local cache, ends immediately after receiving the last byte.


Resource Timing API

The resource timing API is a Javascript API that browsers provide to enable retrieving and analyzing detailed network timing data (in millisecond) and size about the fetched resource. Let’s see how to use it.

var resources = performance.getEntriesByType("resource");

The resources is an array of PerformanceResourceTiming objects. The PerformanceResourceTiming object contains the URL of the requested resource, timing metrics and resource size data. The browser has a resource timing buffer to hold PerformanceResourceTiming objects. Whenever the browser starts to fetch a resource, it will create a new PerformanceResourceTiming object and store into the buffer until the buffer is full.

Properties of PerformanceResourceTiming object.

With the help of the properties of resource timing, you can calculate the amount of time the resource loading phases take.

  • redirection: redirectEnd — redirectStart
  • DNS lookup: domainLookupEnd — domainLookupStart
  • TCP handshake: connectEnd — connectStart
  • response: responseEnd — responseStart

The following example illustrates using these properties to calculate loading times.


Tips to use resource timing API

As you can see, it’s pretty easy to use resource timing API to measure the network performance. But when we wrote the script to do the calculation, there are some issues we thought it’s worth sharing.

Why the properties are 0?

For cross origin resources, the following properties are returned as 0 by default.

  • redirectStart, redirectEnd
  • domainLookupStart, domainLookupEnd
  • connectStart, connectEnd, secureConnectionStart
  • requestStart
  • responseStart

If you see these properties are 0, don’t be too surprise, that’s because the resource’s domain doesn’t match your domain. To permit these values to be shared, server should haveTiming-Allow-Origin HTTP header with a value to tell browser what origins can get the timestamp value. You can specify Timing-Allow-Origin: * to permit all origins to see the timing detail.

Can’t find the performance entry of the resource

When browser starts to fetch a resource, it will create a PerformanceResourceTiming object to collect the timing metrics and put it into the resource timing buffer. However, the buffer is not unlimited. You have to manage the buffer size in case your application may use more entries than the limit.

Please see clearResourceTimings() method to remove all the entries in the buffer and setResourceTimingBufferSize() method to adjust the buffer limit. Theresourcetimingbufferfull event will be fired as well when the buffer is full. With the help of this event, you can set up an event handler to clear the entries when the buffer reaches its limit.

Wait, where is requestEnd?

As you see from the image of loading phases, the resource timing API does not include requestEnd , which may represent the completion of sending the request. Let’s see how w3c explain it.

Completion of sending the request from the user agent does not always indicate the corresponding completion time in the network transport, which brings most of the benefit of having such an attribute.
Some user agents have high cost to determine the actual completion time of sending the request due to the HTTP layer encapsulation.

We were wondering the time elapsed between the completion of sending HTTP request and the time when the browser get the first byte. But, it seems like no way to get it.

Why some properties are gone?

Almost all modern browsers have supported the resource timing API, but some only include timing metrics without the resource size data. At this time, the mobile Safari is still not supporting yet. Please see the browser compatibility before using it.

What about the resources loaded within the iframe?

Only the iframe’s src resource is included as an entry in the resource timing buffer. The sub-resources requested within the iframe can be found by using contentWindow.performance object.


Conclusion

The resource timing API can help you to measure the network performance of your application. You can use it to track the metrics of DNS lookup, TCP handshake, etc. We hope that you find this article interesting, and can help you to get started to use the resource timing API to collect the real world data of your website.