Почему HTTP-запрос в 1С обрезает тело запроса при отправке в ЕГАИС УТМ?

Программист 1С v8.3 (Управляемые формы) Торговля и дистрибуция
← На главную

При интеграции 1С с Универсальным транспортным модулем (УТМ) ЕГАИС разработчики нередко сталкиваются с ошибкой сервера (HTTP 500) при отправке объемных накладных (WayBill). Анализ сетевого трафика через отладчик или анализатор пакетов показывает неожиданную картину: тело запроса отправляется не полностью, а обрезается примерно наполовину. При этом небольшие запросы (например, запрос остатков) уходят без каких-либо сбоев с кодом ответа 200.

Разберем детально, почему возникает такое поведение, как протокол HTTP интерпретирует размер передаваемых данных и как корректно составить запрос в платформе «1С:Предприятие».

Анализ проблемы: почему обрезается ровно половина сообщения

Посмотрим на типичный фрагмент кода, приводящий к ошибке:


ЗаголовокHTTP = Новый Соответствие();
ЗаголовокHTTP.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
ЗаголовокHTTP.Вставить("Content-Length", XMLСтрока(СтрДлина(Body)));
ЗаголовокHTTP.Вставить("Connection", "Keep-alive");

ЗапросHTTP = Новый HTTPЗапрос("/opt/in/WayBill", ЗаголовокHTTP);
ЗапросHTTP.УстановитьТелоИзСтроки(Body, "UTF-8");
ОтветHTTP = НТТР.ОтправитьДляОбработки(ЗапросHTTP);

Обратим внимание на вычисление значения заголовка Content-Length. Здесь используется функция СтрДлина(Body), которая возвращает количество символов в строке.

По стандарту HTTP значение заголовка Content-Length определяет размер тела сущности исключительно в байтах (октетах), а не в символах. В кодировке UTF-8 символы кириллицы кодируются ровно двумя байтами. Если накладная содержит наименования товаров на русском языке, фактический размер строки в байтах оказывается практически в два раза больше, чем число символов. Веб-сервер УТМ ЕГАИС считывает из сокета ровно то количество байт, которое указано в Content-Length, и закрывает чтение. В результате XML-документ обрезается посередине, структура пакета нарушается, парсер не находит закрывающий boundary и возвращает ошибку 500 Internal Server Error.

Способ 1. Исключение ручной установки заголовка Content-Length (рекомендуемый способ)

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

Самое простое и надежное решение — полностью удалить ручную вставку заголовка Content-Length из коллекции заголовков:


ЗаголовокHTTP = Новый Соответствие();
ЗаголовокHTTP.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
ЗаголовокHTTP.Вставить("Connection", "Keep-alive");

ЗапросHTTP = Новый HTTPЗапрос("/opt/in/WayBill", ЗаголовокHTTP);
ЗапросHTTP.УстановитьТелоИзСтроки(Body, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
ОтветHTTP = НТТР.ОтправитьДляОбработки(ЗапросHTTP);

Способ 2. Корректный расчет размера тела в байтах

Если специфика внешнего шлюза или промежуточного прокси-сервера требует обязательного явного указания Content-Length до момента отправки, его необходимо рассчитывать в байтах. Для этого строку необходимо перевести в двоичные данные:


// Получаем двоичные данные строки в нужной кодировке
ДвоичныеДанныеТела = ПолучитьДвоичныеДанныеИзСтроки(Body, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

ЗаголовокHTTP = Новый Соответствие();
ЗаголовокHTTP.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
// Размер двоичных данных возвращает точный размер в байтах
ЗаголовокHTTP.Вставить("Content-Length", XMLСтрока(ДвоичныеДанныеТела.Размер()));

ЗапросHTTP = Новый HTTPЗапрос("/opt/in/WayBill", ЗаголовокHTTP);
ЗапросHTTP.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеТела);
ОтветHTTP = НТТР.ОтправитьДляОбработки(ЗапросHTTP);

Если тело запроса сохраняется во временный файл на диске, размер определяется через объект Файл:


РазмерВБайтах = Новый Файл(ИмяФайлаТела).Размер();
ЗаголовокHTTP.Вставить("Content-Length", XMLСтрока(РазмерВБайтах));

Важные нюансы при отправке документов в УТМ ЕГАИС

Помимо корректного размера пакета, при взаимодействии с УТМ ЕГАИС следует учитывать следующие требования платформы и транспортного модуля:

  1. Метка порядка байтов (BOM): При использовании метода УстановитьТелоИзСтроки() платформа может по умолчанию добавлять BOM-сигнатуру UTF-8 (байты EF BB BF). Встроенный в УТМ парсер XML не принимает BOM внутри multipart-содержимого и завершает разбор ошибкой. Всегда явно передавайте третий параметр: ИспользованиеByteOrderMark.НеИспользовать.
  2. Формат переноса строк в multipart/form-data: Спецификация MIME строго регламентирует использование комбинации CRLF (Символы.ВК + Символы.ПС) в качестве разделителя строк тела запроса. Использование одиночного Символы.ПС (характерно для серверов 1С под управлением Linux) приводит к ошибке 500 при разборе границ boundary. Заключительная граница должна иметь вид: "--" + Boundary + "--" + Символы.ВК + Символы.ПС.
  3. Имя поля формы: При отправке файлов на адреса вида /opt/in/... УТМ ожидает обязательный параметр с именем xml_file в заголовке части формы: Content-Disposition: form-data; name="xml_file"; filename="WayBill.xml".
  4. Оптимизация работы с памятью: При формировании крупных накладных конкатенация гигантских строк через оператор + приводит к избыточному потреблению оперативной памяти. Рекомендуется записывать данные на диск с помощью объектов ЗаписьXML и ЗаписьТекста, а затем передавать файл через метод ЗапросHTTP.УстановитьИмяФайлаТела(ИмяФайла). Это обеспечивает потоковую передачу данных без загрузки файла целиком в память.
  5. Таймаут соединения: Валидация объемного XML-документа и подписание его ключом аппаратного носителя могут занимать продолжительное время. В конструкторе HTTPСоединение рекомендуется устанавливать таймаут не менее 60–120 секунд, чтобы предотвратить обрыв соединения со стороны клиента.
← На главную