HTTP Overview keeping performance in mind

Introduction

HTTP is how browsers and servers communicate with each other over the Internet. Whenever we are working on web application performance improvements, it is very important to understand the working and parts of HTTP that affect performance. HTTP is a client/server protocol made up of requests and responses. HTTP/1.1 is the most common version today, but some browsers and servers still use HTTP/1.0.

A browser sends an HTTP request for a specific URL, and a server hosting that URL sends back an HTTP response. Like many Internet services, the protocol uses a simple, plain-text format. The types of requests are GET, POST, HEAD, PUT, DELETE, OPTIONS, and TRACE. For this discussion we will focus on GET request, which is the most common.A GET request includes a URL followed by headers. The HTTP response contains a status code, headers, and a body. The following example shows the possible HTTP headers when requesting the script myScript.js.

HTTP Request
GET /abc.com/common/myScript.js
HTTP/1.1
Host: abc.com
User-Agent: Mozilla/5.0 (…) Gecko/20131206 Firefox/1.5.0.9
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/x-javascript
Last-Modified: Wed, 22 Feb 2013 04:15:54 GMT
Content-Length: 355
var xyz=…

Compression

The output size can be reduced using compression if both the browser and server support it. Browsers let server know their support of compression using the Accept-Encoding header. Servers identify compressed responses using the Content-Encoding header.
HTTP Request
GET /abc.com/common/myScript.js
HTTP/1.1
Host: abc.com
User-Agent: Mozilla/5.0 (…) Gecko/20131206 Firefox/1.5.0.9
Accept-Encoding: gzip,deflate
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/x-javascript
Last-Modified: Wed, 22 Feb 2013 04:15:54 GMT
Content-Length: 355
Content-Encoding: gzip
^_\213^H^@^@^@^@^@^@^Cl\217\315j\3030^P\204_E\361IJ…
Please note in above response how the body of the response is compressed.

How Compression Works

Starting with HTTP/1.1, web clients indicate support for compression with the Accept-Encoding header in the HTTP request.

           Accept-Encoding: gzip, deflate

If the web server sees this header in the request, it may compress the response using one of the methods listed by the client. The web server notifies the web client of this via the Content-Encoding header in the response.

          Content-Encoding: gzip

Gzip is currently the most popular, effective and free format compression method. The other compression format is deflate, but it’s slightly less effective and less popular. Browsers that support deflate also support gzip, but several browsers that support gzip do not support deflate, so gzip is the preferred method of compression.

What to Compress

Servers choose what to gzip based on file type. For example gzip can compress follwoing types by default.

1.    HTML documents
2.    Scripts and stylesheets
3.    Any text response including XML and JSON
Image and PDF files should not be gzipped. Trying to gzip them not only wastes CPU resources, it can also potentially increase file sizes. Ideally they should be compressed and optimised by UI developer before using.

Cost of compression

Gzipping takes additional CPU cycles on the server to carry out the compression and on the client to decompress the gzipped file. To determine whether the benefits outweigh the costs you would have to consider the size of the response, the connection bandwidth, and the Internet distance between the client and the server. Generally, it’s worth gzipping any file greater than 1 or 2K. The mod_gzip_minimum_file_size directive, which has a default value of 500 bytes, controls the minimum file size that we would like to compress.

Conditional GET Requests

To reduce the response time of a request browser uses their cache. Whenever a request is made the browser checks if it has a copy of the request in cache. If then it will serve the request. If the browser isn’t sure whether the cache copy still valid, a conditional GET request is made. If the cached copy is still valid, the browser uses the copy from its cache, resulting in a smaller response and a faster user experience.
Typically, the validity of the cached copy is derived from the date it was last modified. The browser knows when the component was last modified based on the Last-Modified header in the response. It uses the If-Modified-Since header to send the last modified date back to the server. Whenever browser makes any conditional request it asking server, “I have a version of this resource with the following last modified date. May I just use it?”
          HTTP Request
GET /abc.com/common/myScript.js
HTTP/1.1
Host: abc.com
User-Agent: Mozilla/5.0 (…) Gecko/20061206 Firefox/1.5.0.9
Accept-Encoding: gzip,deflate
If-Modified-Since: Wed, 22 Feb 2013 04:15:54 GMT
          HTTP Response
HTTP/1.1 200 OK
Content-Type: application/x-javascript
Last-Modified: Wed, 22 Feb 2013 04:15:54 GMT

