Внутренняя структура объекта запроса

HTTP-запрос в Iron представлен как высокоуровневая абстракция над входящим потоком Node.js, скрывающая детали транспорта и предоставляющая унифицированную структуру данных. Внутри этот объект формируется поэтапно: от сырых TCP-данных до полностью разобранного представления с маршрутизацией, заголовками, телом и метаданными запроса.

Внутренне объект запроса можно разделить на несколько логических слоёв:

  • транспортный уровень (сырой входящий поток)
  • уровень HTTP-метаданных
  • уровень маршрутизации
  • уровень тела запроса
  • вычисляемые поля (lazy properties)

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

Транспортный слой и первичное представление

На самом нижнем уровне запрос опирается на поток, аналогичный http.IncomingMessage. На этом этапе доступны только базовые данные:

  • метод HTTP (GET, POST, PUT и т.д.)
  • URL в исходном виде
  • заголовки
  • сокет соединения
  • версия HTTP протокола

Эти данные не интерпретируются, а лишь фиксируются как исходное состояние запроса. Важно, что на этом уровне тело запроса ещё не прочитано и не буферизовано.

Нормализация URL и разбиение структуры адреса

После первичной фиксации данных происходит разбор URL. Внутри Iron URL преобразуется в структурированный объект:

  • pathname — путь без query-параметров
  • queryString — строковая часть после ?
  • query — объект разобранных параметров
  • hash — фрагмент (если применимо)

При этом разбор query часто реализуется лениво. Это означает, что строка параметров хранится в исходном виде до первого обращения к request.query.

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

Заголовки HTTP как нормализованная карта

Заголовки в Iron представлены не как сырый объект Node.js, а как нормализованная структура:

  • ключи приводятся к нижнему регистру
  • значения заголовков могут быть строками или массивами строк
  • некоторые заголовки декодируются автоматически (например, content-type)

Внутри используется структура, близкая к Map, что позволяет:

  • сохранять порядок поступления заголовков
  • обеспечивать быстрый доступ по ключу
  • поддерживать множественные значения

Дополнительно часто формируется кэш популярных заголовков:

  • content-type
  • content-length
  • authorization
  • user-agent

Это ускоряет доступ к наиболее часто используемым данным без повторного поиска в общей карте.

Структура тела запроса

Тело запроса не загружается автоматически. Вместо этого создаётся потоковая обёртка, которая может быть преобразована в зависимости от типа контента.

Внутренне тело представлено в виде одного из вариантов:

  • Buffer (для бинарных данных)
  • строка (для текстовых payload)
  • объект (после JSON-десериализации)
  • поток (stream mode)

Процесс обработки выглядит следующим образом:

  1. чтение данных из сокета
  2. накопление чанков
  3. определение типа контента
  4. применение парсера
  5. кэширование результата

Парсинг JSON, form-data или urlencoded данных обычно выполняется только при первом обращении к request.body.

Контент-нейгоциация и определение парсера

Одним из ключевых элементов внутренней структуры является определение парсера тела запроса. Оно базируется на заголовке Content-Type.

Примеры внутреннего сопоставления:

  • application/json → JSON parser
  • application/x-www-form-urlencoded → URL encoded parser
  • multipart/form-data → multipart parser
  • отсутствие типа → raw buffer

Это сопоставление хранится в виде таблицы стратегий. При необходимости система может быть расширена пользовательскими парсерами, которые регистрируются в реестре middleware.

Параметры маршрута

После прохождения слоя маршрутизации объект запроса дополняется параметрами пути.

Если маршрут задан как:

/users/:id/posts/:postId

то внутри запроса появляется структура:

  • params.id
  • params.postId

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

Метаданные запроса

Помимо стандартных HTTP-данных, объект запроса содержит набор вычисляемых метаданных:

  • protocol (http/https)
  • secure (булевый флаг)
  • hostname
  • ip (IP клиента)
  • ips (цепочка прокси)
  • subdomains

Определение IP-адреса может учитывать заголовки:

  • x-forwarded-for
  • x-real-ip

При этом порядок доверенных прокси играет ключевую роль, так как влияет на корректность вычисления реального клиента.

Lazy-вычисления и мемоизация

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

  • request.body
  • request.query
  • request.cookies
  • request.ip

После первого вычисления результат сохраняется в кеше внутри объекта запроса. Повторные обращения не запускают повторный парсинг.

Такой подход снижает нагрузку при больших потоках запросов, особенно в API с минимальным использованием данных запроса.

Куки и их разбор

Cookies не извлекаются автоматически в базовой структуре. При первом обращении к cookie-объекту выполняется:

  1. чтение заголовка cookie
  2. разбиение строки по ;
  3. декодирование значений
  4. формирование объекта ключ-значение

Внутренне cookies часто хранятся отдельно от основных заголовков, чтобы ускорить доступ и избежать повторного парсинга строки.

Состояние и неизменяемость

После инициализации объект запроса обычно считается частично неизменяемым:

  • заголовки не изменяются
  • URL не модифицируется
  • параметры маршрута фиксированы

Однако некоторые поля могут дополняться middleware:

  • request.state — пользовательское состояние
  • request.context — контекст выполнения

Эти структуры отделены от системных данных, чтобы избежать конфликтов между библиотечным и пользовательским кодом.

Потоковая модель и потребление тела

Если тело запроса не буферизуется заранее, оно может быть обработано как поток. В этом режиме:

  • данные поступают чанками
  • обработка происходит по мере чтения
  • возможна ранняя остановка чтения

Это особенно важно для больших файлов и streaming API, где полная загрузка тела в память недопустима.

Внутренне поток оборачивается в контролируемый интерфейс, который позволяет:

  • ограничивать размер payload
  • устанавливать таймауты чтения
  • прерывать соединение при ошибке

Ошибки разбора и их представление

Ошибки, возникающие при обработке запроса, сохраняются внутри объекта в стандартизированном виде:

  • ошибка парсинга JSON
  • превышение лимита тела
  • некорректный multipart
  • неподдерживаемый content-type

Каждая ошибка имеет:

  • код
  • сообщение
  • контекст (например, часть тела или заголовок)

Это позволяет middleware принимать решения без необходимости повторного анализа входных данных.

Итоговая структура объекта запроса

После прохождения всех этапов объект запроса представляет собой объединённую структуру:

  • raw HTTP данные
  • нормализованные заголовки
  • разобранный URL
  • параметры маршрута
  • тело запроса (или поток)
  • cookies
  • метаданные клиента
  • пользовательский контекст

Эта структура формируется инкрементально, что позволяет Iron эффективно обрабатывать большое количество параллельных запросов без избыточной нагрузки на парсинг и память.