Improving Node.js https request performance

The HTTPS module of Node.js allows making HTTPS request to other servers. Unfortunately, making requests with this module often leads to poor performance.

I found that calling a nearby HTTPS server usually took between 300 ms and 150 ms.

With the following simple solution, I was able to reduce this time to less than 40 ms.

By default, Node.js does not keep SSL connections alive. Thus, a new handshake has to be performed for every request. If you make many requests to the same server (a very common situation when dealing with APIs), it makes a lot of sense to keep SSL connections alive. This can be accomplished as follows:

var agent = new https.Agent({
 keepAlive: true
});

var options = {
 host: 'objecthub.io',
 port: 443,
 path: '/admin/metrics/main.json',
 method: 'GET',
 agent: agent
};

var req = https.request(options, function(res) {

  var str = "";
  res.on('data', function (chunk) {
     str += chunk;
  });

  res.on('end', function () {
     // done
  });
});

req.write('');
req.end();

req.on('error', function(e) {
   // error
});

Note here that a https.Agent is created with the parameter keepAlive: true. This agent is then passed in the options for the request. You can use the same agent for all requests.

Using this should significantly speed up making requests to the same server.

You can find the example code I used to test this here:

https://repo.on.objecthub.io/object/nodejs-https-performance-test

More Resources

Why Netty cannot serve multiple SSL domains from one IP

Background

Due to some issues with a transparent proxy apparently employed by Vodafone New Zealand, a signed SSL certificate had to be added to the REST server in the Appjangle cloud in order to serve resources ‘protected’ from Vodafone’s caching efforts.

One REST server in the Appjangle cloud can serve more than one domain … but this appears to be troublesome to achieve using SSL.

Problem

You would like to serve content through HTTPS from two domains from one IP address using Netty. For example:

https://mydomain1.com

and https://mydomain2.com

both should be served from a Netty server running on the IP 184.2.23.1.

Analysis

It is relatively easy to host multiple HTTP hosts on one IP address. One can implement a SimpleChannelUpstreamHandler arranged in a pipeline after a HttpRequestDecoder. The SimpleChannelUpstramHandler can then separate requests based on the HOST value of the incoming HttpRequests.

For SSL, this is not possible, since the SslHandler is usually defined at the very beginning of a Netty pipeline. This is an inherent requirement of the SSL protocol, which protects communication with an IP address and not with a domain.

It is therefore not possible to ‘unwrap’ the incoming encoded message and retrieve its HOST value, since this can only be done AFTER the SSL connection is established.

Note: This is not an inherent problem of Netty but caused by the JDK’s SSL implementation.

Solution

One possible solution would be to use Server Name Indication – an extension to the SSL protocol to allow for ‘virtual hosts’ over SSL. However, this is extension not necessarily supported by all available HTTP clients. Moreover, it does not appear that Netty/Java currently support Server Name Indication.

Therefore, the best option at present appears to use one dedicated IP address for every HTTPS host you want to use.

Also note that JDK 7 adds support for Server Name Indication, so if you are running on this platform, you could explore if you can configure Netty to support it.