If the component has not been modified since the specified date, the server returns a “304 Not Modified” status code and the body of the response is not sent. This results a smaller and faster response. In HTTP/1.1 the ETag and If-None-Match headers are another way to make conditional GET requests.

ETags (Entity Tags)

Entity tags (ETags) were introduced in HTTP/1.1. ETags are a mechanism that web servers and browsers use to validate cached components. It provides a way to determine whether the component in the browser’s cache and server are same or not. An ETag is a string that uniquely identifies a specific version of a component. The only format constraint is that the string must be inside quotes “”. The origin server specifies the component’s ETag using the ETag response header.
HTTP Request
GET /i/abc.gif HTTP/1.1
Host: abc.com
HTTP Response
HTTP/1.1 200 OK
Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
ETag: “10c24bc-4ab-457e1c1f”
Content-Length: 1195
ETags were added to provide a more flexible mechanism for validating entities than the last-modified date. If, for example, an entity changes based on the User-Agent or Accept-Language headers, the state of the entity can be reflected in the ETag. Later, if the browser has to validate a component, it uses the If-None-Match header to pass the ETag back to the origin server. If the ETags match, a 304 status code is returned, reducing the response.
HTTP Request
GET /i/abc.gif HTTP/1.1
Host: abc.com
HTTP Response
            HTTP/1.1 304 Not Modified
The Problem with ETags
ETags are server based, they are typically constructed using attributes that make them unique to a specific server where the site is hosted. This is good when the site is hosted on a single server, which is not always the case. In case of multi server hosted site ETags won’t match when a browser gets the original component from one server and later makes a conditional GET request that goes to a different server. Due to this ETags generated by Apache and IIS for the exact same component won’t match from one server to another. If the ETags don’t match, instead of 304 response that ETags were designed for; they’ll get a normal 200 response along with all the data for the component. This degrades the performance of the web request. If there are n servers in cluster in round-robin rotation, the probability that the ETag in the user’s cache will match the server they land on next is 1/n. If you have 10 servers, the user has a 10% chance of getting the correct 304 response, leaving a 90% chance of getting a wasteful 200 response and full data download.
Expires
Conditional GET requests help loading a page faster, but this still requires roundtrip to server to perform the validity check which adds to response time. The Expires header eliminates the roundtrip to server for validation check by making it clear whether the browser can use its cached copy of a component or not?
HTTP/1.1 200 OK
Content-Type: application/x-javascript
Last-Modified: Wed, 22 Feb 2013 04:15:54 GMT
Expires: Wed, 05 Oct 2016 19:16:20 GMT

When the browser sees an Expires header in the response, it saves the expiration date with the component in its cache. As long as the component hasn’t expired, the browser uses the cached version and avoids making any HTTP requests.

Keep-Alive

HTTP is built on top of Transmission Control Protocol (TCP). In early implementations of HTTP, each HTTP request required opening a new socket connection, which results in delayed response because many HTTP requests in a web page go to the same server. For example, most requests for images in a web page all go to a common image server.

Persistent Connections, also known as Keep-Alive in HTTP/1.0, was introduced to solve the inefficiency of opening and closing multiple socket connections to the same server. It lets browsers make multiple requests over a single connection. Browsers and servers use the Connection header to indicate Keep-Alive support. The Connection header looks the same in the server’s response.

HTTP Request
GET /abc.com/common/myScript.js
HTTP/1.1
Host: abc.com
User-Agent: Mozilla/5.0 (…) Gecko/20061206 Firefox/1.5.0.9
Accept-Encoding: gzip,deflate
Connection: keep-alive
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/x-javascript
Last-Modified: Wed, 22 Feb 2013 04:15:54 GMT
Connection: keep-alive
The browser or server can close the connection by sending a Connection: close header. Technically, the Connection: keep-alive header is not required in HTTP/1.1, but most browsers and servers still include it.

Pipelining

Pipelining, defined in HTTP/1.1, allows for sending multiple requests over a single socket without waiting for a response. Pipelining has better performance than persistent connections. Unfortunately, pipelining is supported in Internet Explorer after version 7. It is turned off by default in Firefox through version 2. Until pipelining is more widely adopted, Keep-Alive is the way browsers and servers can more efficiently use socket connections for HTTP. This is even more important for HTTPS because establishing new secure socket connections is more time consuming.

0 Comments

Leave a Comment