Comment fonctionne un serveur de fichiers Node.js

Node.js permet de charger des pages ou des applications à partir d'un navigateur grâce à un script serveur simple.

Le script que nous présentons n'est pas différent dans son fonctionnement des différents scripts publiés sur le site officiel ou dans différents "tutoriels" ou pseudo-tutoriels sur Node.
Je parle de pseudo-tutoriel car les programmeurs ne font généralement pas la différence entre un manuel qui explique comment utiliser une chose et le tutoriel qui apprend son fonctionnement, en procédant par étapes et allant du plus simple au plus complexe. Quand un programmeur ajoute des commentaires à un listing, c'est un "tutoriel". On en est loin, et le code source lui-même, est-il écrit pour être facilement compris?
C'est particulièrement probant dans le cas de Node.js où tous les scripts de démonstration sont écrits sous une forme récursive qui oblige le lecteur à penser comme un processeur, ce qui le but ultime du hacker semble-t-il.

J'ai donc réécrit le serveur de pages en décomposant le script en fonctions élémentaires, ce qui permettra de mieux comprendre comment il fonctionne. Mais en fait mon but est d'accomplir un premier pas vers un script plus élaboré, un serveur d'outils fonctionnant localement et permettant d'utiliser des pages web comme interfaces à des exécutables, binaires ou non.

1) Création du serveur

var server = http.createServer(getFilename);
server.listen(1000);
console.log("Server available...");

Le paramètre de createServer est un callback qui sera activé lorsque le navigateur se connecte au port 1000, avec un nom de fichier en paramètre. En tapant par exemple dans la barre d'un navigateur:

127.0.0.1:1000/page.html

2) Analyse de l'URL et transmission du nom de fichier au système

function getFilename(request, response)
{
  var urlpath = url.parse(request.url).pathname;
  var localpath = path.join(process.cwd(), urlpath);
  fs.exists(localpath, function(result) { getFile(result, response, localpath)});
}

Cette fonction extrait le chemin de l'URL et passe ce paramètre à une seconde fonction qui va lire le fichier sur le serveur ou l'ordinateur local.
Pour cela on utilise les modules url et path.

3) Lecture du fichier

function getFile(exists, response, localpath)
{
  if(!exists) return sendError(404, '404 Not Found', response);
  fs.readFile(localpath, "binary",
    function(err, file){ sendFile(err, file, response);});
}

Le fichier est lu grâce au module fs qui réalise les opérations courantes dans le système de fichier, sauf l'exécution de programmes qui requiert un autre module. Notre fonction sendFile est appelée par readFile du module fs.

4) Envoi de la page

function sendFile(err, file, response)
{
  if(err) return sendError(500, err, response);
  response.writeHead(200);
  response.write(file, "binary");
  response.end();
} 

Le contenu du fichier est envoyé au navigateur qui l'affiche. Cela est fait par l'objet response créé en même temps que le serveur et que l'on transmet à chaque fonction.

5) Liste des modules utiles

http = require("http");
path = require("path");
url = require("url");
fs = require("fs");

On a vu le rôle de chacun d'eux précédemment, sauf http vu en introduction et qui permet d'utiliser le protocole HTTP pour échanger des données entre le serveur et le navigateur (ou autre agent utilisateur).

6) Le script complet:

http = require("http"),
path = require("path"),
url = require("url"),
fs = require("fs");

function sendError(errCode, errString, response)
{
  response.writeHead(errCode, {"Content-Type": "text/plain"});
  response.write(errString + "\n");
  response.end();
  return;
}

function sendFile(err, file, response)
{
  if(err) return sendError(500, err, response);
  response.writeHead(200);
  response.write(file, "binary");
  response.end();
}

function getFile(exists, response, localpath)
{
  if(!exists) return sendError(404, '404 Not Found', response);
  fs.readFile(localpath, "binary",
   function(err, file){ sendFile(err, file, response);});
}

function getFilename(request, response)
{
  var urlpath = url.parse(request.url).pathname; // following domain or IP and port
  var localpath = path.join(process.cwd(), urlpath); // if we are at root
  fs.exists(localpath, function(result) { getFile(result, response, localpath)});
}

var server = http.createServer(getFilename);
server.listen(1000);
console.log("Server available...");  

Vous pouvez tester ce serveur basique en chargeant le fichier page.html contenu dans l'archive. Le code source du script est le fichier server.js.

Précédemment: Introduction à Node.js. Description du système et l'intérêt de l'utiliser.
L'article suivant montre comment utiliser Node.js pour exécuter des scripts PHP localement depuis le navigateur.