The next way to mitigate computationally intensive code is to push the calculation to a backend process. To explore that strategy, we'll request computations from a backend Fibonacci server, using the HTTP Client object to do so. However, before we look at that, let's first talk in general about using the HTTP Client object.
Node.js includes an HTTP Client object, useful for making HTTP requests. It has the capability to issue any kind of HTTP request. In this section, we'll use the HTTP Client object to make HTTP requests similar to calling a Representational State Transfer (REST) web service.
Let's start with some code inspired by the wget or curl commands to make HTTP requests and show the results. Create a file named wget.js containing this code:
const http = require('http');
const url = require('url');
const util = require('util');
const argUrl = process.argv[2];
const parsedUrl = url.parse(argUrl, true);
// The options object is passed to http.request
// telling it the URL to retrieve
const options = {
host: parsedUrl.hostname,
port: parsedUrl.port,
path: parsedUrl.pathname,
method: 'GET'
};
if (parsedUrl.search) options.path += "?"+parsedUrl.search;
const req = http.request(options);
// Invoked when the request is finished
req.on('response', res => {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + util.inspect(res.headers));
res.setEncoding('utf8');
res.on('data', chunk => { console.log('BODY: ' + chunk); });
res.on('error', err => { console.log('RESPONSE ERROR: ' + err); });
});
// Invoked on errors
req.on('error', err => { console.log('REQUEST ERROR: ' + err); });
req.end();
You can run the script as follows:
$ node wget.js http://example.com
STATUS: 200
HEADERS: { 'accept-ranges': 'bytes',
'cache-control': 'max-age=604800',
'content-type': 'text/html',
date: 'Sun, 10 Dec 2017 23:40:44 GMT',
etag: '"359670651"',
expires: 'Sun, 17 Dec 2017 23:40:44 GMT',
'last-modified': 'Fri, 09 Aug 2013 23:54:35 GMT',
server: 'ECS (rhv/81A7)',
vary: 'Accept-Encoding',
'x-cache': 'HIT',
'content-length': '1270',
connection: 'close' }
BODY: <!doctype html>
<html>
...
There's more in the printout, namely the HTML of the page at http://example.com/. The purpose of wget.js is to make an HTTP request and show you voluminous details of the response. An HTTP request is initiated with the http.request method, as follows:
var http = require('http');
var options = {
host: 'example.com',
port: 80,
path: null,
method: 'GET'
};
var request = http.request(options);
request.on('response', response => {
...
});
The options object describes the request to make, and the callback function is called when the response arrives. The options object is fairly straightforward, with the host, port, and path fields specifying the URL being requested. The method field must be one of the HTTP verbs (GET, PUT, POST, and so on). You can also provide a headers array for the headers in the HTTP request. For example, you might need to provide a cookie:
var options = {
headers: { 'Cookie': '.. cookie value' }
};
The response object is itself an EventEmitter, which emits the data and error events. The data event is called as data arrives, and the error event is, of course, called on errors.
The request object is a WritableStream, which is useful for HTTP requests containing data, such as PUT or POST. This means the request object has a write function that writes data to the requester. The data format in an HTTP request is specified by the standard Multipurpose Internet Mail Extensions (MIME) originally created to give us better email. Around 1992, the WWW community worked with the MIME standard committee which was developing a format for multi-part, multi-media-rich electronic mail. Receiving fancy-looking email is so commonplace today that one might not be aware that email used to be plain text. MIME-types were developed to describe the format of each piece of data, and the WWW community adopted this for use on the web. HTML forms will post with a Content-Type of multipart/form-data, for example.