Skip to content
On this page

HTTP Protocol

The purpose of Node.js development is to write web server programs using JavaScript. Since JavaScript has effectively dominated client-side scripting, its advantage lies in the sheer number of front-end developers worldwide. If you have mastered JavaScript for front-end development, learning to apply it in back-end development truly makes you a full-stack developer.

HTTP Server

To understand how web server programs work, we first need a basic understanding of the HTTP protocol. If you're not familiar with HTTP, take a look at an overview of it.

To develop an HTTP server program, it's impractical to handle TCP connections and parse HTTP from scratch. These tasks are already managed by Node.js's built-in http module. Applications interact with the request and response objects provided by this module instead of directly dealing with the HTTP protocol.

The request object encapsulates the HTTP request, allowing us to access all HTTP request information through its properties and methods. The response object encapsulates the HTTP response, enabling us to return the HTTP response to the browser using its methods.

Implementing a simple HTTP server program in Node.js is straightforward. Let's create the simplest web program, hello.js, which returns "Hello world!" for all requests:

javascript
// Import the http module:
import http from 'node:http';

// Create an http server and provide a callback function:
const server = http.createServer((request, response) => {
    // The callback function receives the request and response objects,
    // obtaining the HTTP request method and URL:
    console.log(request.method + ': ' + request.url);
    // Write HTTP response 200 to response, setting Content-Type: text/html:
    response.writeHead(200, {'Content-Type': 'text/html'});
    // Write the HTML content of the HTTP response to response:
    response.end('<h1>Hello world!</h1>');
});

// Return 400 on error:
server.on('clientError', (err, socket) => {
  socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});

// Make the server listen on port 8080:
server.listen(8080);
console.log('##');

When you run this program in the command prompt, you will see the following output:

bash
$ node simple-server.mjs 
##

Do not close the command prompt. Instead, open a browser and enter http://localhost:8080 to see the server's response:

http-page.webp

File Server

Now, let’s extend the web program into a file server. We can set a directory and have the web program act as a file server. To do this, we simply need to parse the path from request.url, find the corresponding file locally, and send its content.

Observing the printed request.url, it reflects the path and parameters requested by the browser, such as:

/
/index.html
/hello?name=bob

To extract the path portion, we can use the URL object directly:

javascript
let url = new URL('http://localhost' + '/index.html?v=1');
let pathname = url.pathname;
console.log(pathname); // index.html

To handle local file directories, we need to use Node.js's path module, which makes constructing directories easy:

javascript
import path from 'node:path';

// Resolve the current directory:
let workDir = path.resolve('.'); // '/Users/michael'

// Combine the complete file path: current directory + 'pub' + 'index.html':
let filePath = path.join(workDir, 'pub', 'index.html');
// '/Users/michael/pub/index.html'

Using the path module correctly manages OS-related file paths. On Windows, the returned path looks like C:\Users\michael\static\index.html, allowing us to avoid worrying about how to concatenate paths.

Finally, we implement a file server in simple-file-server.js:

javascript
// Import the http module:
import http from 'node:http';
import path from 'node:path';
import { createReadStream } from 'node:fs';
import { stat } from 'node:fs/promises';

// Set the www root directory to the current directory:
const wwwRoot = path.resolve('.');
console.log(`set www root: ${wwwRoot}`);

// Determine MIME type based on extension:
function guessMime(pathname) {
    // FIXME:
    return 'text/html';
}

// Create an http file server and provide a callback function:
const server = http.createServer((request, response) => {
    // Get the HTTP request method and URL:
    console.log(request.method + ': ' + request.url);
    if (request.method !== 'GET') {
        response.writeHead(400, { 'Content-Type': 'text/html' });
        response.end('<h1>400 Bad Request</h1>');
    } else {
        // Parse the path: 
        let url = new URL(`http://localhost${request.url}`);
        let pathname = url.pathname;
        let filepath = path.join(wwwRoot, pathname);
        // TODO: Necessary security checks
        // Check file status:
        stat(filepath).then(st => {
            if (st.isFile()) {
                console.log('200 OK');
                // Send 200 response:
                response.writeHead(200, { 'Content-Type': guessMime(pathname) });
                // Stream the file to response:
                createReadStream(filepath).pipe(response);
            } else {
                console.log('404 Not Found');
                response.writeHead(404, { 'Content-Type': 'text/html' });
                response.end('<h1>404 Not Found</h1>');
            }
        }).catch(err => {
            console.log('404 Not Found');
            response.writeHead(404, { 'Content-Type': 'text/html' });
            response.end('<h1>404 Not Found</h1>');
        });
    }
});

// Return 400 on error:
server.on('clientError', (err, socket) => {
    socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});

// Make the server listen on port 8080:
server.listen(8080);
console.log('##');

There’s no need to manually read the file content. Since the response object is a writable stream, we can use the pipe() method to automatically read the file content and output it to the HTTP response.

Run node simple-file-server.mjs in the command line, then enter http://localhost:8080/index.html in the browser:

http-index.webp

Exercise

When entering http://localhost:8080/ in the browser, a 404 response will be returned because the program identifies the HTTP request as a directory rather than a file. Please modify simple-file-server.mjs to automatically search for index.html or default.html within the directory if the requested path is a directory, returning the HTML file content if found.

HTTP Protocol has loaded