KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Марейн Хавербеке - Выразительный JavaScript

Марейн Хавербеке - Выразительный JavaScript

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Марейн Хавербеке, "Выразительный jаvascript" бесплатно, без регистрации.
Перейти на страницу:

Для предотвращения завершения соединений по таймауту (по истечению времени неактивные соединения обрываются), технология длинных запросов обычно устанавливает максимальное время для каждого запроса, по прошествии которого сервер в любом случае ответит, даже если ему нечего сообщить, а затем клиент запустит новый запрос. Периодическое обновление запроса делает технику более надёжной, позволяя клиентам восстанавливаться после временных обрывов или проблем на сервере.

У занятого сервера, использующего длинные запросы, могут висеть открытыми тысячи запросов, а, следовательно, и TCP-соединений. Node хорошо подходит для такой системы, потому, что он позволяет с лёгкостью управлять многими соединениями без создания отдельных потоков.

Интерфейс HTTP

Перед тем, как мы начнём делать сервер или клиент, подумаем об их точке соприкосновения: интерфейсе HTTP, через который они взаимодействуют.

Интерфейс будет основан на JSON, и, как и в файловом сервере в главе 20, мы будем с выгодой использовать методы HTTP. Интерфейс сосредоточен вокруг пути /talks. Пути, которые не начинаются с /talks, будут использоваться для отдачи статичных файлов – HTML и JavaScript, определяющих клиентскую часть.

Запрос GET к /talks возвращает документ JSON типа этого:

{"serverTime": 1405438911833,

 "talks": [{"title": "Unituning ",

            "presenter": "Васисуалий",

            "summary": "Украшаем свой моноцикл",

            "comment": []}]}

Поле serverTime используется для надёжности длинных запросов. Вернёмся к нему позже.

Создание новой темы происходит через запрос PUT к URL вида /talks/Unituning, где часть после второго слэша – название темы. Тело запроса PUT должно содержать объект JSON, в котором описаны свойства presenter и summary.

Поскольку заголовки тем могут содержать пробелы и другие символы, которые нельзя вставлять в URL, при создании URL их надо закодировать при помощи функции encodeURIComponent.

console.log("/talks/" + encodeURIComponent("How to Idle"));

// → /talks/How%20to%20Idle

Запрос на создание темы может выглядеть так:

PUT /talks/How%20to%20Idle HTTP/1.1

Content-Type: application/json

Content-Length: 92


{«presenter»: «Даша»,

 «summary»: «Неподвижно стоим на моноцикле»}

Такие URL поддерживают запросы GET для получения JSON-представления темы и DELETE для удаления темы.

Добавление комментария происходит через POST-запрос к URL вида /talks/Unituning/comments, с объектом JSON, содержащим свойства author и message в теле запроса.

POST /talks/Unituning/comments HTTP/1.1

Content-Type: application/json

Content-Length: 72


{«author»: «Alice»,

 «message»: «Will you talk about raising a cycle?»}

Для поддержки длинных запросов, запросы GET к /talks могут включать параметр под именем changesSince, показывающий, что клиенту нужны обновления, случившиеся после заданной точки во времени. Когда обновления появляются, они сразу же возвращаются. Когда их нет, запрос задерживается, пока что-нибудь не случится, или пока не пройдёт заданный период времени (мы зададим 90 секунд).

Время используется в формате количества миллисекунд с начала 1970 года, в том же формате, что возвращает Date.now(). Чтобы удостовериться, что клиент получает все обновления, и не получает одно и то же обновление дважды, клиент должен передать время, в которое он в последний раз получил информацию с сервера. Часы сервера могут не совпадать с клиентом, и даже если б они совпадали, клиент не мог бы знать точное время, в которое сервер отправлял ответ, потому что передача данных по сети занимает время.

Поэтому в ответах на запросы GET к /talks и существует свойство serverTime. Оно сообщает клиенту точное время по часам сервера, когда были созданы передаваемые данные. Клиент просто сохраняет время и передаёт его вместе со следующим запросом, чтобы убедиться, что он получает только те обновления, которых ещё не получал.

