You have an HTTP or HTTPS server. This server will need to process incoming requests.
Solution
When you build a web server, you need to handle requests. Requests come in many forms and contain what can quickly become an overwhelming set of data. In handling requests you need to be able to sift through the incoming data efficiently in order to handle headers, methods, and URL parameters.
In this solution, you will create a web server that handles requests. It will likely look similar to many web servers that you are familiar with. This server will handle request headers and deal with them as you see fit. An example of this would be to not send a tracking cookie if the request header contains the “do not track” directive.
After properly handling the headers, you will likely want to parse the request URLs. This will help you handle 404s and general application routing by processing the incoming path. Aside from the path you may also find interest in the query string parameters that were sent along with the request.
Finally, you will want to examine the request method that initiated the request. This is the HTTP method, and it will be useful when you create any application, or a representational state transfer (REST) application programming interface (API).
Chapter 4 ■ Building a WeB Server
Listing 4-7. Handling Requests /**
* Processing Requests */
var http = require('http'),
url = require('url');
var server = http.createServer(function(req, res) { //Handle headers
if (req.headers.dnt == 1) {
console.log('Do Not Track'); }
//Parse the URL
var url_parsed = url.parse(req.url, true); //What type of request is this
if (req.method === 'GET') {
handleGetRequest(res, url_parsed);
} else if (['POST', 'PUT', 'DELETE'].indexOf(req.method) > -1) { handleApiRequest(res, url_parsed, req.method);
} else {
res.end('Method not supported'); }
});
handleGetRequest = function(res, url_parsed) { console.log('search: ' + url_parsed.search); console.log('query: ' + JSON.stringify(url_parsed.query)); console.log('pathname: ' + url_parsed.pathname); console.log('path: ' + url_parsed.path); console.log('href: ' + url_parsed.href); res.end('get\n'); };
handleApiRequest = function(res, url_parsed, method) { if (url_parsed.path !== '/api') { res.statusCode = 404; res.end('404\n'); } res.end(method); }; server.listen(8080);
Chapter 4 ■ Building a WeB Server
How It Works
The web server in this solution is built to handle requests. It does this by examining the details that surround the request received by the server. This request is in actuality an object known as the http.IncomingMessage.
The http.IncomingMessage inherits the readable stream interface. On top of this, it builds some objects that are useful in the case of HTTP messages, as shown in Listing 4-8.
Listing 4-8. http.IncomingMessage function IncomingMessage(socket) { Stream.Readable.call(this); this.socket = socket; this.connection = socket; this.httpVersion = null; this.complete = false; this.headers = {}; this.trailers = {}; this.readable = true; this._pendings = []; this._pendingIndex = 0; // request (server) only this.url = '';
this.method = null; // response (client) only this.statusCode = null; this.client = this.socket; this._consuming = false; this._dumped = false; } util.inherits(IncomingMessage, Stream.Readable);
As you can see from the source, the http.IncomingMessage brings with it several objects or settings that are important to your solution. First, it brings the headers. The request headers are objects that directly reflect the key-value pairs that were sent with the request. When I attempt to send a request to this server from my web browser, the headers look as shown in Listing 4-9.
Listing 4-9. Typical Request Headers { host: 'localhost:8080',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:20.0) Gecko/20100101 Firefox/20.0', accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'accept-language': 'en-us,en;q=0.5', 'accept-encoding': 'gzip, deflate', dnt: '1',
Chapter 4 ■ Building a WeB Server
Second, in your web server you need to handle the request.url. This contains all the information that is sent via the request URL. The easiest way to parse this is to utilize the URL module. You can tell the URL module to parse the request.url including the query string.
The third part of the request that can be valuable for your server is the request.method. request.method will provide you with the HTTP method that began your request. In the solution the web server was set up to imitate a web API. In a case such as this, the routing of API methods is not determined completely by the path of the URL but also by the request.method. These methods are simply the string names of the HTTP methods. Your solution handles these different methods in two different ways. First, you serve an HTTP GET request; using that will then log some of the details of the request and respond that the method was indeed a GET. Second, you mimic an API routing scheme with other methods. These are handled by a singular function that will check to make sure you are requesting not only the proper method but also the path. As you can see in the solution these are each handled in such a way that you could cURL each type to see a different result as you see in Listing 4-10.
Listing 4-10. Curling for different results $ curl -X PUT http://localhost:8080/api
put
$ curl -X PUT http://localhost:8080/apis
404
$ curl -X DELETE http://localhost:8080/api
delete
$ curl -X TRACE http://localhost:8080/api
Method not supported
By understanding the information that accompanies an http.request in Node.js, you are able to leverage this to build your web server to handle these requests. Next, you will see how to send responses from your server.