При интеграции 1С со сторонними веб-сервисами разработчики нередко сталкиваются с ситуацией, когда небольшие объемы данных отправляются успешно, а при передаче больших списков или массивов возникает ошибка:
Произошла исключительная ситуация (WinHttp.WinHttpRequest): Время ожидания операции истекло
Также возможен сценарий, при котором сервер принимает запрос, но данные оказываются неполными («обрубленными»). Разберем по шагам, почему это происходит, как оперативно исправить таймаут в COM-объекте WinHttp.WinHttpRequest.5.1 и почему для стабильной работы лучше перейти на встроенные механизмы 1С.
WinHttpRequest установлены небольшие таймауты на отправку и получение ответа. Если сервер долго формирует или обрабатывает массив данных, сетевое соединение разрывается по таймауту.Content-Length. Функция 1С СтрДлина() возвращает количество символов в строке, а не размер данных в байтах. Если в теле запроса передаются кириллические символы в кодировке UTF-8, каждый русский символ занимает 2 байта. Веб-сервер ориентируется на переданное значение Content-Length, считывает только указанное число байт и отсекает оставшуюся часть тела запроса либо ждет недостающие байты до наступления таймаута.+ внутри циклов приводит к многократному перевыделению оперативной памяти и существенному замедлению работы программы.Если проект жестко завязан на использование 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);
КонецЕсли;
Использование 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С.