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

У занятого сервера, использующего длинные запросы, могут висеть открытыми тысячи запросов, а, следовательно, и 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, работающие локально. Такой прокси можно настроить, чтобы он спрашивал имя и пароль пользователя, и вы могли бы устроить так, чтобы пароль был только у членов вашей группы.