@swarmmachina/swm-core v5.1.2

swm-core

HTTP / WebSocket server

Высокопроизводительный HTTP- и WebSocket-сервер для Node.js поверх нативного транспорта swm-uws. Один экземпляр сервера может обслуживать оба протокола.

Выбирайте swm-core, если нужен готовый серверный слой: маршрутизация и before hooks, контексты запросов, лимиты body и времени, streaming, backpressure и корректное завершение работы.

Приложениеswm-coreswm-uwsuWebSockets C++
22 / 24
Node.js
HTTP + WS
один сервер
ESM
формат модуля
MPL-2.0
лицензия

Установка и быстрый старт

Shellinstall
npm install @swarmmachina/swm-core
JavaScriptquick-start.js
import Server from '@swarmmachina/swm-core'

const server = new Server({
  port: 3000,
  http: {
    routes: [
      {
        method: 'get',
        path: '/health',
        handler: () => ({ ok: true })
      }
    ]
  },
  ws: {
    onUpgrade: () => ({}),
    onMessage: (ctx, message, isBinary) => {
      ctx.send(message, isBinary)
    }
  }
})

await server.listen()
  • Синхронный handler оставляйте синхронным: Promise-путь нужен только при реальном await.
  • http.onRequest и http.routes взаимоисключающие. Пустой http: {} возвращает предсказуемый 404.
  • HTTP и WebSocket можно включать независимо; хотя бы один из них должен быть настроен.
  • ws.onUpgrade обязателен: верните объект user data для принятия соединения или null для отказа. Тот же объект становится ctx.data без копирования; подпротокол выбирает отдельный selectProtocol.
  • Все размеры задаются в байтах. По умолчанию HTTP body ограничен 1 МиБ на запрос и общим бюджетом 256 МиБ.
  • В v5.1.2 ctx.bodyStream() даёт поток owned Buffer chunks с backpressure для крупных upload. Создайте его до первого await, не сочетайте с prefetch и другими readers; maxStreamBodySize ограничивает один поток, а тело не учитывается в maxBodyBudget.

Что предоставляет сервер

