Today most of us work with HTTP—whether we’re producing or consuming web services, or building web applications. The HTTP protocol is stateless and built on TCP, and Node’s HTTP module is similarly built on top of its TCP module.
You could, of course, use your own protocol built with TCP. After all, HTTP is built on top of TCP. But due to the prevalence of web browsers and tools for working with web-based services, HTTP is a natural fit for many problems that involve communicating between remote systems.
In the next section you’ll learn how to write a basic HTTP server using Node’s core modules.
HTTP servers
In this technique you’ll learn how to create HTTP servers with Node’s http module. Although this is more work than using a web framework built on top of Node, popular web frameworks generally use the same techniques internally, and the objects they expose are derived from Node’s standard classes. Understanding the underlying modules
and classes is therefore useful for working extensively with HTTP.
■ Problem – You want to run HTTP servers and test them.
■ Solution – Use http.createServer and http.createClient.
■ Discussion – The http.createServer method is a shortcut for creating a new http.Server object that descends from net.Server. The HTTP server is extended to handle various elements of the HTTP protocol—parsing headers, dealing with response codes, and setting up various events on sockets. The major focus in Node’s HTTP handling code is
parsing; a C++ wrapper around Joyent’s own C parser library is used. This library can extract header fields and values, Content-Length, request method, response status code, and more.
The following listing shows a small “Hello World” web server that uses the http module.
The http module contains both Node’s client and server HTTP classes. The http.createServer creates a new server object and returns it. The argument is a callback that receives req and res objects—request and response, respectively. You may be familiar with these objects if you’ve used higher-level Node web frameworks like Express and restify.
The interesting thing about the listener callback passed to http.createServer is that it behaves much like the listener passed to net.createServer. Indeed, the mechanism is the same—we’re creating TCP sockets, but layering HTTP on top.
The main conceptual difference between the HTTP protocol and TCP socket communication is a question of state: HTTP is a stateless protocol. It’s perfectly acceptable and in fact typical to create and tear down TCP sockets per request. This partly explains why Node’s underlying HTTP implementation is low-level C++ and C: it needs to be fast and use as little memory as possible.
In listing 7.6, the listener runs for every request. In the TCP example from technique 45, the server kept a connection open as long as the client was connected. Because HTTP connections are just TCP sockets, we can use res and req like the sockets in listing 7.6: res.write will write to the socket E, and headers can be written back with res.writeHead D, which is where the socket connection and HTTP APIs visibly diverge—the underlying socket will be closed as soon as the response has been written.
After the server has been set up, we can set it to listen on a port with server.listen . Now that we can create servers, let’s look at creating HTTP requests. The http.request method will create new connections, and accepts an options argument object and a callback that will be run when a connection is made. This means we still need to attach a data listener to the response passed to the callback to slurp down any sent data.
The data callback ensures the response from the server has the expected format: the body content and status code are checked. The server is told to stop listening for connections when the last client has disconnected by calling server.unref, which means the script exits cleanly. This makes it easy to see if any errors were encountered.