Экспериментальная поддержка HTTP/3 уже встроена в основные браузеры и начинает потихоньку пробираться на сервера. А это значит, что уже можно полностью отказаться от использования в своих nodejs-приложениях от http-библиотеки и переключиться на http2. Насколько же отличается реализация http2-сервера от обычного http-сервера?

Под катом пример простого web-приложения, выполняющего типовые задачи (получение статики GET'ом, upload файлов, POST-запросы, server sent events) на серверах HTTP/1 и HTTP/2. HTTP/2 Server Push в данном примере не затрагивался. Приложение не использует внешних зависимостей (npm-пакетов), всё сделано при помощи собственного функционала nodejs.

Описание web-приложения

Клиентская часть - это ./index.html и ./favicon.ico (куда ж без него!). Раздача статики серверами - обычный GET-запрос. Функционал клиента позволяет загрузить файл на сервер (upload), получить список файлов (SSE - сервер передаёт клиенту имя одного файла раз в 200 мсек.), удалить выбранные файлы с сервера (типовой POST-запрос).

Скриншот тестового web-приложения.
Скриншот тестового web-приложения.

Запросы клиента обслуживают HTTP/1 и HTTP/2 серверы на портах 4010 и 4020. Каждый сервер реализован в собственном файле (./http1.mjs и ./http2.mjs), общий код вынесен в библиотеку ./lib.mjs. Оба сервера используют один и тот же каталог для хранения загруженных файлов.

Чтобы попробовать приложение, нужно склонировать на свой компьютер github-репозиторий flancer64/habr_node_http_servers и запустить сервера:

$ node http1.mjs & 
$ node http2.mjs & 

Точки входа в приложение:

Так как для доступа к HTTP/2-серверу через браузер нужно использовать шифрование (https), а сертификат в тестовом приложении самоподписной, то при первом входе на HTTP/2-сервер высвечивается предупреждение браузера, которое можно игнорить.

Резюме

Внезапно оказалось, что в nodejs для этих 4-функций (GET, POST, upload, SSE) различий на уровне кода между HTTP/1 и HTTP/2 нет. Вернее, единственное различие - это способ создания сервера. Вот код для HTTP/1:

import {createServer} from 'http';
import {onRequest} from './lib.mjs';
const server = createServer();
server.listen(4010);
server.on('error', (err) => console.dir(err));
server.on('request', onRequest);

А это код для HTTP/2:

import {createSecureServer} from 'http2';
import {readFileSync} from 'fs';
import {onRequest} from './lib.mjs';
const server = createSecureServer({
    key: readFileSync('key.pem'),
    cert: readFileSync('cert.pem')
});
server.listen(4020);
server.on('error', (err) => console.dir(err));
server.on('request', onRequest);

Всё, далее оба сервера используют один и тот же библиотечный код (функция onRequest), реализующий backend-логику web-приложения.

Как бы, это довольно очевидно: HTTP/2 является развитием HTTP/1 и базируется на принципе "запрос - ответ":

Обычный HTTP-запрос.
Обычный HTTP-запрос.

В случае с SSE получается, что ответ состоит из фрагментов и растянут во времени:

Запрос и ответы для SSE.
Запрос и ответы для SSE.

Этот базовый принцип отличает протокол HTTP (полудуплекс - сначала передача, потом приём) от WebSocket, который является полнодуплексным (приём и передача информации осуществляется независимо):

Двунаправленная передача информации для WebSocket.
Двунаправленная передача информации для WebSocket.

Но для меня стало неожиданным, что между http и http2 библиотеками в nodejs есть такая качественная обратная совместимость - просто выноси бизнес-логику отдельно и используй её на http или http2-сервере по своему желанию.

"That's all I have to say about that" (c)

Комментарии (0)