Выбрать главу

Следующий код, будучи запущенным одновременно с сервером, отправит запрос на сервер и выведет полученный ответ:

var http = require("http");

var request = http.request({

  hostname: "localhost",

  port: 8000,

  method: "POST"

}, function(response) {

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

    process.stdout.write(chunk.toString());

  });

});

request.end("Hello server");

Пример пишет в process.stdout (стандартный вывод процесса, являющийся потоком с возможностью записи), а не в console.log. Мы не можем использовать console.log, так как он добавляет лишний перевод строки после каждого куска кода – это здесь не нужно.

Простой файловый сервер

Давайте скомбинируем наши новые знания о серверах HTTP и работе с файловой системой, и наведём мостик между ними: HTTP-сервер, предоставляющий удалённый доступ к файлам. У такого сервера много вариантов использования. Он позволяет веб-приложениям хранить и делиться данными, или может дать группе людей доступ к набору файлов.

Когда мы относимся к файлам, как к ресурсам HTTP, методы GET, PUT и DELETE можно использовать для чтения, записи и удаления файлов. Мы будем интерпретировать путь в запросе как путь к файлу.

Нам не надо открывать доступ ко всей файловой системе, поэтому мы будем интерпретировать эти пути как заданные относительно корневого каталога, и это будет каталог запуска скрипта. Если я запущу сервер из /home/marijn/public/ (или C:\Users\marijn\public\ на Windows), то запрос на /file.txt должен указать на /home/marijn/public/file.txt (или C:\Users\marijn\public\file.txt).

Программу мы будем строить постепенно, используя объект methods для хранения функций, обрабатывающих разные методы HTTP.

var http = require("http"), fs = require("fs");

var methods = Object.create(null);

http.createServer(function(request, response) {

  function respond(code, body, type) {

    if (!type) type = "text/plain";

    response.writeHead(code, {"Content-Type": type});

    if (body && body.pipe)

      body.pipe(response);

    else

      response.end(body);

  }

  if (request.method in methods)

    methods[request.method](urlToPath(request.url),

                            respond, request);

  else

    respond(405, "Method " + request.method

            " not allowed.");

}).listen(8000);

Этот код запустит сервер, возвращающий ошибки 405 – этот код используется для обозначения того, что запрошенный метод сервером не поддерживается.

Функция respond передаётся функциям, обрабатывающим разные методы, и работает как обратный вызов для окончания запроса. Она принимает код статуса HTTP, тело, и, возможно, тип содержимого. Если переданное тело – поток с возможностью чтения, у него будет метод pipe, который используется для передачи читаемого потока в записываемый. Если нет – предполагается, что это либо null (тело пустое), или строка, и тогда она передаётся напрямую в метод ответа end.

Чтобы получить путь из URL в запросе, функция urlToPath, используя встроенный модуль Node “url”, разбирает URL. Она принимает имя пути, нечто вроде /file.txt, декодирует, чтобы убрать экранирующие коды %20, и вставляет в начале точку, чтобы получить путь относительно текущего каталога.

function urlToPath(url) {

  var path = require("url").parse(url).pathname;

  return "." + decodeURIComponent(path);

}

Вам кажется, что функция urlToPath небезопасна? Вы правы. Вернёмся к этому вопросу в упражнениях.

Мы устроим метод GET так, чтобы он возвращал список файлов при чтении директории, и содержимое файла при чтении файла.

Вопрос на засыпку – какой тип заголовка Content-Type мы должны возвращать, читая файл. Поскольку в файле может быть всё, что угодно, сервер не может просто вернуть один и тот же тип для всех. Но NPM с этим может помочь. Модуль mime (индикаторы типа содержимого файла вроде text/plain также называются MIME types) знает правильный тип для огромного количества расширений файлов.

Запустив следующую команду npm в директории, где живёт скрипт сервера, вы сможете использовать require("mime") для запросов к библиотеке типов.

$ npm install mime

npm http GET https://registry.npmjs.org/mime

npm http 304 https://registry.npmjs.org/mime

mime@1.2.11 node_modules/mime

Когда запрошенного файла не существует, правильным кодом ошибки для этого случая будет 404. Мы будем использовать fs.stat для возврата информации по файлу, чтобы выяснить, есть ли такой файл, и не директория ли это.