HTTP is arguably the most important topic of learning how to properly implement web sites. We must understand how our data is transported in order to understand how it is interpreted at a later date. To that end, we will look at HTTP itself, HTTP clients, and HTTP servers.

HTTP

URLs

URIs are universal. They do not necessarily only represent web sites! URLs tend to describe either HTTP or HTTPS.

Reference: URI Syntax

The format is as follows: URI = scheme:[//authority]path[?query][#fragment]

And the authority field: authority = [userinfo@]host[:port]

Sidenote: HTTP BASIC Authentication

Basic authentication is described by the authority field. This is a built in scheme to pass user credentials, but it is not secure.

Status codes

  • 2xx - Status okay

  • 3xx - Status moved

  • 4xx - Status user error

  • 5xx - Status server error

Verbs

  • GET

  • POST

  • PUT

  • DELETE

  • PATCH

Content type

Usually text/html, but An incomplete list of mime types shows there are many others. It’s important to send a correct MIME type with any HTTP response so that the client knows what to do.

HTTP Clients in Node

var http = require('https');
http.get('https://www.gitignore.io/api/node', function(res) {
  const { statusCode } = res;
  const contentType = res.headers['content-type'];

  let error;
  if (statusCode !== 200) {
    error = new Error('Request Failed.\n' +
                      `Status Code: ${statusCode}`);
	console.log(res.headers.location);
  }
  if (error) {
    console.error(error.message);
    // consume response data to free up memory
    res.resume();
    return;
  }

  res.setEncoding('utf8');
  let rawData = '';
  res.on('data', (chunk) => { rawData += chunk; });
  res.on('end', () => {
    try {
      console.log(rawData);
    } catch (e) {
      console.error(e.message);
    }
  });
}).on('error', (e) => {
  console.error(`Got error: ${e.message}`);
});

Can we simplify this? Are all of these checks necessary?

HTTP Services in Node

A simple service (from the book):

var fs = require('fs');
	http = require('http');
	url = require('url');

var ROOT_DIR = "html/";
http.createServer(function(req, res) {
	var urlObj = url.parse(req.url, true, false);
	fs.readFile(ROOT_DIR + urlObj.pathname, function (err, data) {
		if (err) {
			res.writeHead(404);
			res.end(JSON.stringify(err));
			return;
		}

		res.writeHead(200);
		res.end(data);
	});
}).listen(8080);

This is great for files, but we could just use Apache, NGINX, or any other number of servers for that. We want dynamic websites!

We could write straight to the res handler and interpret the req object to figure out what we’re going to write…​

var fs = require('fs');
	http = require('http');
	url = require('url');

var ROOT_DIR = "html/";
http.createServer(function(req, res) {
	res.setHeader('Content-type', 'text/html');
	res.writeHead(200);
	res.write('<html><head><title>My great webservice</title></head><body>Hello, World!</body></html>');
	res.end();
}).listen(8080);

But that’s no good either. That’s a lot of work and we need something to dynamically interpret where to route requests within a larger codebase. Nobody writes webservers as single function applications. We still need to handle other HTTP verbs like POST, PATH, PUT, DELETE!

Express

npm install --save express

var express = require('express');
var app = express();
http.createServer(app).listen(8080);
app.get('/', function(req, res) {
	res.send('Hello from Express!');
});