Почему при отправке большого POST-запроса через WinHttp в 1С возникает ошибка таймаута или обрезаются данные?

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

При интеграции 1С со сторонними веб-сервисами разработчики нередко сталкиваются с ситуацией, когда небольшие объемы данных отправляются успешно, а при передаче больших списков или массивов возникает ошибка:

Произошла исключительная ситуация (WinHttp.WinHttpRequest): Время ожидания операции истекло

Также возможен сценарий, при котором сервер принимает запрос, но данные оказываются неполными («обрубленными»). Разберем по шагам, почему это происходит, как оперативно исправить таймаут в COM-объекте WinHttp.WinHttpRequest.5.1 и почему для стабильной работы лучше перейти на встроенные механизмы 1С.

Причины возникновения ошибок при передаче данных

  1. Стандартные ограничения времени ожидания (Timeouts). По умолчанию у COM-объекта WinHttpRequest установлены небольшие таймауты на отправку и получение ответа. Если сервер долго формирует или обрабатывает массив данных, сетевое соединение разрывается по таймауту.
  2. Некорректный расчет заголовка Content-Length. Функция 1С СтрДлина() возвращает количество символов в строке, а не размер данных в байтах. Если в теле запроса передаются кириллические символы в кодировке UTF-8, каждый русский символ занимает 2 байта. Веб-сервер ориентируется на переданное значение Content-Length, считывает только указанное число байт и отсекает оставшуюся часть тела запроса либо ждет недостающие байты до наступления таймаута.
  3. Неэффективная конкатенация строк. Складывание строк через оператор + внутри циклов приводит к многократному перевыделению оперативной памяти и существенному замедлению работы программы.

Решение 1. Настройка таймаутов в WinHttpRequest

Если проект жестко завязан на использование COM-объекта WinHttp.WinHttpRequest.5.1, проблему с превышением времени ожидания можно решить с помощью метода SetTimeouts(). Данный метод принимает 4 параметра в миллисекундах: время разрешения DNS-имени, время установки соединения, время отправки данных и время ожидания ответа.

Посмотрим на пример установки таймаутов в 10 минут (600 000 миллисекунд):


WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");

// Устанавливаем таймауты: ResolveTimeout, ConnectTimeout, SendTimeout, ReceiveTimeout
WinHttp.SetTimeouts(600000, 600000, 600000, 600000);

WinHttp.Open("POST", АдресСервера, 0);
WinHttp.SetRequestHeader("Content-Type", "multipart/form-data; boundary=" + Boundary);
WinHttp.SetCredentials(ЛогинУчётнойЗаписи, ПарольУчётнойЗаписи, 0);

// Отправляем тело запроса
WinHttp.Send(ТелоЗапроса);

Если WinHttp.Status = 200 Тогда
    Сообщить(WinHttp.ResponseText);
Иначе
    Сообщить("Ошибка HTTP: " + WinHttp.Status);
КонецЕсли;

Решение 2. Переход на штатные объекты HTTPСоединение и HTTPЗапрос

Использование COM-объектов имеет ряд существенных недостатков: привязка исключительно к ОС Windows (код не будет работать на сервере 1С под управлением Linux) и необходимость ручного контроля сетевых параметров. Платформа 1С:Предприятие 8.3 предоставляет встроенные кроссплатформенные объекты HTTPСоединение и HTTPЗапрос.

При передаче объемных данных рекомендуется формировать тело запроса в потоке или временном файле на диске через ЗаписьТекста в кодировке UTF-8. Это позволяет не перегружать оперативную память процессом сборки огромных строк и гарантирует правильный расчет длины содержимого платформой.

Разберем пример отправки данных с использованием штатных механизмов:


// 1. Формируем тело запроса во временный файл
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("txt");
Запись = Новый ЗаписьТекста(ИмяВременногоФайла, КодировкаТекста.UTF8, Символы.ВК + Символы.ПС, Ложь);

Boundary = "----" + СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "");
РазделительСтрок = Символы.ВК + Символы.ПС;

// Заголовок multipart-секции
Запись.ЗаписатьСтроку("--" + Boundary);
Запись.ЗаписатьСтроку("Content-Disposition: form-data; name=""basisDate""");
Запись.ЗаписатьСтроку("");
Запись.ЗаписатьСтроку(Формат(Период, "ДЛФ=Д"));

// Блок со счетами
Запись.ЗаписатьСтроку("--" + Boundary);
Запись.ЗаписатьСтроку("Content-Disposition: form-data; name=""счета""");
Запись.ЗаписатьСтроку("");
Для Каждого СтрСчета Из Массив_Счетов Цикл
    Запись.ЗаписатьСтроку(СтрСчета);
КонецЦикла;

// Завершающая граница
Запись.ЗаписатьСтроку("--" + Boundary + "--");
Запись.Закрыть();

// 2. Настраиваем соединение и выполняем отправку
ЗащищенноеСоединение = Неопределено; // Для HTTPS используйте: Новый ЗащищенноеСоединениеOpenSSL()
Таймаут = 600; // Таймаут в секундах

Соединение = Новый HTTPСоединение(
    "example.com", 
    443, 
    ЛогинУчётнойЗаписи, 
    ПарольУчётнойЗаписи, 
    , 
    Таймаут, 
    ЗащищенноеСоединение
);

Запрос = Новый HTTPЗапрос("/api/v1/upload");
Запрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
Запрос.УстановитьИмяФайлаТела(ИмяВременногоФайла);

Ответ = Соединение.ОтправитьДляОбработки(Запрос);

Если Ответ.КодСостояния = 200 Тогда
    Сообщить(Ответ.ПолучитьТелоКакСтроку());
Иначе
    Сообщить("Ошибка: " + Ответ.КодСостояния);
КонецЕсли;

// Удаляем временный файл
УдалитьФайлы(ИмяВременногоФайла);

Переход на HTTPСоединение исключает ошибки подсчета байтов заголовка Content-Length, предотвращает аварийное завершение работы из-за переполнения памяти при сборке больших строк и обеспечивает стабильную передачу данных независимо от операционной системы сервера 1С.

← На главную