GET /talks?changesSince=1405438911833 HTTP/1.1


(прошло время)


HTTP/1.1 200 OK

Content-Type: application/json

Content-Length: 95


{«serverTime»: 1405438913401,

 «talks»: [{«title»: «Unituning»,

 «deleted»: true}]}

Когда тема меняется, создаётся или комментируется, в ответ на следующий запрос включается полная информация о теме. Когда тема удаляется, включаются только название и свойство deleted. Клиент может добавлять темы с заголовками, которые он ещё не видел, к странице, обновлять темы, которые он уже показывает, и удалять темы, которые были удалены.

Протокол, описываемый в этой главе, не осуществляет контроль доступа. Каждый может комментировать, менять и удалять темы. Так как интернет полон хулиганов, размещение такой системы в онлайне без защиты, скорее всего, закончится плохо.

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

Сервер

Начнём с написания серверной части программы. Код работает на Node.js

Маршрутизация

Для запуска сервера будет использоваться http.createServer. В функции, обрабатывающей новый запрос, мы должны различать запросы (определяемые методом и путём), которые мы поддерживаем. Это можно сделать через длинную цепочку if / else, но можно и красивее.

Маршрутизатор – компонент, помогающий распределить запрос к функции, которая может его обработать. Можно сказать маршрутизатору, что запросы PUT с путём, совпадающим с регуляркой /^/talks/(/+)$/ (что совпадает с /talks/, за которым идёт название темы), могут быть обработаны заданной функцией. Кроме того, он может помочь извлечь осмысленные части пути, в нашем случае – название темы, заключённое в кавычки, и передать их вспомогательной функции.

В NPM есть много хороших модулей маршрутизации, но тут мы сами себе такой напишем, чтобы продемонстрировать принцип его работы.

Вот файл router.js, который будет запрашиваться через require из модуля сервера:

var Router = module.exports = function() {

  this.routes = [];

};


Router.prototype.add = function(method, url, handler) {

  this.routes.push({method: method,

                    url: url,

                    handler: handler});

};


Router.prototype.resolve = function(request, response) {

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


  return this.routes.some(function(route) {

    var match = route.url.exec(path);

    if (!match || route.method != request.method)

      return false;


    var urlParts = match.slice(1).map(decodeURIComponent);

    route.handler.apply(null, [request, response]

                                .concat(urlParts));

    return true;

  });

};

Модуль экспортирует конструктор Router. Объект router позволяет регистрировать новые обработчики с методом add, и распределять запросы методом resolve.

Последний вернёт булевское значение, показывающее, был ли найден обработчик. Метод some массива путей будет пробовать их по очереди (в порядке, в каком они были заданы), и остановится с возвратом true, если путь найден.

Функции обработчиков вызываются с объектами request и response. Когда регулярка, проверяющая URL, возвращает группы, то представляющие их строки передаются в обработчик в качестве дополнительных аргументов. Эти строчки надо декодировать из URL-стиля %20.

Выдача файлов

Когда тип запроса не совпадает ни с одним из типов, которые обрабатывает маршрутизатор, сервер должен интерпретировать его как запрос файла из общей директории. Можно было бы использовать файловый сервер из главы 20 для выдачи этих файлов, но нам не нужна поддержка PUT и DELETE, зато нам нужны дополнительные функции типа поддержки кеширования. Поэтому, давайте использовать проверенный и протестированный файловый сервер из NPM.

Я выбрал ecstatic. Это не единственный сервер на NPM, но он хорошо работает и удовлетворяет нашим требованиям. Модуль ecstatic экспортирует функцию, которую можно вызвать с объектом конфигурации, чтобы она выдала функцию обработчика. Мы используем опцию root, чтобы сообщить серверу, где нужно искать файлы. Обработчик принимает параметры request и response, и его можно передать напрямую в createServer, чтобы создать сервер, который отдаёт только файлы. Но сначала нам нужно проверить те запросы, которые мы обрабатываем особо – поэтому мы обёртываем его в ещё одну функцию.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*