What's the internet? → a global network of networks
What's the underlying protocol that computer's on the internet use to communicate? → TCP/IP
What's the world wide web? → a bunch of documents connected by hyperlinks … that are retrievable by url
What protocol is the web based on? → HTTP
The web is a service built on top of the internet. HTTP is a protocol built on top of TCP/IP (TCP/IP handles the connection, sending/routing/transmitting of data, etc. … while HTTP is the message).
So… previously, we made a simple web server using nothing more than the net
module. The TCP/IP part was taken care of by the module, but we had to build http on top of it. That meant: →
Also, it had a lot of shortcomings:
Sooo… let's use another module that takes care of the http bits for us. The built-in HTTP module gives us: →
(From the docs, on the http module) is
How do we bring in a module in node? →
Use the require
function.
// http is a core node module
// it's compiled in to the node binary
const http = require('http');
How can we see what's in the node module? →
Just try importing it in the interactive shell, and typing out http:
const http = require('http');
http
We'll be going over:
When reading node documentation, note that:
functionName(requiredArg1, [optionalArg2], [optionalArg3]);
// an example from the docs
response.writeHead(statusCode, [reasonPhrase], [headers])
It's pretty spartan:
http.STATUS_CODES
http.createServer
http.STATUS_CODES is an object that contains:
{ '100': 'Continue',
'101': 'Switching Protocols',
'102': 'Processing',
'200': 'OK',
'201': 'Created',
.
.
'418': 'I\'m a teapot',
.
.
'510': 'Not Extended',
'511': 'Network Authentication Required' }
createServer returns a new web server object:
http.createServer([requestListener])
request
object and a response
objectThe server object that results from calling http.createServer
is simply an object that emits (generates) events, some of which include:
Additionally, some useful methods that it has are:
http.createServer
binds a callback function to a request
event. What are the two arguments that this callback function takes? →
http.IncomingMessage
http.ServerResponse
http.IncomingMessage is an object that represents a client's HTTP request.
https.ServerResponse is an object that represents the HTTP response that the server will return.
Some useful properties and methods that a ServerResponse
object has are:
writeHead
wasn't called) to the clientwriteHead
wasn't called) to the clientwriteHead
wasn't calledwriteHead
wasn't called)writeHead(200, {'Content-Type':'text/plain'})
Number
)end()
is calledwrite()
or response.end()
before writeHead
, the implicit headers will be determined and writeHead
will be called with those headersend('<!DOCTYPE html><html><body>hello</body></html>')
end()
, must be called on each responsewriteHead()
will (either explicitly or implicitly) be called before endend()
must be called on each responseLet's try writing our own web server, with help from the http module! →
const http = require('http');
const port = 3000;
http.createServer(handleRequest).listen(port);
console.log('starting server on ' + port);
function handleRequest(req, res) {
const responseStatusCode = 200;
res.writeHead(responseStatusCode, {'Content-Type':'text/plain'});
res.end('hello');
}
Let's try adding some logging. On the server side, output the requested url to the console whenever a request is made. →
// in handleRequest
console.log(req.url);
How about sending back some html? →
First let's try actually sending back html in the body… →
// in handleRequest
res.end('<!DOCTYPE html>Hi there!');
What happened? → The content type needs to be changed to text/html
Check out the rfc… here are few that you'll commonly see:
cache-control:no-cache
content-encoding:gzip
content-type:text/html; charset=utf-8
date:Mon, 29 Sep 2014 01:15:00 GMT
server:cloudflare-nginx
status:200 OK
vary:Accept-Encoding
version:HTTP/1.1
Which header(s) will fix our problem, again? →
// change Content-Type header to text/html
res.writeHead(200, {'Content-Type':'text/html'});
Node's primary programming paradigm is event driven programming. Event driven programming is a way of programming where:
Great. So, usually there's more than one page on a site, so let's figure out how to serve up additional URLs. →
/
, /home
, /about
, /about/
, /about?q=something
, /blog
In handleResponse:
→
const resCode,
body,
headers = {'Content-Type':'text/html'},
path = req.url.toLowerCase();
if(path === '/about') {
resCode = 200;
body = 'made with node';
} else if (path === '/' || path === '/home') {
resCode = 200;
body = 'homepage v2, now with routing';
} else {
resCode = 404;
body = 'nothing to see here'
}
res.writeHead(resCode, headers);
res.end(body);
console.log(req.url, res.statusCode);
Hardcoding html in our code, doesn't seem great. How about we just read some files, and serve them up? →
The code node module, fs, allows general file I/O. It allows the reading and manipulation of files.
As usual, bring it in to your program by using require
.
const fs = require('fs');
In order to have our web server read static files, we'll use fs.readFile(). fs.readFile() asynchronously reads the entire contents of a file. You know what that means, right? → callback time!
fs.readFile(filename, [options], callback)#
{'encoding':'utf-8'}
Let's try printing out the contents of a file… →
const fs = require('fs');
fs.readFile('./public/index.html', {'encoding':'utf-8'}, function(err, data) {
if (err) {
console.log('uh oh!');
} else {
console.log(data);
}
});
We'll serve 3 pages and an image:
Define a function called serverStatic
… it should:
We'll have to create a couple of folders and files: →
mkdir -p public/img
Sample body for index.html, home.html
homepage v3, now with static files!
And, of course, drop magicman.png into img.
function serveStatic(res, path, contentType, resCode) {
fs.readFile(path, function(err, data) {
if (err) {
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('500 - Internal Error');
} else {
res.writeHead(resCode, { 'Content-Type': contentType });
res.end(data);
}
});
}
Let's modify our handleRequest so that it uses serveStatic. →
const http = require('http'),
fs = require('fs');
const port = 3000;
http.createServer(handleRequest).listen(3000);
console.log('Started server on port', port);
function handleRequest(req, res) {
if (req.url === '/home' || req.url === '/') {
serveStatic(res, './public/index.html', 'text/html', 200);
} else if (req.url === '/about') {
serveStatic(res, './public/about.html', 'text/html', 200);
} else if (req.url === '/img/magicman.png') {
serveStatic(res, './public/img/magicman.png', 'image/png', 200);
} else {
serveStatic(res, './public/404.html', 'text/html', 404);
}
}
Let's try it.
In this case, not really (mainly because of the way we've structured our program):
fs.readFile(path, handleFileRead.bind(
{res:res, contentType:contentType, resCode:resCode}));
We'll see a better way to do this later…
Great. We just implemented a terrible static file web server (we already have Apache, Nginx, etc. to handle that).
What was difficult to deal with… and what were some shortcomings? →