Нативный транспорт
HTTP и WebSocket проходят через @swarmmachina/swm-uws без промежуточного JS transport layer.
Маршрутизация и hooks
Универсальный onRequest или декларативные method/path routes с :param, wildcard /* и цепочками before.
Контроль ресурсов
Пулы HttpContext, конечный body budget, лимиты buffered и streaming body на route, 30-секундный async timeout и нативная transport policy для заголовков, body rate, соединений и trusted proxy.
Потоки и backpressure
ctx.bodyStream() для upload с backpressure, stream, startStreaming, tryEnd, onWritable и числовой статус отправки WebSocket.
Управление соединениями
Безопасный async upgrade: ctx.data сохраняет точную идентичность возвращённого объекта; доступны selectProtocol, pub/sub, адресация через connectionKey, graceful close и force terminate.
Ответы и lifecycle
prepareHeaders, CORS и static-file helpers; shutdown(timeout) ждёт активные запросы и соединения, close() завершает их немедленно.

Runtime и платформы

СредаСтатусПримечание
Node.js 22 / 24ПоддерживаетсяДругие major-версии отклоняются engines constraint.
Linux x64 + glibcПоддерживаетсяДля контейнеров используйте bookworm/slim.
Windows x64ПоддерживаетсяГотовая нативная сборка.
macOS arm64 / x64ПоддерживаетсяApple Silicon и Intel.
Linux ARM64 / Windows ARM64Нет сборкиНе входят в текущую матрицу prebuilds.
Alpine / muslНе поддерживаетсяИспользуйте glibc-based image.
TLS / permessage-deflateОтключеноЗавершайте TLS перед приложением.

Карта API

Server

Жизненный цикл, pub/sub и адресуемые WebSocket-соединения.

listen()shutdown(timeout)close()publish(topic, message)getSubscribersCount(topic)sendTo(key, message)closeConnection(key)terminateConnection(key)hasConnection(key)getConnection(key)connectionCountactiveHttpactiveWseffectiveConfigbindingCapabilitieshttpErrorDeliveryStats

HttpContext

Явные get* readers делят один request cache; из коротких aliases в v5 остался только buffer() для body().

headersreplied / abortedgetIP()getMethod()getUrl()getQuery()getQuery(name)getParameter(name)getReqHeader(name)getHeaders()getContentLength()body()buffer() — alias body()json()text()bodyStream()setStatus()setHeader()appendHeader()setHeaders()flushHeaders()send()sendJson()sendText()sendBuffer()sendError()reply()replyAndClose()terminate()stream()startStreaming()write()end()tryEnd()onWritable()getWriteOffset()

WSContext

Операции одного WebSocket-соединения.

datawskeysend()end()terminate()subscribe()unsubscribe()publish()decode()

Helpers

Переиспользуемые заголовки, CORS и раздача статических файлов.

defineConfig()prepareHeaders()cors()serveStatic()

Полные сигнатуры, типы и примеры находятся в README и TypeScript declarations исходного репозитория.

Body lifetime, лимиты и backpressure

swm-core намеренно оставляет стоимость и время жизни данных видимыми. Эти правила важны для корректного production-кода.

Lazy body до первого await

В режиме по умолчанию вызовите ctx.body(), buffer(), text() или json() до первой асинхронной операции. Иначе uWS-события body могут прийти до регистрации reader.

Prefetch для async auth

Включите http.prefetch или route.prefetch, если сначала нужно дождаться БД/авторизации, а затем читать body. Байты собираются заранее, JSON всё равно парсится лениво.

Потоковый upload создавайте до await

Для большого request body вызовите ctx.bodyStream() синхронно до первого await в hook или handler и передайте поток в pipeline(). Не сочетайте его с prefetch, body(), buffer(), text() или json(). maxStreamBodySize и более узкий route/per-call лимит ограничивают размер одного upload; stream.destroy() нужен, если чтение прекращается раньше.

Ограничивайте память и время

Значения задаются в байтах: maxBodySize по умолчанию 1 МиБ (максимум 64 МиБ), maxBodyBudget — 256 МиБ. maxStreamBodySize по умолчанию равен maxBodySize и не учитывается в общем body budget. 0 означает нулевую ёмкость, null явно отключает общий учёт. Превышение запроса даёт 413, исчерпание бюджета — 503.

Учитывайте защитные дефолты v4.1

requestTimeoutMs по умолчанию завершает зависшую async-цепочку через 30 секунд с 408. WebSocket ограничен 1 МиБ на входящее сообщение и 64 КиБ backpressure на соединение; медленный сокет по умолчанию закрывается после drop.

Изолируйте доставку ошибок

В v5.1 http.onError получает неизменяемое body-free event, а не HttpContext. Укажите errorDelivery.headers, query и includeIp только для необходимой диагностики; queueLimit, concurrency и timeoutMs ограничивают удалённый sink. Смотрите httpErrorDeliveryStats, включая dropped и timedOut.

Сохраняйте только нужные заголовки

http, route и ws prefetchHeaders сохраняют выбранные поля в ctx.headers или meta.headers для работы после await. Используйте 'all' только когда нужен полный набор: selective prefetch дешевле и явнее.

Используйте явные request readers

В v5 используйте getIP(), getMethod(), getUrl(), getQuery(), getParameter(), getReqHeader(), getHeaders() и getContentLength(): короткие aliases удалены. getHeaders() возвращает изолированную копию полного набора и после await требует заранее сохранённых данных.

Ограничивайте нативный transport

transport задаёт maxHeaderSize/count, таймауты headers, keep-alive, body idle и response write, минимальную скорость body и trustedProxy. Значения валидируются до запуска сервера.

Проверяйте send status

WSContext.send() возвращает 1 при успехе, 0 при backpressure и 2, если сообщение отброшено из-за лимита.

Не удерживайте HttpContext

HttpContext переиспользуется между запросами. WSContext живёт всё соединение, но использовать его после onClose нельзя.

Задавайте границу доверия к proxy явно

transport.trustedProxy по умолчанию выключен. Включайте { header: 'x-real-ip' } или { header: 'x-forwarded-for', hops: N } только на listener за proxy, который перезаписывает выбранный заголовок; X-Forwarded-For считается справа. ctx.getIP() возвращает выбранный адрес, а без заголовка — адрес upstream peer. Не используйте сетевой адрес как аутентифицированную identity.

Частые вопросы

Короткие ответы на вопросы о выборе пакета, runtime-контрактах и production-ограничениях.

  1. Что такое swm-core?

    swm-core - экспериментальный HTTP- и WebSocket-сервер для Node.js 22 и 24 поверх нативного binding swm-uws. Он объединяет routing, request context, лимиты body, таймауты, backpressure и graceful shutdown в одном серверном API.

  2. Когда выбирать swm-core, а когда swm-uws?

    Для прикладного API, WebSocket gateway или обычного production-сервиса начинайте со swm-core. Берите swm-uws, если нужна совместимая низкоуровневая поверхность uWebSockets.js App() и команда готова сама владеть routing, lifetime request/response, лимитами и shutdown.

  3. Как сохранить HTTP-заголовки после await в swm-core 5.1?

    Укажите http.prefetchHeaders или route.prefetchHeaders со списком нужных полей. Для WebSocket upgrade используйте ws.prefetchHeaders. Выбранные заголовки доступны через ctx.headers или meta.headers после await; режим 'all' сохраняйте для случаев, где действительно нужен полный набор.

  4. Как безопасно отправлять HTTP-ошибки во внешний сервис?

    В v5.1 http.onError получает immutable HttpErrorEvent, а не переиспользуемый HttpContext. Настройте errorDelivery с конечными concurrency, queueLimit и timeoutMs; headers, query и includeIp включайте только явным allowlist. Состояние очереди и потери доступны через server.httpErrorDeliveryStats.

  5. Как читать body после асинхронной авторизации?

    Включите http.prefetch или route.prefetch: swm-core начнёт ограниченный сбор байтов до before/handler, а JSON распарсит только при вызове ctx.json(). В lazy-режиме вызовите ctx.body(), buffer(), text() или json() до первого await и сохраните Promise.

  6. Как оптимизировать большие upload в swm-core 5.1.2?

    Не материализуйте весь body: при выключенном prefetch создайте ctx.bodyStream() синхронно до первого await и передайте его в pipeline(). maxStreamBodySize ограничивает один поток, route/per-call лимит может только сузить его. Поток нельзя сочетать с body(), buffer(), text() или json(); если handler прекращает чтение раньше, вызовите stream.destroy(). Backpressure ограничивает очередь потока, но не заменяет лимит параллельных upload и бюджет downstream-хранилища.

  7. Как ограничить медленные или некорректные HTTP-клиенты?

    Передайте transport с явными maxHeaderSize, maxHeaderCount, headersTimeoutMs, keepAliveTimeoutMs, bodyIdleTimeoutMs, minBodyRateBytesPerSec и responseWriteTimeoutMs. Эти ограничения применяются нативным транспортом до или во время работы прикладного handler.

  8. Как получить IP клиента за доверенным reverse proxy?

    В swm-core 5.1.2 настройте transport.trustedProxy как { header: 'x-real-ip' } или { header: 'x-forwarded-for', hops: N }. Опция выключена по умолчанию: включайте её только на listener, доступном через proxy, который перезаписывает выбранный заголовок. X-Forwarded-For считается справа; если заголовка нет, ctx.getIP() возвращает адрес upstream peer.

  9. Поддерживает ли swm-core TLS, Alpine и ARM64 Linux?

    Нет. Текущая матрица поддерживает Node.js 22/24, Linux x64 с glibc, Windows x64 и macOS arm64/x64. TLS и permessage-deflate отключены, Alpine/musl и Linux/Windows ARM64 не имеют текущих prebuilds; TLS нужно завершать перед приложением.

Когда брать низкоуровневый binding

swm-core подходит большинству сервисов. Переходите к swm-uws, если нужна прямая совместимость с обычным App() API uWebSockets.js и вы готовы сами управлять lifetime request/response объектов.

JavaScripttransport-choice.js
// Высокоуровневый сервер
import Server from '@swarmmachina/swm-core'

// Низкоуровневый binding
import uWS from '@swarmmachina/swm-uws'
Нужен низкоуровневый API uWebSockets.js без серверных абстракций?Открыть swm-uws