12.2.1. Пример использования Node: HTTP-сервер

В примере 12.2 приводится реализация простого HTTP-сервера, основанная на особенностях интерпретатора Node. Она обслуживает файлы в текущем каталоге и дополнительно реализует два адреса URL специального назначения, которые обслуживаются особым образом. В этой реализации используется модуль «http», входящий в состав интерпретатора Node, и применяются API доступа к файлам и потокам ввода/вывода, демонстрировавшиеся выше. В примере 18.17, в главе 18, демонстрируется аналогичный специализированный НТТР-сервер.

Пример 12.2. HTTP-сервер, основанный на особенностях Node

// Простой NodeJS HTTP-сервер, обслуживающий файлы в текущем каталоге

// и реализующий два специальных адреса URL для нужд тестирования.

// Подключение к серверу выполняется по адресу http://localhost;8000

// или http://127.0-0.1:8000

// Сначала необходимо загрузить используемые модули

var http = require( http'); // API НТТР-сервера

var fs = require('fs'); // Для работы с локальными файлами

var server = new http.Server(); // Создать новый HTTP-сервер

server.listen(8000); // Прослушивать порт 8000.

// Для регистрации обработчиков событий в Node используется метод "оп()".

// Когда сервер получает новый запрос, для его обработки вызывается функция,

server.on("request", function (request, response) {

  // Выполнить разбор адреса URL

  var url = require("url").parse(request.url);

  // Специальный адрес URL, который вынуждает сервер выполнить задержку перед ответом.

  // Это может быть полезно для имитации работы с медленным сетевым подключением,

  if (url.pathname === "/test/delay") {

    // Величина задержки определяется из строки запроса

    // или устанавливается равной 2000 миллисекунд

    var delay = parseInt(url.query) || 2000;

    // Установить код состояния и заголовки ответа

    response.writeHead(200, {"Content-Type": "text/plain; charset=UTF-8 "});

    // Начать отправку ответа немедленно

    response.write("Задержка на " + delay + " миллисекунд...”);

    // А затем завершить другой функцией, которая будет вызвана позже.

    setTimeout(function() { 

        response.write("roTOBO.");

        response.end();

    }, delay);

  }

  // Если запрошен адрес "/test/mirror", отправить запрос обратно целиком.

  // Удобно, когда необходимо увидеть тело и заголовки запроса,

  else if (url.pathname === "/test/mirror") {

    // Код состояния и заголовки ответа

    response.writeHead(200,{"Content-Type": "text/plain; charset=UTF-8"});

    // Вставить в ответ тело запроса

    response.write(request.method + " " + request.url +

      " HTTP/" + request.httpVersion + " ");

    // И заголовки запроса

    for(var h in request.headers) {

      response.write(h + ": + request.headers[h] + " ");

    }

    response.write(" ");

    // За заголовками следует дополнительная пустая строка

    // Завершение отправки ответа выполняется следующими функциями-обработчиками:

    // Если в chunk передается тело запроса, вставить его в ответ,

    request.on("data", function(chunk) {

      response.write(chunk); });

    // Когда достигнут конец запроса, ответ также завершается,

    request.on("end", function(chunk) { response.end(); });

  }

  // Иначе обслужить файл из локального каталога,

  else {

    // Получить имя локального файла и определить тип его содержимого по расширению,

    var filename = url.pathname.substring(1); // удалить начальный /

    var type;

    switch(filename.substring(filename.lastlndexOf(".")+1)) { // расшир.

    case "html":

    case "htm":      type = "text/html; charset=UTF-8"; break;

    case "js":       type = "application/JavaScript;charset=UTF-8"; break;

    case "css":      type = "text/css; charset=UTF-8"; break;

    case "txt":      type = "text/plain; charset=UTF-8"; break;

    case "manifest": type = "text/cache-manifest; charset=UTF-8"; break;

    default: type = "application/octet-stream"; break;

    }

    // Прочитать файл в асинхронном режиме и передать его содержимое единым блоком

    // в функцию обратного вызова. Для очень больших файлов лучше было бы

    // использовать API потоков ввода/вывода с функцией

    fs.createReadStream().fs.readFile(filename, function(err, content) {

      if (err) { // Если no каким-то причинам невозможно прочитать файл

        response.writeHead(404, { // Отправить 404 Not Found

        "Content-Type": "text/plain; charset=UTF-8"});

        response.write(err.message); // Тело сообщения об ошибке

        response.end(); // Завершить отправку

      }

      else { // Иначе, если файл успешно прочитан.

        response.writeHead(200, // Установить код состояния и тип MIME

          {"Content-Type": type});

        response.write(content); // Отправить содержимое файла

        response.end(); // И завершить отправку

      }

    }):

  }

});