Загрузка данных


// Коннектор: удобный HTTP-клиент для 1С:Предприятие 8
//
// Copyright 2017-2020 Vladimir Bondarevskiy
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//
//
// URL:    https://github.com/vbondarevsky/Connector
// e-mail: vbondarevsky@gmail.com
// Версия: 2.1.3
//
// Требования: платформа 1С версии 8.3.10 и выше

#Область ПрограммныйИнтерфейс

#Область МетодыHTTP

#Область МетодыОбщегоНазначения

// Отправляет GET запрос
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   ПараметрыЗапроса - Структура, Соответствие - параметры, которые будут отправлены в URL (часть после ?).
//                                                См. описание Сессия.ПараметрыЗапроса.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Get(URL, ПараметрыЗапроса = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт

	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, ПараметрыЗапроса, Неопределено, Неопределено);
	Возврат ВызватьHTTPМетод(ТекущаяСессия, "GET", URL, ДополнительныеПараметры);
	
КонецФункции

// Отправляет OPTIONS запрос
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Options(URL, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
	
	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Неопределено);
	Возврат ВызватьHTTPМетод(ТекущаяСессия, "OPTIONS", URL, ДополнительныеПараметры);
	
КонецФункции

// Отправляет HEAD запрос
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Head(URL, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
	
	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Неопределено);
	Возврат ВызватьHTTPМетод(ТекущаяСессия, "HEAD", URL, ДополнительныеПараметры);
	
КонецФункции

// Отправляет POST запрос
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   Данные - Структура, Соответствие, Строка, ДвоичныеДанные - см. описание ДополнительныеПараметры.Данные.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Post(URL, Данные = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
	
	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Данные, Неопределено);
	Возврат ВызватьHTTPМетод(ТекущаяСессия, "POST", URL, ДополнительныеПараметры);

КонецФункции

// Отправляет PUT запрос
//
// Параметры:
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   Данные - Структура, Соответствие, Строка, ДвоичныеДанные - см. описание ДополнительныеПараметры.Данные.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Put(URL, Данные = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт

	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Данные, Неопределено);
	Возврат ВызватьHTTPМетод(ТекущаяСессия, "PUT", URL, ДополнительныеПараметры);
	
КонецФункции

// Отправляет PATCH запрос
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   Данные - Структура, Соответствие, Строка, ДвоичныеДанные - см. описание ДополнительныеПараметры.Данные.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Patch(URL, Данные = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт

	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Данные, Неопределено);
	Возврат ВызватьHTTPМетод(ТекущаяСессия, "PATCH", URL, ДополнительныеПараметры);
	
КонецФункции

// Отправляет DELETE запрос
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   Данные - Структура, Соответствие, Строка, ДвоичныеДанные - см. описание ДополнительныеПараметры.Данные.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Delete(URL, Данные = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт

	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Данные, Неопределено);
	Возврат ВызватьHTTPМетод(ТекущаяСессия, "DELETE", URL, ДополнительныеПараметры);
	
КонецФункции

#КонецОбласти

#Область УпрощенныеМетодыДляРаботыСЗапросамиВФорматеJSON

// Отправляет GET запрос
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   ПараметрыЗапроса - Структура, Соответствие - параметры, которые будут отправлены в URL (часть после ?).
//                                                См. описание Сессия.ПараметрыЗапроса.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Соответствие, Структура - ответ, десериализованный из JSON. 
//                             Параметры преобразования см. ДополнительныеПараметры.ПараметрыПреобразованияJSON.
//
Функция GetJson(URL, ПараметрыЗапроса = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт

	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, ПараметрыЗапроса, Неопределено, Неопределено);
	ПараметрыПреобразованияJSON = 
		ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыПреобразованияJSON", Неопределено);
	Возврат КакJson(ВызватьHTTPМетод(ТекущаяСессия, "GET", URL, ДополнительныеПараметры), ПараметрыПреобразованияJSON);
	
КонецФункции

// Отправляет POST запрос
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   Json - Структура, Соответствие - данные, которые необходимо сериализовать в JSON.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Соответствие, Структура - ответ, десериализованный из JSON. 
//                             Параметры преобразования см. ДополнительныеПараметры.ПараметрыПреобразованияJSON 
//
Функция PostJson(URL, Json, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
	
	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Json);
	ПараметрыПреобразованияJSON = 
		ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыПреобразованияJSON", Неопределено);
	Возврат КакJson(ВызватьHTTPМетод(ТекущаяСессия, "POST", URL, ДополнительныеПараметры), ПараметрыПреобразованияJSON);

КонецФункции

// Отправляет PUT запрос
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   Json - Структура, Соответствие - данные, которые необходимо сериализовать в JSON.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Соответствие, Структура - ответ, десериализованный из JSON. 
//                             Параметры преобразования см. ДополнительныеПараметры.ПараметрыПреобразованияJSON 
//
Функция PutJson(URL, Json, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт

	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Json);
	ПараметрыПреобразованияJSON = 
		ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыПреобразованияJSON", Неопределено);
	Возврат КакJson(ВызватьHTTPМетод(ТекущаяСессия, "PUT", URL, ДополнительныеПараметры), ПараметрыПреобразованияJSON);
	
КонецФункции

// Отправляет DELETE запрос
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   Json - Структура, Соответствие - данные, которые необходимо сериализовать в JSON.
//   ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Соответствие, Структура - ответ, десериализованный из JSON. 
//                             Параметры преобразования см. ДополнительныеПараметры.ПараметрыПреобразованияJSON 
// 
Функция DeleteJson(URL, Json, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт

	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Json);
	ПараметрыПреобразованияJSON = 
		ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыПреобразованияJSON", Неопределено);
	Возврат КакJson(ВызватьHTTPМетод(ТекущаяСессия, "DELETE", URL, ДополнительныеПараметры), ПараметрыПреобразованияJSON);
	
КонецФункции

#КонецОбласти

// Отправляет данные на указанный адрес для обработки с использованием указанного HTTP-метода.
//
// Параметры:
//   Метод - Строка - имя HTTP-метода для запроса.
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//   ДополнительныеПараметры - Структура - позволяет задать дополнительные параметры:
//      *Заголовки - Соответствие - см. описание Сессия.Заголовки.
//      *Аутентификация - Структура - см. описание Сессия.Аутентификация
//      *Прокси - ИнтернетПрокси - см. описание Сессия.Прокси.
//      *ПараметрыЗапроса - Структура, Соответствие - см. описание Сессия.ПараметрыЗапроса.
//      *ПроверятьSSL - Булево - см. описание Сессия.ПроверятьSSL.
//      *КлиентскийСертификатSSL - см. описание Сессия.КлиентскийСертификатSSL.
//      *Cookies - Массив - см. описание Сессия.Cookies.
//      *Таймаут - Число - время ожидания осуществляемого соединения и операций, в секундах.
//                 Значение по умолчанию - 30 сек.
//      *РазрешитьПеренаправление - Булево - Истина - редиректы будут автоматически разрешены.
//                                           Ложь - будет выполнен только один запрос к серверу.
//      *Json - Структура, Соответствие - данные, которые необходимо сериализовать в JSON.
//      *ПараметрыПреобразованияJSON - Структура - задает параметры преобразования JSON:
//          **ПрочитатьВСоответствие - Булево - Если Истина, чтение объекта JSON будет выполнено в Соответствие.
//                                              Если Ложь, объекты будут считываться в объект типа Структура.
//          **ФорматДатыJSON - ФорматДатыJSON - формат, в котором представлена дата в строке,
//                             подлежащей преобразованию. 
//          **ИменаСвойствСоЗначениямиДата -  Массив, Строка - имена свойств JSON,
//                                            для которых нужно вызывать восстановление даты из строки.
//      *ПараметрыЗаписиJSON - Структура - параметры, используемые при записи объекта JSON.
//                                         См. в синтаксис-помощнике описание ПараметрыЗаписиJSON.
//      *Данные - Структура, Соответствие - поля формы, которые необходимо отправить в запрос:
//          **<Ключ> - Строка - имя поля.
//          **<Значение> - Строка - значение поля.
//              - Строка, ДвоичныеДанные - произвольные данные, которые необходимо отправить в запросе.
//      *Файлы - Структура, Массив - файлы, которые необходимо отправить в запросе:
//          **Имя - Строка - имя поля формы.
//          **Данные - ДвоичныеДанные - двоичные данные файла.
//          **ИмяФайла - Строка - имя файла.
//          **Тип - Строка - MIME-тип файла.
//          **Заголовки - Соответствие, Неопределено - HTTP заголовки запроса.
//      *МаксимальноеКоличествоПовторов - Число - количество повторных попыток соединения/отправки запроса.
//                                        Между попытками выполняется задержка, растущая по экспоненте.
//                                        Но если код состояния один из 413, 429, 503
//                                        и в ответе есть заголовок Retry-After,
//                                        то время задержки формируется из значения этого заголовка
//                                        Значение по умолчанию: 0 - повторы не выполняются.
//      *МаксимальноеВремяПовторов - Число - максимальное общее время (в секундах) отправки запроса с учетом повторов.
//                                   Значение по умолчанию: 600.
//      *КоэффициентЭкспоненциальнойЗадержки - Число - коэффициент изменения экспоненциальной задержки.
//                                             1 формирует последовательность задержек: 1, 2, 4, 8 и т.д.
//                                             2 формируется последовательность задержек: 2, 4, 8, 16 и т.д.
//                                             ...
//                                             Значение по умолчанию: 1.
//      *ПовторятьДляКодовСостояний - Неопределено - повторы будут выполняться для кодов состояний >= 500.
//                                  - Массив - повторы будут выполняться для конкретных кодов состояний.
//                                    Значение по умолчанию: Неопределено.
//   Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
//   Структура - ответ на выполненный запрос:
//      *ВремяВыполнения - Число - время выполнения запроса в миллисекундах.
//      *Cookies - Соответствие - cookies полученные с сервера.
//      *Заголовки - Соответствие - HTTP заголовки ответа.
//      *ЭтоПостоянныйРедирект - Булево - признак постоянного редиректа.
//      *ЭтоРедирект - Булево - признак редиректа.
//      *Кодировка - Строка - кодировка текста ответа.
//      *Тело - ДвоичныеДанные - тело ответа.
//      *КодСостояния - Число - код состояния ответа.
//      *URL - Строка - итоговый URL, по которому был выполнен запрос.
//
Функция ВызватьМетод(Метод, URL, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт 

	ТекущаяСессия = ?(Сессия = Неопределено, СоздатьСессию(), Сессия);
	ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Неопределено);
	Возврат ВызватьHTTPМетод(ТекущаяСессия, Метод, URL, ДополнительныеПараметры);
	
КонецФункции

// Создает объект для хранения параметров сессии.
//
// Возвращаемое значение:
//   Структура - параметры сессии:
//      *Заголовки - Соответствие - HTTP заголовки запроса.
//      *Аутентификация - Структура - параметры аутентификации запроса.
//          *ИспользоватьАутентификациюОС - Булево - включает использование аутентификации NTLM или Negotiate.
//                                                   Значение по умолчанию: Ложь.
//          *Тип - Строка - тип аутентификации. Для Basic Тип можно не указывать.
//       Если Тип = Digest или Basic: 
//          *Пользователь - Строка - имя пользователя.
//          *Пароль - Строка - пароль пользователя.
//       Если Тип = AWS4-HMAC-SHA256:
//          *ИдентификаторКлючаДоступа - Строка - идентификатор ключа доступа.
//          *СекретныйКлюч - Строка - секретный ключ.
//          *Сервис - Строка - сервис, к которому выполняется подключение.
//          *Регион - Строка - регион, к которому выполняется подключение.
//      *Прокси - ИнтернетПрокси - параметры прокси, которые будут использованы при отправке запроса.
//                Значение по умолчанию: Неопределено. При этом если в конфигурации используется БСП, 
//                                                     то значения прокси будет взято из БСП.
//      *ПараметрыЗапроса - Структура, Соответствие - параметры, которые будут отправлены в URL (часть после ?):
//          *<Ключ> - Строка - ключ параметра в URL.
//          *<Значение> - Строка - значение параметра URL
//                      - Массив - сформирует строку из нескольких параметров: key=value1&key=value2 и т.д.
//      *ПроверятьSSL - Булево - Ложь - проверка сертификата сервера не выполняется.
//                             - Истина - используется значение СертификатыУдостоверяющихЦентровОС. 
//                    - СертификатыУдостоверяющихЦентровФайл - см. в синтаксис-помощнике описание
//                                                             СертификатыУдостоверяющихЦентровФайл.
//                      Значение по умолчанию: Истина.
//      *КлиентскийСертификатSSL - СертификатКлиентаФайл - см. в синтаксис-помощнике описание СертификатКлиентаФайл.
//                               - СертификатКлиентаWindows - см. в синтаксис-помощнике описание
//                                                            СертификатКлиентаWindows.
//                                 Значение по умолчанию: Неопределено.
//      *МаксимальноеКоличествоПеренаправлений - Число - максимальное количество редиректов. Защита от зацикливания.
//                                               Значение по умолчанию:
//                                               см. функцию МаксимальноеКоличествоПеренаправлений 
//      *Cookies - Соответствие - хранилище cookies.
//
Функция СоздатьСессию() Экспорт
	
	Сессия = Новый Структура;
	Сессия.Вставить("Заголовки", ЗаголовкиПоУмолчанию());
	Сессия.Вставить("Аутентификация", Неопределено);
	Сессия.Вставить("Прокси", Неопределено);
	Сессия.Вставить("ПараметрыЗапроса", Новый Структура);
	Сессия.Вставить("ПроверятьSSL", Истина);
	Сессия.Вставить("КлиентскийСертификатSSL", Неопределено);
	Сессия.Вставить("МаксимальноеКоличествоПеренаправлений", МаксимальноеКоличествоПеренаправлений());
	Сессия.Вставить("Cookies", Новый Соответствие);
	Сессия.Вставить("СлужебныеДанные", Новый Структура("ПараметрыDigest"));
	
	Возврат Сессия;
	
КонецФункции

#КонецОбласти

#Область ФорматыОтветов

// Возвращает ответ сервера в виде десериализованного значения JSON.
//
// Параметры:
//   Ответ - Структура - ответ сервера на отправленный запрос. 
//                      См. описание возвращаемого значения ВызватьМетод.
//   ПараметрыПреобразованияJSON - Структура - задает параметры преобразования JSON.
//      *ПрочитатьВСоответствие - Булево - Если Истина, чтение объекта JSON будет выполнено в Соответствие.
//                                         Если Ложь, объекты будут считываться в объект типа Структура.
//      *ФорматДатыJSON - ФорматДатыJSON - формат, в котором представлена дата в строке, подлежащей преобразованию. 
//      *ИменаСвойствСоЗначениямиДата -  Массив, Строка - имена свойств JSON,
//                                       для которых нужно вызывать восстановление даты из строки.
// 
// Возвращаемое значение:
//   Соответствие - ответ сервера в виде десериализованного значения JSON.
//                  Если ПараметрыПреобразования.ПрочитатьВСоответствие = Истина (по умолчанию).
//   Структура - если ПараметрыПреобразования.ПрочитатьВСоответствие = Ложь. 
//
Функция КакJson(Ответ, ПараметрыПреобразованияJSON = Неопределено) Экспорт
	
	Возврат JsonВОбъект(РаспаковатьОтвет(Ответ), Ответ.Кодировка, ПараметрыПреобразованияJSON);
	
КонецФункции

// Возвращает ответ сервера в виде текста.
//
// Параметры:
//   Ответ - Структура - ответ сервера на отправленный запрос. 
//                       См. описание возвращаемого значения ВызватьМетод.
//   Кодировка - Строка, КодировкаТекста - определяет кодировку текста.
//                                         Если значение не задано, то кодировка извлекается из Ответ.Кодировка.
// 
// Возвращаемое значение:
//   Строка - ответ сервера в виде текста. 
//
Функция КакТекст(Ответ, Кодировка = Неопределено) Экспорт
	
	Если Не ЗначениеЗаполнено(Кодировка) Тогда
		Кодировка = Ответ.Кодировка;
	КонецЕсли;
	
	ЧтениеТекста = Новый ЧтениеТекста(РаспаковатьОтвет(Ответ).ОткрытьПотокДляЧтения(), Кодировка);
	Текст = ЧтениеТекста.Прочитать();
	ЧтениеТекста.Закрыть();
	
	Если Текст = Неопределено Тогда
		Текст = "";
	КонецЕсли;
	
	Возврат Текст;
	
КонецФункции

// Возвращает ответ сервера в двоичных данных.
//
// Параметры:
//   Ответ - Структура - ответ сервера на отправленный запрос. 
//                       См. описание возвращаемого значения ВызватьМетод.
//
// Возвращаемое значение:
//   Строка - ответ сервера в виде двоичных данных. 
//
Функция КакДвоичныеДанные(Ответ) Экспорт
	
	Возврат РаспаковатьОтвет(Ответ);
	
КонецФункции

#КонецОбласти

#Область ВспомогательныеФункции

// Возвращает структурированное представление URL.
//
// Параметры:
//   URL - Строка - URL ресурса, к которому будет отправлен запрос.
//
// Возвращаемое значение:
//   Структура - структура URL:
//      *Схема - Строка - схема обращения к серверу (http, https).
//      *Аутентификация - Структура - параметры аутентификации:
//          *Пользователь - Строка - имя пользователя.
//          *Пароль - Строка - пароль пользователя.
//      *Сервер - Строка - адрес сервера.
//      *Порт - Число - порт сервера.
//      *Путь - Строка - адрес ресурса на сервере.
//      *ПараметрыЗапроса - Соответствие - параметры запроса передаваемые на сервер в URL (часть после ?):
//          *<Ключ> - Строка - ключ параметра в URL.
//          *<Значение> - Строка - значение параметра URL;
//                      - Массив - значения параметра (key=value1&key=value2).
//      *Фрагмент - Строка - часть URL после #. 
//
Функция РазобратьURL(Знач URL) Экспорт

	Схема = "";
	Путь = "";
	Аутентификация = Новый Структура("Пользователь, Пароль", "", "");
	Сервер = "";
	Порт = "";
	Фрагмент = "";
	
	ДопустимыеСхемы = СтрРазделить_Костыль("http,https", ",", Истина);
	
	URLБезСхемы = URL;
	РазбитьСтрокуПоРазделителю(Схема, URLБезСхемы, "://");
	Если ДопустимыеСхемы.Найти(НРег(Схема)) <> Неопределено Тогда
		URL = URLБезСхемы;
	Иначе
		Схема = "";
	КонецЕсли;
	
	Результат = РазделитьПоПервомуНайденномуРазделителю(URL, СтрРазделить_Костыль("/,?,#", ",", Истина));
	URL = Результат[0];
	Если ЗначениеЗаполнено(Результат[2]) Тогда
		Путь = Результат[2] + Результат[1];
	КонецЕсли;
	
	АутентификацияСтрока = "";
	РазбитьСтрокуПоРазделителю(АутентификацияСтрока, URL, "@");
	Если ЗначениеЗаполнено(АутентификацияСтрока) Тогда
		АутентификацияЧасти = СтрРазделить_Костыль(АутентификацияСтрока, ":", Истина);
		Аутентификация.Пользователь = АутентификацияЧасти[0];
		Аутентификация.Пароль       = АутентификацияЧасти[1];
	КонецЕсли;

	// IPv6
	РазбитьСтрокуПоРазделителю(Сервер, URL, "]");
	Если ЗначениеЗаполнено(Сервер) Тогда
		Сервер = Сервер + "]";
	КонецЕсли;
	
	URL = СтрЗаменить(URL, "/", "");
	
	РазбитьСтрокуПоРазделителю(Порт, URL, ":", Истина);
	
	Если Не ЗначениеЗаполнено(Сервер) Тогда
		Сервер = URL;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Порт) Тогда 
		Порт = Число(Порт);
	Иначе
		Порт = 0;
	КонецЕсли;
	
	РазбитьСтрокуПоРазделителю(Фрагмент, Путь, "#", Истина);
	
	ПараметрыЗапроса = ЗаполнитьПараметрыЗапроса(Путь);
	
	Если Не ЗначениеЗаполнено(Схема) Тогда
		Схема = "http";
	КонецЕсли;
	Путь = ?(ЗначениеЗаполнено(Путь), Путь, "/");
	
	Результат = Новый Структура;
	Результат.Вставить("Схема", Схема);
	Результат.Вставить("Аутентификация", Аутентификация);
	Результат.Вставить("Сервер", Сервер);
	Результат.Вставить("Порт", Порт);
	Результат.Вставить("Путь", Путь);
	Результат.Вставить("ПараметрыЗапроса", ПараметрыЗапроса);
	Результат.Вставить("Фрагмент", Фрагмент);

	Возврат Результат;
	
КонецФункции

// Преобразование Объекта в JSON.
//
// Параметры:
//   Объект - Произвольный - данные, которые необходимо преобразовать в JSON.
//   ПараметрыПреобразования - Структура - кодировка текста JSON. Значение по умолчанию - utf-8.
//      *ФорматДатыJSON - ФорматДатыJSON - определяет формат сериализации дат JSON-объектов.
//   ПараметрыЗаписи - Структура - параметры преобразования JSON:
//      *ПереносСтрок - ПереносСтрокJSON - определяет способ переноса строк,
//                      который будет использован при записи данных JSON.
//      *СимволыОтступа - Строка - определяет символы отступа, используемые при записи данных JSON.
//      *ИспользоватьДвойныеКавычки - Булево - определяет, будут ли при записи имена свойств JSON
//                                    записываться в двойных кавычках.
//      *ЭкранированиеСимволов - ЭкранированиеСимволовJSON - определяет используемый способ экранирования (замены)
//                               символов при записи данных JSON.
//      *ЭкранироватьУгловыеСкобки - Булево - определяет, будут ли при записи экранироваться символы "<" и ">".
//      *ЭкранироватьРазделителиСтрок - Булево - определяет, будут ли экранироваться разделители строк
//                                      U+2028 (line-separator) и U+2029 (page-separator).
//      *ЭкранироватьАмперсанд - Булево - определяет, будет ли при записи экранироваться символ амперсанда "&".
//      *ЭкранироватьОдинарныеКавычки - Булево - определяет, будут ли экранироваться одинарные кавычки.
//      *ЭкранироватьСлеш - Булево - определяет, будет ли экранироваться слеш (косая черта) при записи значения.
// 
// Возвращаемое значение:
//   Строка - объект в формате JSON. 
//
Функция ОбъектВJson(Объект, Знач ПараметрыПреобразования = Неопределено, Знач ПараметрыЗаписи = Неопределено) Экспорт
	
	ПараметрыПреобразованияJSON = ДополнитьПараметрыПреобразованияJSON(ПараметрыПреобразования);
	
	НастройкиСериализации = Новый НастройкиСериализацииJSON;
	НастройкиСериализации.ФорматСериализацииДаты = ПараметрыПреобразованияJSON.ФорматДатыJSON;
	
	ПараметрыЗаписи = ДополнитьПараметрыЗаписиJSON(ПараметрыЗаписи);
	
	ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(
		ПереносСтрокJSON.Авто,
		ПараметрыЗаписи.СимволыОтступа,
		ПараметрыЗаписи.ИспользоватьДвойныеКавычки,
		ПараметрыЗаписи.ЭкранированиеСимволов,
		ПараметрыЗаписи.ЭкранироватьУгловыеСкобки,
		ПараметрыЗаписи.ЭкранироватьРазделителиСтрок,
		ПараметрыЗаписи.ЭкранироватьАмперсанд,
		ПараметрыЗаписи.ЭкранироватьОдинарныеКавычки,
		ПараметрыЗаписи.ЭкранироватьСлеш);
	
	ЗаписьJSON = Новый ЗаписьJSON;
	ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписиJSON);
	ЗаписатьJSON(ЗаписьJSON, Объект, НастройкиСериализации);
	
	Возврат ЗаписьJSON.Закрыть();
	
КонецФункции

// Преобразование JSON в Объект.
//
// Параметры:
//   Json - Поток, ДвоичныеДанные - данные в формате JSON.
//   Кодировка - Строка - кодировка текста JSON. Значение по умолчанию - utf-8.
//   ПараметрыПреобразования - Структура - параметры преобразования JSON:
//      *ПрочитатьВСоответствие - Булево - если Истина, чтение объекта JSON будет выполнено в Соответствие,
//                                         иначе в Структура.
//      *ИменаСвойствСоЗначениямиДата - Массив, Строка, ФиксированныйМассив - имена свойств JSON,
//                                      для которых нужно вызывать восстановление даты из строки.
//      *ФорматДатыJSON - ФорматДатыJSON - определяет формат десериализации дат JSON-объектов.
// 
// Возвращаемое значение:
//   Произвольный - значение, десериализованное из JSON. 
//
Функция JsonВОбъект(Json, Кодировка = "utf-8", ПараметрыПреобразования = Неопределено) Экспорт
	
	Если Кодировка = "" Тогда
		Кодировка = "utf-8";
	КонецЕсли;
	
	ПараметрыПреобразованияJSON = ДополнитьПараметрыПреобразованияJSON(ПараметрыПреобразования);
	
	ЧтениеJSON = Новый ЧтениеJSON;
	Если ТипЗнч(Json) = Тип("ДвоичныеДанные") Тогда
		ЧтениеJSON.ОткрытьПоток(Json.ОткрытьПотокДляЧтения(), Кодировка);
	Иначе
		ЧтениеJSON.ОткрытьПоток(Json, Кодировка);
	КонецЕсли;
	
	Объект = Новый Соответствие;
	Попытка
		Объект = ПрочитатьJSON(
		ЧтениеJSON, 
		ПараметрыПреобразованияJSON.ПрочитатьВСоответствие,
		ПараметрыПреобразованияJSON.ИменаСвойствСоЗначениямиДата,
		ПараметрыПреобразованияJSON.ФорматДатыJSON);
		ЧтениеJSON.Закрыть();
	Исключение
		Возврат ОписаниеОшибки();
	КонецПопытки;
	
	Возврат Объект;

КонецФункции

// Вычисляет HMAC (hash-based message authentication code).
//
// Параметры:
//   Ключ - ДвоичныеДанные - секретный ключ.
//   Данные - ДвоичныеДанные - данные, для которых нужно посчитать HMAC.
//   Алгоритм - ХешФункция - алгоритм, используемый для вычисления хеша.
//
// Возвращаемое значение:
//   ДвоичныеДанные - вычисленное значение HMAC. 
//
Функция HMAC(Ключ, Данные, Алгоритм) Экспорт
	
	ДлинаБлока = 64;
	
	Если Ключ.Размер() > ДлинаБлока Тогда
		Хеширование = Новый ХешированиеДанных(Алгоритм);
		Хеширование.Добавить(Ключ);
		
		КлючБуфер = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Хеширование.ХешСумма);
	Иначе
		КлючБуфер = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Ключ);
	КонецЕсли;
	
	ИзмененныйКлюч = Новый БуферДвоичныхДанных(ДлинаБлока);
	ИзмененныйКлюч.Записать(0, КлючБуфер);
	
	ВнутреннийКлюч = ИзмененныйКлюч.Скопировать();
	ВнешнийКлюч = ИзмененныйКлюч;
	
	ВнутреннееВыравнивание = Новый БуферДвоичныхДанных(ДлинаБлока);
	ВнешнееВыравнивание = Новый БуферДвоичныхДанных(ДлинаБлока);
	Для Индекс = 0 По ДлинаБлока - 1 Цикл
		ВнутреннееВыравнивание.Установить(Индекс, 54);
		ВнешнееВыравнивание.Установить(Индекс, 92);
	КонецЦикла;
	
	ВнутреннееХеширование = Новый ХешированиеДанных(Алгоритм);
	ВнешнееХеширование = Новый ХешированиеДанных(Алгоритм);
	
	ВнутреннийКлюч.ЗаписатьПобитовоеИсключительноеИли(0, ВнутреннееВыравнивание);
	ВнешнийКлюч.ЗаписатьПобитовоеИсключительноеИли(0, ВнешнееВыравнивание);
	
	ВнешнееХеширование.Добавить(ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ВнешнийКлюч));
	ВнутреннееХеширование.Добавить(ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ВнутреннийКлюч));
	
	Если ЗначениеЗаполнено(Данные) Тогда
		ВнутреннееХеширование.Добавить(Данные);
	КонецЕсли;
	
	ВнешнееХеширование.Добавить(ВнутреннееХеширование.ХешСумма);
	
	Возврат ВнешнееХеширование.ХешСумма;

КонецФункции

// Возвращает структуру именованных кодов состояний HTTP.
//
// Возвращаемое значение:
//   Структура - именованные коды состояний HTTP.
//
Функция КодыСостоянияHTTP() Экспорт

	КодыСостояния = Новый Структура;
	КодыСостояния.Вставить("ПеремещеноНавсегда_301", 301);
	КодыСостояния.Вставить("ПеремещеноВременно_302", 302);
	КодыСостояния.Вставить("СмотретьДругое_303", 303);
	КодыСостояния.Вставить("ВременноеПеренаправление_307", 307);
	КодыСостояния.Вставить("ПостоянноеПеренаправление_308", 308);
	КодыСостояния.Вставить("НеверныйЗапрос_400", 400);
	КодыСостояния.Вставить("ПолезнаяНагрузкаСлишкомВелика_413", 413);
	КодыСостояния.Вставить("СлишкомМногоЗапросов_429", 429);
	КодыСостояния.Вставить("ВнутренняяОшибкаСервера_500", 500);
	КодыСостояния.Вставить("ОшибочныйШлюз_502", 502);
	КодыСостояния.Вставить("СервисНедоступен_503", 503);
	КодыСостояния.Вставить("ШлюзНеОтвечает_504", 504);
	
	Возврат КодыСостояния;

КонецФункции

// Выполняет чтение данных из архива GZip.
//
// Параметры:
//   СжатыеДанные - ДвоичныеДанные - данные упакованные GZip.
//
// Возвращаемое значение:
//   ДвоичныеДанные - распакованные данные. 
//
Функция ПрочитатьGZip(СжатыеДанные) Экспорт
	
	РазмерПрефиксаGZip = 10;
	РазмерПостфиксаGZip = 8;
	
	ЧтениеДанных = Новый ЧтениеДанных(СжатыеДанные);
	ЧтениеДанных.Пропустить(РазмерПрефиксаGZip);
	РазмерСжатыхДанных = ЧтениеДанных.ИсходныйПоток().Размер() - РазмерПрефиксаGZip - РазмерПостфиксаGZip; 
	
	ПотокZip = Новый ПотокВПамяти(ZipРазмерLFH() + РазмерСжатыхДанных + ZipРазмерDD() + ZipРазмерCDH() + ZipРазмерEOCD());
	ЗаписьДанных = Новый ЗаписьДанных(ПотокZip);
	ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(ZipLFH());
	ЧтениеДанных.КопироватьВ(ЗаписьДанных, РазмерСжатыхДанных);
	
	ЗаписьДанных.Закрыть();
	ЗаписьДанных = Новый ЗаписьДанных(ПотокZip);
	
	CRC32 = ЧтениеДанных.ПрочитатьЦелое32();
	РазмерНесжатыхДанных = ЧтениеДанных.ПрочитатьЦелое32();
	ЧтениеДанных.Закрыть();
	
	ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(ZipDD(CRC32, РазмерСжатыхДанных, РазмерНесжатыхДанных));
	ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(ZipCDH(CRC32, РазмерСжатыхДанных, РазмерНесжатыхДанных));
	ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(ZipEOCD(РазмерСжатыхДанных));
	ЗаписьДанных.Закрыть();
		
	Возврат ПрочитатьZip(ПотокZip);
	
КонецФункции

// Выполняет запись данных в архив GZip.
//
// Параметры:
//   Данные - ДвоичныеДанные - исходные данные.
//
// Возвращаемое значение:
//   ДвоичныеДанные - данные упакованные GZip.
//
Функция ЗаписатьGZip(Данные) Экспорт

	ЧтениеДанных = Новый ЧтениеДанных(ЗаписатьZip(Данные));

	НачальноеСмещение = 14;
	ЧтениеДанных.Пропустить(НачальноеСмещение);
	CRC32 = ЧтениеДанных.ПрочитатьЦелое32();

	РазмерСжатыхДанных = ЧтениеДанных.ПрочитатьЦелое32();
	РазмерИсходныхДанных = ЧтениеДанных.ПрочитатьЦелое32();
	
	РазмерИмениФайла = ЧтениеДанных.ПрочитатьЦелое16();
	РазмерДополнительногоПоля = ЧтениеДанных.ПрочитатьЦелое16();
	ЧтениеДанных.Пропустить(РазмерИмениФайла + РазмерДополнительногоПоля);

	ПотокGZip = Новый ПотокВПамяти;
	ЗаписьДанных = Новый ЗаписьДанных(ПотокGZip);
	ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(GZipHeader());
	ЧтениеДанных.КопироватьВ(ЗаписьДанных, РазмерСжатыхДанных);
	ЗаписьДанных.Закрыть();
	ЗаписьДанных = Новый ЗаписьДанных(ПотокGZip);

	ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(GZipFooter(CRC32, РазмерИсходныхДанных));

	Возврат ПотокGZip.ЗакрытьИПолучитьДвоичныеДанные();

КонецФункции

#КонецОбласти

#КонецОбласти

#Область СлужебныйПрограммныйИнтерфейс

Функция ПодготовитьЗапрос(Сессия, Метод, URL, ДополнительныеПараметры) Экспорт
	
	Cookies = ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Cookies", Новый Массив);
	Cookies = ОбъединитьCookies(ДозаполнитьCookie(Сессия.Cookies, URL), ДозаполнитьCookie(Cookies, URL));
	
	АутентификацияИзДополнительныхПараметров =
		ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Аутентификация", Новый Структура);
	ПараметрыЗапросаИзДополнительныхПараметров =
		ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыЗапроса", Новый Структура);
	ЗаголовкиИзДополнительныхПараметров =
		ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Заголовки", Новый Соответствие);

	Аутентификация = ОбъединитьПараметрыАутентификации(АутентификацияИзДополнительныхПараметров, Сессия.Аутентификация);
	ПараметрыЗапроса = ОбъединитьПараметрыЗапроса(ПараметрыЗапросаИзДополнительныхПараметров, Сессия.ПараметрыЗапроса);
	Заголовки = ОбъединитьЗаголовки(ЗаголовкиИзДополнительныхПараметров, Сессия.Заголовки);
	ПараметрыПреобразованияJSON = 
		ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыПреобразованияJSON", Неопределено);
		
	ПодготовленныйЗапрос = Новый Структура;
	ПодготовленныйЗапрос.Вставить("Cookies", Cookies);
	ПодготовленныйЗапрос.Вставить("Аутентификация", Аутентификация);
	ПодготовленныйЗапрос.Вставить("Метод", Метод);
	ПодготовленныйЗапрос.Вставить("Заголовки", Заголовки);
	ПодготовленныйЗапрос.Вставить("ПараметрыЗапроса", ПараметрыЗапроса);
	ПодготовленныйЗапрос.Вставить("URL", ПодготовитьURL(URL, ПараметрыЗапроса));
	ПодготовленныйЗапрос.Вставить("ПараметрыПреобразованияJSON", ПараметрыПреобразованияJSON);
	
	ПодготовитьCookies(ПодготовленныйЗапрос);

	Данные = ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Данные", Новый Структура);
	Файлы = ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Файлы", Новый Массив);
	Json = ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Json", Неопределено);
	ПараметрыЗаписиJSON = ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыЗаписиJSON", Неопределено);

	ПодготовитьТелоЗапроса(ПодготовленныйЗапрос, Данные, Файлы, Json, ПараметрыЗаписиJSON);
	ПодготовитьАутентификацию(ПодготовленныйЗапрос);

	Возврат ПодготовленныйЗапрос;
	
КонецФункции

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Функция ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(КодСостояния)

	Коды = КодыСостоянияHTTP();
	Возврат КодСостояния = Коды.ПолезнаяНагрузкаСлишкомВелика_413
		ИЛИ КодСостояния = Коды.СлишкомМногоЗапросов_429
		ИЛИ КодСостояния = Коды.СервисНедоступен_503;

КонецФункции

Функция ЧислоИзСтроки(Знач Строка) Экспорт

	ОписаниеТипа = Новый ОписаниеТипов("Число");
	Возврат ОписаниеТипа.ПривестиЗначение(Строка);

КонецФункции

Функция ДатаИзСтроки(Знач Строка) Экспорт

	КвалификаторДаты = Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя);
	ОписаниеТипа = Новый ОписаниеТипов("Дата", Неопределено, Неопределено, КвалификаторДаты);
	Возврат ОписаниеТипа.ПривестиЗначение(Строка);

КонецФункции

Функция ДатаИзСтрокиRFC7231(Знач Строка) Экспорт
	
	Разделители = ",-:/\.";
	Для Индекс = 1 По СтрДлина(Разделители) Цикл
		Разделитель = Сред(Разделители, Индекс, 1);
		Строка = СтрЗаменить(Строка, Разделитель, " ");
	КонецЦикла;
	Строка = СтрЗаменить(Строка, "  ", " ");
	СоставляющиеДаты = СтрРазделить_Костыль(Строка, " ", Истина);
	МесяцСтр = СоставляющиеДаты[2];

	Месяцы = СтрРазделить_Костыль("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", ",", Истина);
	Месяц = Месяцы.Найти(МесяцСтр);
	Если Месяц = Неопределено Тогда
		Возврат '00010101';
	КонецЕсли;
	
	Дата = СоставляющиеДаты[3] + Формат(Месяц + 1, "ЧЦ=2; ЧВН=;") + СоставляющиеДаты[1];
	Время = СоставляющиеДаты[4] + СоставляющиеДаты[5] + СоставляющиеДаты[6];

	Возврат ДатаИзСтроки(Дата + Время);
 
КонецФункции

Функция ВызватьHTTPМетод(Сессия, Метод, URL, ДополнительныеПараметры)
	
	КодыСостоянияHTTP = КодыСостоянияHTTP();
	
	ПодготовленныйЗапрос = ПодготовитьЗапрос(Сессия, Метод, URL, ДополнительныеПараметры);
	
	НастройкиПодключения = ПолучитьНастройкиПодключения(Метод, URL, ДополнительныеПараметры);
	
	Ответ = ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, НастройкиПодключения);
	
	Перенаправление = 0;
	Пока Перенаправление < Сессия.МаксимальноеКоличествоПеренаправлений Цикл
		Если Не НастройкиПодключения.РазрешитьПеренаправление ИЛИ Не Ответ.ЭтоРедирект Тогда
			Возврат Ответ;
		КонецЕсли;
		
		НовыйURL = СформироватьНовыйURLПриПеренаправлении(Ответ);

		ПодготовленныйЗапрос.URL = КодироватьСтроку(НовыйURL, СпособКодированияСтроки.URLВКодировкеURL);
		НовыйHTTPЗапрос = Новый HTTPЗапрос(СобратьАдресРесурса(РазобратьURL(НовыйURL), Неопределено));
		ПереопределитьМетод(ПодготовленныйЗапрос, Ответ);
			
		Если Ответ.КодСостояния <> КодыСостоянияHTTP.ВременноеПеренаправление_307 
			И Ответ.КодСостояния <> КодыСостоянияHTTP.ПостоянноеПеренаправление_308 Тогда
			УдалитьЗаголовки(ПодготовленныйЗапрос.Заголовки, "content-length,content-type,transfer-encoding");
			НовыйHTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки;
		Иначе
			ИсходныйПоток = ПодготовленныйЗапрос.HTTPЗапрос.ПолучитьТелоКакПоток();
			ИсходныйПоток.КопироватьВ(НовыйHTTPЗапрос.ПолучитьТелоКакПоток());
		КонецЕсли;
		ПодготовленныйЗапрос.HTTPЗапрос = НовыйHTTPЗапрос;
		УдалитьЗаголовки(ПодготовленныйЗапрос.Заголовки, "cookies");

		ПодготовленныйЗапрос.Cookies = ОбъединитьCookies(Сессия.Cookies, ПодготовленныйЗапрос.Cookies);
		ПодготовитьCookies(ПодготовленныйЗапрос);
		
		// INFO: по хорошему аутентификацию нужно привести к новых параметрам, но пока будем игнорировать.
		
		Ответ = ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, НастройкиПодключения);
		
		Перенаправление = Перенаправление + 1;
	КонецЦикла;
	
	ВызватьИсключение("СлишкомМногоПеренаправлений");
	
КонецФункции

Функция СформироватьНовыйURLПриПеренаправлении(Ответ)

	НовыйURL = ПолучитьЗначениеЗаголовка("location", Ответ.Заголовки);
	НовыйURL = РаскодироватьСтроку(НовыйURL, СпособКодированияСтроки.URLВКодировкеURL);
	
	// Редирект без схемы
	Если СтрНачинаетсяС_Костыль(НовыйURL, "//") Тогда
		СтруктураURL = РазобратьURL(Ответ.URL);
		НовыйURL = СтруктураURL.Схема + ":" + НовыйURL;
	КонецЕсли;

	СтруктураURL = РазобратьURL(НовыйURL);
	Если Не ЗначениеЗаполнено(СтруктураURL.Сервер) Тогда
		СтруктураURLОтвета = РазобратьURL(Ответ.URL);
		БазовыйURL = СтрШаблон_Костыль("%1://%2", СтруктураURLОтвета.Схема, СтруктураURLОтвета.Сервер);
		Если ЗначениеЗаполнено(СтруктураURLОтвета.Порт) Тогда
			БазовыйURL = БазовыйURL + Формат(СтруктураURLОтвета.Порт, "ЧРГ=; ЧГ=");
		КонецЕсли;
		НовыйURL = БазовыйURL + НовыйURL;
	КонецЕсли;

	Возврат НовыйURL;

КонецФункции

Процедура УдалитьЗаголовки(Заголовки, СписокЗаголовковСтрокой)

	ЗаголовкиДляУдаления = Новый Массив;
	СписокЗаголовков = СтрРазделить_Костыль(СписокЗаголовковСтрокой, ",", Ложь);
	Для Каждого Заголовок Из Заголовки Цикл
		Если СписокЗаголовков.Найти(НРег(Заголовок.Ключ)) <> Неопределено Тогда
			ЗаголовкиДляУдаления.Добавить(Заголовок.Ключ);
		КонецЕсли;
	КонецЦикла;
	Для Каждого ЗаголовокДляУдаления Из ЗаголовкиДляУдаления Цикл
		Заголовки.Удалить(ЗаголовокДляУдаления);
	КонецЦикла;

КонецПроцедуры

Функция ПолучитьНастройкиПодключения(Метод, URL, ДополнительныеПараметры) 

	РазрешитьПеренаправление = 
		ПолучитьЗначениеПоКлючу(ДополнительныеПараметры, "РазрешитьПеренаправление", ВРег(Метод) <> "HEAD");
	ПроверятьSSL = ПолучитьЗначениеПоКлючу(ДополнительныеПараметры, "ПроверятьSSL", Истина);
	КлиентскийСертификатSSL = ПолучитьЗначениеПоКлючу(ДополнительныеПараметры, "КлиентскийСертификатSSL");
	Прокси = ПолучитьЗначениеПоКлючу(ДополнительныеПараметры, "Прокси", ПолучитьПроксиПоУмолчанию(URL));
	МаксимальноеКоличествоПовторов = ПолучитьЗначениеПоКлючу(ДополнительныеПараметры, "МаксимальноеКоличествоПовторов", 0);
	ПовторятьДляКодовСостояний = 
		ПолучитьЗначениеПоКлючу(ДополнительныеПараметры, "ПовторятьДляКодовСостояний", Неопределено);
	КоэффициентЭкспоненциальнойЗадержки = 
		ПолучитьЗначениеПоКлючу(ДополнительныеПараметры, "КоэффициентЭкспоненциальнойЗадержки", 1);
	МаксимальноеВремяПовторов = ПолучитьЗначениеПоКлючу(ДополнительныеПараметры, "МаксимальноеВремяПовторов", 600);

	Настройки = Новый Структура;
	Настройки.Вставить("Таймаут", ПолучитьТаймаут(ДополнительныеПараметры));
	Настройки.Вставить("РазрешитьПеренаправление", РазрешитьПеренаправление);
	Настройки.Вставить("ПроверятьSSL", ПроверятьSSL);
	Настройки.Вставить("КлиентскийСертификатSSL", КлиентскийСертификатSSL);
	Настройки.Вставить("Прокси", Прокси);
	Настройки.Вставить("МаксимальноеКоличествоПовторов", МаксимальноеКоличествоПовторов);
	Настройки.Вставить("ПовторятьДляКодовСостояний", ПовторятьДляКодовСостояний);
	Настройки.Вставить("КоэффициентЭкспоненциальнойЗадержки", КоэффициентЭкспоненциальнойЗадержки);
	Настройки.Вставить("МаксимальноеВремяПовторов", МаксимальноеВремяПовторов);

	Возврат Настройки;

КонецФункции

Функция ПолучитьТаймаут(ДополнительныеПараметры)
	
	Если ДополнительныеПараметры.Свойство("Таймаут") И ЗначениеЗаполнено(ДополнительныеПараметры.Таймаут) Тогда
		Таймаут = ДополнительныеПараметры.Таймаут;
	Иначе
		Таймаут = СтандартныйТаймаут();
	КонецЕсли;
	
	Возврат Таймаут;
	
КонецФункции

Функция ПолучитьПроксиПоУмолчанию(URL)
	//Начало изменений Рогачев Алексей модуль есть но не такой по этому не используем
	ПроксиПоУмолчанию = Новый ИнтернетПрокси;
	Если Метаданные.ОбщиеМодули.Найти("ПолучениеФайловИзИнтернетаКлиентСервер") = Неопределено Тогда
		ПолучениеФайловИзИнтернетаКлиентСервер = Неопределено;
	//Иначе
	//	СтруктураURL = РазобратьURL(URL);
	//	ПроксиПоУмолчанию = ПолучениеФайловИзИнтернетаКлиентСервер.ПолучитьПрокси(СтруктураURL.Схема);
	КонецЕсли;
	
	//Конец изменений Рогачев Алексей
	
	Возврат ПроксиПоУмолчанию;
	
КонецФункции

Функция ДозаполнитьCookie(Cookies, URL)
	
	СтруктураURL = РазобратьURL(URL);
	НовыеCookies = Новый Массив;
	Если ТипЗнч(Cookies) = Тип("Массив") Тогда
		Для Каждого Cookie Из Cookies Цикл
			НовыйCookie = КонструкторCookie(Cookie.Наименование, Cookie.Значение);
			ЗаполнитьЗначенияСвойств(НовыйCookie, Cookie);
			
			Если Не ЗначениеЗаполнено(НовыйCookie.Домен) Тогда
				НовыйCookie.Домен = СтруктураURL.Сервер;
			КонецЕсли;
			Если Не ЗначениеЗаполнено(НовыйCookie.Путь) Тогда
				НовыйCookie.Путь = "/";
			КонецЕсли;
			
			НовыеCookies.Добавить(НовыйCookie);
		КонецЦикла;
		
		Возврат НовыеCookies;
	КонецЕсли;
	
	Возврат Cookies;
	
КонецФункции

Процедура УдалитьCookieИзХранилища(ХранилищеCookies, Cookie)

	Если ХранилищеCookies.Получить(Cookie.Домен) <> Неопределено
		И ХранилищеCookies[Cookie.Домен].Получить(Cookie.Путь) <> Неопределено
		И ХранилищеCookies[Cookie.Домен][Cookie.Путь].Получить(Cookie.Наименование) <> Неопределено Тогда
		ХранилищеCookies[Cookie.Домен][Cookie.Путь].Удалить(Cookie.Наименование);
	КонецЕсли;

КонецПроцедуры

Процедура ДобавитьCookieВХранилище(ХранилищеCookies, Cookie, Замещать = Ложь)
	
	Если ХранилищеCookies.Получить(Cookie.Домен) = Неопределено Тогда
		ХранилищеCookies[Cookie.Домен] = Новый Соответствие;
	КонецЕсли;
	Если ХранилищеCookies[Cookie.Домен].Получить(Cookie.Путь) = Неопределено Тогда
		ХранилищеCookies[Cookie.Домен][Cookie.Путь] = Новый Соответствие;
	КонецЕсли;
	Если ХранилищеCookies[Cookie.Домен][Cookie.Путь].Получить(Cookie.Наименование) = Неопределено ИЛИ Замещать Тогда
		ХранилищеCookies[Cookie.Домен][Cookie.Путь][Cookie.Наименование] = Cookie;
	КонецЕсли;
	
КонецПроцедуры

Функция ДобавитьЛидирующуюТочку(Знач Домен)
	
	Если Не СтрНачинаетсяС_Костыль(Домен, ".") Тогда
		Домен = "." + Домен;
	КонецЕсли;
	
	Возврат Домен;
	
КонецФункции

Процедура ЗаполнитьСписокОтфильтрованнымиCookies(Cookies, СтруктураURL, Список)

	Для Каждого Cookie Из Cookies Цикл
		Если Cookie.Значение.ТолькоБезопасноеСоединение = Истина И СтруктураURL.Схема <> "https" Тогда
			Продолжить;
		КонецЕсли;
		// INFO: проверка срока действия игнорируется (Cookie.Значение.СрокДействия)
		// INFO: проверка порта игнорируется
		
		Список.Добавить(Cookie.Значение);
	КонецЦикла;

КонецПроцедуры

Функция ОтобратьCookiesДляЗапроса(СтруктураURL, Cookies)
	
	СерверВЗапросе = ДобавитьЛидирующуюТочку(СтруктураURL.Сервер);
	
	Результат = Новый Массив;
	Для Каждого Домен Из Cookies Цикл
		Если Не СтрЗаканчиваетсяНа_Костыль(СерверВЗапросе, Домен.Ключ) Тогда
			Продолжить;
		КонецЕсли;
		Для Каждого Путь Из Домен.Значение Цикл
			Если Не СтрНачинаетсяС_Костыль(СтруктураURL.Путь, Путь.Ключ) Тогда
				Продолжить;
			КонецЕсли;
			ЗаполнитьСписокОтфильтрованнымиCookies(Путь.Значение, СтруктураURL, Результат);
		КонецЦикла;
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Функция ПодготовитьЗаголовокCookie(ПодготовленныйЗапрос)
	
	СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
	
	Cookies = Новый Массив;
	Для Каждого Cookie Из ОтобратьCookiesДляЗапроса(СтруктураURL, ПодготовленныйЗапрос.Cookies) Цикл
		Cookies.Добавить(СтрШаблон_Костыль("%1=%2", Cookie.Наименование, Cookie.Значение));
	КонецЦикла;
	
	Возврат СтрСоединить_Костыль(Cookies, "; ");
	
КонецФункции

Процедура ПодготовитьCookies(ПодготовленныйЗапрос)
	
	ЗаголовокCookie = ПодготовитьЗаголовокCookie(ПодготовленныйЗапрос);
	Если ЗначениеЗаполнено(ЗаголовокCookie) Тогда
		ПодготовленныйЗапрос.Заголовки["Cookie"] = ЗаголовокCookie;
	КонецЕсли;
	
КонецПроцедуры

Функция КодироватьПараметрыЗапроса(ПараметрыЗапроса)
	
	ЧастиПараметрыЗапроса = Новый Массив;
	Для Каждого Параметр Из ПараметрыЗапроса Цикл
		Если ТипЗнч(Параметр.Значение) = Тип("Массив") Тогда
			Значения = Параметр.Значение;
		Иначе
			Значения = Новый Массив;
			Значения.Добавить(Параметр.Значение);
		КонецЕсли;
		
		Если Параметр.Значение = Неопределено Тогда
			ЧастиПараметрыЗапроса.Добавить(Параметр.Ключ);
		Иначе
			Для Каждого Значение Из Значения Цикл
				ЗначениеПараметра = КодироватьСтроку(Значение, СпособКодированияСтроки.КодировкаURL);
				ЧастиПараметрыЗапроса.Добавить(СтрШаблон_Костыль("%1=%2", Параметр.Ключ, ЗначениеПараметра));
			КонецЦикла;
		КонецЕсли;
	КонецЦикла;
	
	Возврат СтрСоединить_Костыль(ЧастиПараметрыЗапроса, "&");
	
КонецФункции

Функция ПодготовитьURL(Знач URL, ПараметрыЗапроса = Неопределено)
	
	URL = СокрЛ(URL);
	
	СтруктураURL = РазобратьURL(URL);
	
	ПодготовленныйURL = СтруктураURL.Схема + "://";
	Если ЗначениеЗаполнено(СтруктураURL.Аутентификация.Пользователь) Тогда
		ПодготовленныйURL = ПодготовленныйURL 
			+ СтруктураURL.Аутентификация.Пользователь + ":"
			+ СтруктураURL.Аутентификация.Пароль + "@";
	КонецЕсли;
	ПодготовленныйURL = ПодготовленныйURL + СтруктураURL.Сервер;
	Если ЗначениеЗаполнено(СтруктураURL.Порт) Тогда
		ПодготовленныйURL = ПодготовленныйURL + ":" + Формат(СтруктураURL.Порт, "ЧРГ=; ЧГ=");
	КонецЕсли;
	
	ПодготовленныйURL = ПодготовленныйURL + СобратьАдресРесурса(СтруктураURL, ПараметрыЗапроса);
		
	Возврат ПодготовленныйURL;
	
КонецФункции

Функция ЗаголовкиВСтроку(Заголовки)
	
	РазделительСтрок = Символы.ВК + Символы.ПС;
	Строки = Новый Массив;
	
	СортированныеЗаголовки = "Content-Disposition,Content-Type,Content-Location";
	Для Каждого Ключ Из СтрРазделить_Костыль(СортированныеЗаголовки, ",", Истина) Цикл
		Значение = ПолучитьЗначениеЗаголовка(Ключ, Заголовки);
		Если Значение <> Ложь И ЗначениеЗаполнено(Значение) Тогда
			Строки.Добавить(СтрШаблон_Костыль("%1: %2", Ключ, Значение));
		КонецЕсли;
	КонецЦикла;
	
	Ключи = СтрРазделить_Костыль(ВРег(СортированныеЗаголовки), ",", Истина);
	Для Каждого Заголовок Из Заголовки Цикл
		Если Ключи.Найти(ВРег(Заголовок.Ключ)) = Неопределено Тогда
			Строки.Добавить(СтрШаблон_Костыль("%1: %2", Заголовок.Ключ, Заголовок.Значение));
		КонецЕсли;
	КонецЦикла;
	Строки.Добавить(РазделительСтрок);
	
	Возврат СтрСоединить_Костыль(Строки, РазделительСтрок);
	
КонецФункции

Функция ПолучитьЗначениеПоКлючу(Структура, Ключ, ЗначениеПоУмолчанию = Неопределено)
	
	Если ТипЗнч(Структура) = Тип("Структура") И Структура.Свойство(Ключ) Тогда
		Значение = Структура[Ключ];
	ИначеЕсли ТипЗнч(Структура) = Тип("Соответствие") И Структура.Получить(Ключ) <> Неопределено Тогда
		Значение = Структура.Получить(Ключ);
	Иначе
		Значение = ЗначениеПоУмолчанию;
	КонецЕсли;
	
	Возврат Значение;
	
КонецФункции
	
Функция СоздатьПолеФормы(ИсходныеПараметры)
	
	Поле = Новый Структура("Имя,ИмяФайла,Данные,Тип,Заголовки");
	Поле.Имя = ИсходныеПараметры.Имя;
	Поле.Данные = ИсходныеПараметры.Данные;
	
	Поле.Тип = ПолучитьЗначениеПоКлючу(ИсходныеПараметры, "Тип");
	Поле.Заголовки = ПолучитьЗначениеПоКлючу(ИсходныеПараметры, "Заголовки", Новый Соответствие);
	Поле.ИмяФайла = ПолучитьЗначениеПоКлючу(ИсходныеПараметры, "ИмяФайла");
	
	Ключ = "Content-Disposition";
	Если ПолучитьЗначениеЗаголовка("content-disposition", Поле.Заголовки, Ключ) = Ложь Тогда
		Поле.Заголовки.Вставить("Content-Disposition", "form-data");
	КонецЕсли;
	
	Части = Новый Массив;
	Части.Добавить(Поле.Заголовки[Ключ]);
	Части.Добавить(СтрШаблон_Костыль("name=""%1""", Поле.Имя));
	Если ЗначениеЗаполнено(Поле.ИмяФайла) Тогда
		Части.Добавить(СтрШаблон_Костыль("filename=""%1""", Поле.ИмяФайла));
	КонецЕсли;
	
	Поле.Заголовки[Ключ] = СтрСоединить_Костыль(Части, "; ");
	Поле.Заголовки["Content-Type"] = Поле.Тип;
	
	Возврат Поле;
	
КонецФункции

Функция ЗакодироватьФайлы(HTTPЗапрос, Файлы, Данные)
	
	Части = Новый Массив;
	Если ЗначениеЗаполнено(Данные) Тогда
		Для Каждого Поле Из Данные Цикл
			Части.Добавить(СоздатьПолеФормы(Новый Структура("Имя,Данные", Поле.Ключ, Поле.Значение)));
		КонецЦикла;
	КонецЕсли;
	Если ТипЗнч(Файлы) = Тип("Массив") Тогда
		Для Каждого Файл Из Файлы Цикл
			Части.Добавить(СоздатьПолеФормы(Файл));
		КонецЦикла;
	Иначе
		Части.Добавить(СоздатьПолеФормы(Файлы));
	КонецЕсли;
	
	Разделитель = СтрЗаменить(Новый УникальныйИдентификатор, "-", "");
	РазделительСтрок = Символы.ВК + Символы.ПС;
	
	ТелоЗапроса = HTTPЗапрос.ПолучитьТелоКакПоток();
	ЗаписьДанных = Новый ЗаписьДанных(ТелоЗапроса, КодировкаТекста.UTF8, ПорядокБайтов.LittleEndian, "", "", Ложь);
	Для Каждого Часть Из Части Цикл
		ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + РазделительСтрок);
		ЗаписьДанных.ЗаписатьСтроку(ЗаголовкиВСтроку(Часть.Заголовки));
		Если ТипЗнч(Часть.Данные) = Тип("ДвоичныеДанные") Тогда
			ЗаписьДанных.Записать(Часть.Данные);
		Иначе
			ЗаписьДанных.ЗаписатьСтроку(Часть.Данные);
		КонецЕсли;
		ЗаписьДанных.ЗаписатьСтроку(РазделительСтрок);
	КонецЦикла;
	ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + "--" + РазделительСтрок);
	ЗаписьДанных.Закрыть();
	
	Возврат СтрШаблон_Костыль("multipart/form-data; boundary=%1", Разделитель);
	
КонецФункции

Процедура ПодготовитьТелоЗапроса(ПодготовленныйЗапрос, Данные, Файлы, Json, ПараметрыЗаписиJSON)
	
	СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);

	HTTPЗапрос = Новый HTTPЗапрос;
	HTTPЗапрос.АдресРесурса = СобратьАдресРесурса(СтруктураURL, ПодготовленныйЗапрос.ПараметрыЗапроса);
	Если ЗначениеЗаполнено(Файлы) Тогда
		ContentType = ЗакодироватьФайлы(HTTPЗапрос, Файлы, Данные);
	ИначеЕсли ЗначениеЗаполнено(Данные) Тогда
		ContentType = "application/x-www-form-urlencoded";
		Если ТипЗнч(Данные) = Тип("ДвоичныеДанные") Тогда
			HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(Данные);
		Иначе
			Если ТипЗнч(Данные) = Тип("Строка") Тогда
				Тело = Данные;
			Иначе
				Тело = КодироватьПараметрыЗапроса(Данные);
			КонецЕсли;
			HTTPЗапрос.УстановитьТелоИзСтроки(Тело, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
		КонецЕсли;
	ИначеЕсли Json <> Неопределено Тогда
		ContentType = "application/json";
		СтрокаJson = ОбъектВJson(Json, ПодготовленныйЗапрос.ПараметрыПреобразованияJSON, ПараметрыЗаписиJSON);
		HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJson, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
	Иначе
		ContentType = Неопределено;
	КонецЕсли;
	ЗначениеЗаголовка = ПолучитьЗначениеЗаголовка("content-type", ПодготовленныйЗапрос.Заголовки);
	Если ЗначениеЗаголовка = Ложь И ЗначениеЗаполнено(ContentType) Тогда
		ПодготовленныйЗапрос.Заголовки.Вставить("Content-Type", ContentType);
	КонецЕсли;
	
	HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки;
	
	УпаковатьЗапрос(HTTPЗапрос);

	ПодготовленныйЗапрос.Вставить("HTTPЗапрос", HTTPЗапрос);
	
КонецПроцедуры

Процедура ПодготовитьАутентификацию(ПодготовленныйЗапрос)
	
	ПодготовленныйЗапрос.Вставить("СобытияНаОтвет", Новый Массив);
	Если Не ЗначениеЗаполнено(ПодготовленныйЗапрос.Аутентификация) Тогда
		СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
		Если ЗначениеЗаполнено(СтруктураURL.Аутентификация) Тогда
			ПодготовленныйЗапрос.Аутентификация = СтруктураURL.Аутентификация;
		КонецЕсли;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ПодготовленныйЗапрос.Аутентификация) Тогда
		Если ПодготовленныйЗапрос.Аутентификация.Свойство("Тип") Тогда
			ТипАутентификации = НРег(ПодготовленныйЗапрос.Аутентификация.Тип);
			Если ТипАутентификации = "digest" Тогда
				ПодготовленныйЗапрос.СобытияНаОтвет.Добавить("ОбработкаОтветаСКодом401");
			КонецЕсли;
			Если ТипАутентификации = "aws4-hmac-sha256" Тогда
				ПодготовитьАутентификациюAWS4(ПодготовленныйЗапрос);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

Функция ОбъединитьCookies(ГлавныйИсточник, ДополнительныйИсточник)
	
	Cookies = Новый Соответствие;
	Для Каждого Cookie Из ПреобразоватьХранилищеCookiesВМассивCookies(ГлавныйИсточник) Цикл
		ДобавитьCookieВХранилище(Cookies, Cookie, Ложь);
	КонецЦикла;
	Для Каждого Cookie Из ПреобразоватьХранилищеCookiesВМассивCookies(ДополнительныйИсточник) Цикл
		ДобавитьCookieВХранилище(Cookies, Cookie, Ложь);
	КонецЦикла;
	
	Возврат Cookies;
	
КонецФункции

Функция ПреобразоватьХранилищеCookiesВМассивCookies(ХранилищеCookies)
	
	Cookies = Новый Массив;
	Если ТипЗнч(ХранилищеCookies) = Тип("Массив") Тогда
		Для Каждого Cookie Из ХранилищеCookies Цикл
			НоваяCookie = КонструкторCookie();
			ЗаполнитьЗначенияСвойств(НоваяCookie, Cookie);
			Cookies.Добавить(НоваяCookie);
		КонецЦикла;
		
		Возврат Cookies;
	КонецЕсли;
	
	Для Каждого Домен Из ХранилищеCookies Цикл
		Для Каждого Путь Из Домен.Значение Цикл
			Для Каждого Наименование Из Путь.Значение Цикл
				Cookies.Добавить(Наименование.Значение);
			КонецЦикла;
		КонецЦикла;
	КонецЦикла;
	
	Возврат Cookies;
	
КонецФункции

Функция ОбъединитьПараметрыАутентификации(ГлавныйИсточник, ДополнительныйИсточник)
	
	ПараметрыАутентификации = Новый Структура;
	Если ТипЗнч(ГлавныйИсточник) = Тип("Структура") Тогда
		Для Каждого Параметр Из ГлавныйИсточник Цикл
			ПараметрыАутентификации.Вставить(Параметр.Ключ, Параметр.Значение);
		КонецЦикла;
	КонецЕсли;
	Если ТипЗнч(ДополнительныйИсточник) = Тип("Структура") Тогда
		Для Каждого Параметр Из ДополнительныйИсточник Цикл
			Если Не ПараметрыАутентификации.Свойство(Параметр) Тогда
				ПараметрыАутентификации.Вставить(Параметр.Ключ, Параметр.Значение);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат ПараметрыАутентификации;
	
КонецФункции

Функция ОбъединитьЗаголовки(ГлавныйИсточник, ДополнительныйИсточник)
	
	Заголовки = Новый Соответствие;
	Для Каждого Заголовок Из ГлавныйИсточник Цикл
		Заголовки.Вставить(Заголовок.Ключ, Заголовок.Значение);
	КонецЦикла;
	Для Каждого Заголовок Из ДополнительныйИсточник Цикл
		Если Заголовки.Получить(Заголовок.Ключ) = Неопределено Тогда
			Заголовки.Вставить(Заголовок.Ключ, Заголовок.Значение);
		КонецЕсли;
	КонецЦикла;
	
	Возврат Заголовки;
	
КонецФункции

Функция ОбъединитьПараметрыЗапроса(ГлавныйИсточник, ДополнительныйИсточник)
	
	ПараметрыЗапроса = Новый Соответствие;
	Если ТипЗнч(ГлавныйИсточник) = Тип("Структура") ИЛИ ТипЗнч(ГлавныйИсточник) = Тип("Соответствие") Тогда
		Для Каждого Параметр Из ГлавныйИсточник Цикл
			ПараметрыЗапроса.Вставить(Параметр.Ключ, Параметр.Значение);
		КонецЦикла;
	КонецЕсли;
	Если ТипЗнч(ДополнительныйИсточник) = Тип("Структура") ИЛИ ТипЗнч(ДополнительныйИсточник) = Тип("Соответствие") Тогда
		Для Каждого Параметр Из ДополнительныйИсточник Цикл
			Если ПараметрыЗапроса.Получить(Параметр) = Неопределено Тогда
				ПараметрыЗапроса.Вставить(Параметр.Ключ, Параметр.Значение);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат ПараметрыЗапроса;
	
КонецФункции

Функция ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки)
	
	СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
	Соединение = ПолучитьСоединение(СтруктураURL, ПодготовленныйЗапрос.Аутентификация, Настройки, Сессия);
	Ответ = Соединение.ВызватьHTTPМетод(ПодготовленныйЗапрос.Метод, ПодготовленныйЗапрос.HTTPЗапрос);
	
	Для Каждого Обработчик Из ПодготовленныйЗапрос.СобытияНаОтвет Цикл
		Если Обработчик = "ОбработкаОтветаСКодом401" Тогда
			ОбработкаОтветаСКодом401(Сессия, ПодготовленныйЗапрос, Настройки, Ответ);
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ответ;
	
КонецФункции

Функция РассчитатьДлительностьПриостановки(Повтор, КоэффициентЭкспоненциальнойЗадержки, ЗаголовокRetryAfter, Остаток)

	Если ЗаголовокRetryAfter <> Ложь Тогда
		Длительность = ЧислоИзСтроки(ЗаголовокRetryAfter);

		Если Длительность = 0 Тогда
			Дата = ДатаИзСтрокиRFC7231(ЗаголовокRetryAfter);
			Если ЗначениеЗаполнено(Дата) Тогда
				Длительность = Дата - ТекущаяУниверсальнаяДата();
			КонецЕсли;
		КонецЕсли;
	Иначе
		Длительность = КоэффициентЭкспоненциальнойЗадержки * Pow(2, Повтор - 1);
	КонецЕсли;

	Длительность = Мин(Длительность, Остаток);

	Если Длительность < 0 Тогда
		Длительность = 0;
	КонецЕсли;

	Возврат Длительность;

КонецФункции

Функция НеобходимоПовторитьЗапрос(Ответ, Настройки, ОшибкаВыполненияЗапроса)

	Если Настройки.МаксимальноеКоличествоПовторов < 1 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ОшибкаВыполненияЗапроса <> Неопределено Тогда
		Возврат Истина;
	КонецЕсли;

	ПовторПриЛюбомКодеСостоянияБольшеИлиРавным500 = Настройки.ПовторятьДляКодовСостояний = Неопределено
		И Ответ.КодСостояния >= КодыСостоянияHTTP().ВнутренняяОшибкаСервера_500;
	КодСостоянияСоответствуетКодуСостоянияПовтора = ТипЗнч(Настройки.ПовторятьДляКодовСостояний) = Тип("Массив")
		И Настройки.ПовторятьДляКодовСостояний.Найти(Ответ.КодСостояния) <> Неопределено;
	Если ПовторПриЛюбомКодеСостоянияБольшеИлиРавным500 ИЛИ КодСостоянияСоответствуетКодуСостоянияПовтора Тогда
		Возврат Истина;
	КонецЕсли;

	ЗаголовокRetryAfter = ПолучитьЗначениеЗаголовка("retry-after", Ответ.Заголовки);
	Возврат ЗаголовокRetryAfter <> Ложь 
		И ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(Ответ.КодСостояния);

КонецФункции

Функция ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, Настройки)
	
	Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
	МиллисекундВСекунде = 1000;

	Повтор = 0;
	Длительность = 0;
	Пока Истина Цикл
		Попытка
			Ответ = ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки);
		Исключение
			ОшибкаВыполненияЗапроса = ИнформацияОбОшибке();
		КонецПопытки;

		Повтор = Повтор + 1;
		Длительность = (ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало) / МиллисекундВСекунде;

		Если Не НеобходимоПовторитьЗапрос(Ответ, Настройки, ОшибкаВыполненияЗапроса) Тогда
			Прервать;
		КонецЕсли;

		Если Повтор > Настройки.МаксимальноеКоличествоПовторов 
			ИЛИ Длительность > Настройки.МаксимальноеВремяПовторов Тогда
			Прервать;
		КонецЕсли;
	
		Если ОшибкаВыполненияЗапроса <> Неопределено 
			ИЛИ НЕ ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(Ответ.КодСостояния) Тогда
			ЗаголовокRetryAfter = Ложь;
		Иначе
			ЗаголовокRetryAfter = ПолучитьЗначениеЗаголовка("retry-after", Ответ.Заголовки);
		КонецЕсли;
		ДлительностьПриостановки = РассчитатьДлительностьПриостановки(
			Повтор,
			Настройки.КоэффициентЭкспоненциальнойЗадержки,
			ЗаголовокRetryAfter,
			Настройки.МаксимальноеВремяПовторов - Длительность);
		Приостановить(ДлительностьПриостановки);
	КонецЦикла;
	
	Если ОшибкаВыполненияЗапроса <> Неопределено Тогда
		ВызватьИсключение(ПодробноеПредставлениеОшибки(ОшибкаВыполненияЗапроса));
	КонецЕсли;

	ЗаголовокContentType = ПолучитьЗначениеЗаголовка("content-type", Ответ.Заголовки);
	Если ЗаголовокContentType = Ложь Тогда
		ЗаголовокContentType = "";
	КонецЕсли;
	
	ПодготовленныйОтвет = Новый Структура;
	ПодготовленныйОтвет.Вставить("ВремяВыполнения", ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало);
	ПодготовленныйОтвет.Вставить("Cookies", ИзвлечьCookies(Ответ.Заголовки, ПодготовленныйЗапрос.URL)); 
	ПодготовленныйОтвет.Вставить("Заголовки", Ответ.Заголовки);
	ПодготовленныйОтвет.Вставить("ЭтоПостоянныйРедирект", ЭтоПостоянныйРедирект(Ответ.КодСостояния, Ответ.Заголовки));
	ПодготовленныйОтвет.Вставить("ЭтоРедирект", ЭтоРедирект(Ответ.КодСостояния, Ответ.Заголовки));
	ПодготовленныйОтвет.Вставить("Кодировка", ПолучитьКодировкуИзЗаголовка(ЗаголовокContentType));
	ПодготовленныйОтвет.Вставить("Тело", Ответ.ПолучитьТелоКакДвоичныеДанные());
	ПодготовленныйОтвет.Вставить("КодСостояния", Ответ.КодСостояния);
	ПодготовленныйОтвет.Вставить("URL", ПодготовленныйЗапрос.URL);
	
	Сессия.Cookies = ОбъединитьCookies(Сессия.Cookies, ПодготовленныйОтвет.Cookies);
	
	Возврат ПодготовленныйОтвет;
	
КонецФункции

Процедура ПереопределитьМетод(ПодготовленныйЗапрос, Ответ)
	
	КодыСостоянияHTTP = КодыСостоянияHTTP();
	
	Метод = ПодготовленныйЗапрос.Метод;

	// http://tools.ietf.org/html/rfc7231#section-6.4.4
	Если Ответ.КодСостояния = КодыСостоянияHTTP.СмотретьДругое_303 И Метод <> "HEAD" Тогда
		Метод = "GET";
	КонецЕсли;
	
	// Поведение браузеров
	Если Ответ.КодСостояния = КодыСостоянияHTTP.ПеремещеноВременно_302 И Метод <> "HEAD" Тогда
		Метод = "GET";
	КонецЕсли;
	
	ПодготовленныйЗапрос.Метод = Метод;
	
КонецПроцедуры	

Функция ИзвлечьCookies(Заголовки, URL)
	
	ТекущееВремя = ТекущаяУниверсальнаяДата();
	Cookies = Новый Соответствие;
	Для Каждого ОчереднойЗаголовок Из Заголовки Цикл
		Если НРег(ОчереднойЗаголовок.Ключ) = "set-cookie" Тогда
			Для Каждого ЗаголовокCookie Из РазбитьНаОтдельныеЗаголовкиCookies(ОчереднойЗаголовок.Значение) Цикл
				Cookie = РаспарситьCookie(ЗаголовокCookie, URL, ТекущееВремя);
				Если Cookie = Неопределено Тогда
					Продолжить;
				КонецЕсли;
				Если Cookie.СрокДействия <= ТекущееВремя Тогда
					УдалитьCookieИзХранилища(Cookies, Cookie);
				Иначе
					ДобавитьCookieВХранилище(Cookies, Cookie);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Cookies;
	
КонецФункции

Функция РазбитьНаОтдельныеЗаголовкиCookies(Знач Заголовок)
	
	Заголовки = Новый Массив;
	
	Если Не ЗначениеЗаполнено(Заголовок) Тогда
		Возврат Заголовки;
	КонецЕсли;
	
	ЗапчастиЗаголовков = СтрРазделить_Костыль(Заголовок, ",", Ложь);
	
	ОтдельныйЗаголовок = ЗапчастиЗаголовков[0];
	Для Индекс = 1 По ЗапчастиЗаголовков.ВГраница() Цикл
		//Начало изменений Рогачев Алексей
		//ТочкаСЗапятой = СтрНайти(ЗапчастиЗаголовков[Индекс], ";");
		ТочкаСЗапятой = Найти(ЗапчастиЗаголовков[Индекс], ";");
		
		//Равно = СтрНайти(ЗапчастиЗаголовков[Индекс], "=");
		Равно = Найти(ЗапчастиЗаголовков[Индекс], "=");
		//Конец изменений Рогачев Алексей
		
		Если ТочкаСЗапятой И Равно И Равно < ТочкаСЗапятой Тогда
			Заголовки.Добавить(ОтдельныйЗаголовок);
			ОтдельныйЗаголовок = ЗапчастиЗаголовков[Индекс];
		Иначе
			ОтдельныйЗаголовок = ОтдельныйЗаголовок + ЗапчастиЗаголовков[Индекс];
		КонецЕсли;
	КонецЦикла;
	Заголовки.Добавить(ОтдельныйЗаголовок);
	
	Возврат Заголовки;
	
КонецФункции

Функция КонструкторCookie(Наименование = "", Значение = Неопределено)
	
	НовыйCookie = Новый Структура;
	НовыйCookie.Вставить("Наименование", Наименование);
	НовыйCookie.Вставить("Значение", Значение);
	НовыйCookie.Вставить("Домен", "");
	НовыйCookie.Вставить("Путь", "");
	НовыйCookie.Вставить("Порт");
	НовыйCookie.Вставить("СрокДействия", '39990101');
	НовыйCookie.Вставить("ТолькоБезопасноеСоединение");
	
	Возврат НовыйCookie;
	
КонецФункции

Функция СоздатьCookieИЗаполнитьОсновныеПараметры(Параметр)

	Части = СтрРазделить_Костыль(Параметр, "=", Ложь);
	Наименование = Части[0];
	Если Части.Количество() > 1 Тогда
		Значение = Части[1];
	КонецЕсли;

	Возврат КонструкторCookie(Наименование, Значение);

КонецФункции

Функция РаспарситьCookie(Заголовок, URL, ТекущееВремя)
	
	Cookie = Неопределено;
	Индекс = 0;
	
	Для Каждого Параметр Из СтрРазделить_Костыль(Заголовок, ";", Ложь) Цикл
		Индекс = Индекс + 1;
		Параметр = СокрЛП(Параметр);
		
		Если Индекс = 1 Тогда
			Cookie = СоздатьCookieИЗаполнитьОсновныеПараметры(Параметр);
			Продолжить;
		КонецЕсли;
		
		Части = СтрРазделить_Костыль(Параметр, "=", Ложь);
		Ключ = НРег(Части[0]);
		Если Части.Количество() > 1 Тогда
			Значение = Части[1];
		КонецЕсли;

		Если Ключ = "domain" Тогда
			Cookie.Домен = Значение;
		ИначеЕсли Ключ = "path" Тогда
			Cookie.Путь = Значение;
		ИначеЕсли Ключ = "secure" Тогда
			Cookie.ТолькоБезопасноеСоединение = Истина;
		ИначеЕсли Ключ = "max-age" Тогда
			СрокДействияMaxAge = ТекущееВремя + ЧислоИзСтроки(Значение);
		ИначеЕсли Ключ = "expires" Тогда
			Cookie.СрокДействия = ДатаИзСтрокиRFC7231(Значение);
		Иначе
			Продолжить;
		КонецЕсли;
	КонецЦикла;
	Если ЗначениеЗаполнено(Cookie) И ЗначениеЗаполнено(СрокДействияMaxAge) Тогда
		Cookie.СрокДействия = СрокДействияMaxAge;
	КонецЕсли;
	
	ДозаполнитьCookieНеявнымиЗначениями(Cookie, URL);

	Возврат Cookie;
	
КонецФункции

Процедура ДозаполнитьCookieНеявнымиЗначениями(Cookie, URL)

	Если Cookie = Неопределено Тогда
		Возврат;
	КонецЕсли;

	СтруктураURL = РазобратьURL(URL);
	Если Не ЗначениеЗаполнено(Cookie.Домен) Тогда
		Cookie.Домен = СтруктураURL.Сервер;
	КонецЕсли;
	Если Не ЗначениеЗаполнено(Cookie.Порт) И ЗначениеЗаполнено(СтруктураURL.Порт) Тогда
		Cookie.Порт = СтруктураURL.Порт;
	КонецЕсли;
	Если Не ЗначениеЗаполнено(Cookie.Путь) Тогда
		// Начало изменений Рогачев Алексей
		//ПозицияПоследнегоСлеша = СтрНайти(СтруктураURL.Путь, "/", НаправлениеПоиска.СКонца);
		ПозицияПоследнегоСлеша = СтрНайти_Костыль(СтруктураURL.Путь, "/", 1);
		// Конец изменений Рогачев Алексей
		Если ПозицияПоследнегоСлеша <= 1 Тогда
			Cookie.Путь = "/";
		Иначе
			Cookie.Путь = Лев(СтруктураURL.Путь, ПозицияПоследнегоСлеша - 1);
		КонецЕсли;
	КонецЕсли;

КонецПроцедуры

Функция ПолучитьЗначениеЗаголовка(Заголовок, ВсеЗаголовки, Ключ = Неопределено)
	
	Для Каждого ОчереднойЗаголовок Из ВсеЗаголовки Цикл
		Если НРег(ОчереднойЗаголовок.Ключ) = НРег(Заголовок) Тогда
			Ключ = ОчереднойЗаголовок.Ключ;
			Возврат ОчереднойЗаголовок.Значение;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

Функция ЭтоПостоянныйРедирект(КодСостояния, Заголовки)
	
	КодыСостоянияHTTP = КодыСостоянияHTTP();
	
	Возврат ЕстьЗаголовокLocation(Заголовки)
		И (КодСостояния = КодыСостоянияHTTP.ПеремещеноНавсегда_301
		ИЛИ КодСостояния = КодыСостоянияHTTP.ПостоянноеПеренаправление_308);
	
КонецФункции

Функция ЭтоРедирект(КодСостояния, Заголовки)
	
	КодыСостоянияHTTP = КодыСостоянияHTTP();
	
	СостоянияРедиректа = Новый Массив;
	СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПеремещеноНавсегда_301);
	СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПеремещеноВременно_302);
	СостоянияРедиректа.Добавить(КодыСостоянияHTTP.СмотретьДругое_303);
	СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ВременноеПеренаправление_307);
	СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПостоянноеПеренаправление_308);
	
	Возврат ЕстьЗаголовокLocation(Заголовки) И СостоянияРедиректа.Найти(КодСостояния) <> Неопределено;
	
КонецФункции

Функция ЕстьЗаголовокLocation(Заголовки)
	
	Возврат ПолучитьЗначениеЗаголовка("location", Заголовки) <> Ложь;
	
КонецФункции

Функция ПолучитьКодировкуИзЗаголовка(Знач Заголовок)

	Кодировка = Неопределено;
	
	Заголовок = НРег(СокрЛП(Заголовок));
	// Начало изменений Рогачев Алексей
	//ИндексРазделителя = СтрНайти(Заголовок, ";");
	ИндексРазделителя = Найти(Заголовок, ";");
	// Конец изменений Рогачев Алексей
	Если ИндексРазделителя Тогда
		ТипСодержимого = СокрЛП(Лев(Заголовок, ИндексРазделителя - 1));
		КлючКодировки = "charset=";
		//ИндексКодировки = СтрНайти(Заголовок, КлючКодировки);
		ИндексКодировки = Найти(Заголовок, КлючКодировки);
		// Конец изменений Рогачев Алексей
		Если ИндексКодировки Тогда
			// Начало изменений Рогачев Алексей
			//ИндексРазделителя = СтрНайти(Заголовок, ";", НаправлениеПоиска.СНачала, ИндексКодировки);
			ИндексРазделителя = СтрНайти_Костыль(Заголовок, ";", 0, ИндексКодировки);
			// Конец изменений Рогачев Алексей

			НачальнаяПозиция = ИндексКодировки + СтрДлина(КлючКодировки);
			Если ИндексРазделителя Тогда
				ДлинаКодировки = ИндексРазделителя - НачальнаяПозиция;
			Иначе
				ДлинаКодировки = СтрДлина(Заголовок);
			КонецЕсли;
			Кодировка = Сред(Заголовок, НачальнаяПозиция, ДлинаКодировки);
			Кодировка = СтрЗаменить(Кодировка, """", "");
			Кодировка = СтрЗаменить(Кодировка, "'", "");
		КонецЕсли;
	Иначе
		ТипСодержимого = Заголовок;
	КонецЕсли;
	
	// Начало изменений Рогачев Алексей
	//Если Кодировка = Неопределено И СтрНайти(ТипСодержимого, "text") Тогда
	Если Кодировка = Неопределено И Найти(ТипСодержимого, "text") Тогда
	// Конец изменений Рогачев Алексей	
		Кодировка = "iso-8859-1";
	КонецЕсли;
	
	Возврат Кодировка;
	
КонецФункции

Функция СобратьАдресРесурса(СтруктураURL, ПараметрыЗапроса)
	
	АдресРесурса = СтруктураURL.Путь;
	
	ОбъединенныеПараметрыЗапроса = ОбъединитьПараметрыЗапроса(ПараметрыЗапроса, СтруктураURL.ПараметрыЗапроса);
	Если ЗначениеЗаполнено(ОбъединенныеПараметрыЗапроса) Тогда
		АдресРесурса = АдресРесурса + "?" + КодироватьПараметрыЗапроса(ОбъединенныеПараметрыЗапроса);
	КонецЕсли;
	Если ЗначениеЗаполнено(СтруктураURL.Фрагмент) Тогда
		АдресРесурса = АдресРесурса + "#" + СтруктураURL.Фрагмент;
	КонецЕсли;
	
	Возврат АдресРесурса;
	
КонецФункции

Функция ПолучитьЗащищенноеСоединение(ДополнительныеПараметры)
	
	Если ДополнительныеПараметры.ПроверятьSSL = Ложь Тогда
		СертификатыУЦ = Неопределено;
	ИначеЕсли ТипЗнч(ДополнительныеПараметры.ПроверятьSSL) = Тип("СертификатыУдостоверяющихЦентровФайл") Тогда
		СертификатыУЦ = ДополнительныеПараметры.ПроверятьSSL;
	Иначе
		СертификатыУЦ = Новый СертификатыУдостоверяющихЦентровОС;
	КонецЕсли;
	КлиентскийСертификат = Неопределено;
	Если ДополнительныеПараметры.КлиентскийСертификатSSL = Тип("СертификатКлиентаФайл") 
		ИЛИ ДополнительныеПараметры.КлиентскийСертификатSSL = Тип("СертификатКлиентаWindows") Тогда
		КлиентскийСертификат = ДополнительныеПараметры.КлиентскийСертификатSSL;
	КонецЕсли;
	
	Возврат Новый ЗащищенноеСоединениеOpenSSL(КлиентскийСертификат, СертификатыУЦ);
	
КонецФункции

Функция ПолучитьСоединение(ПараметрыСоединения, Аутентификация, ДополнительныеПараметры, Сессия)
	
	Если Не ЗначениеЗаполнено(ПараметрыСоединения.Порт) Тогда
		Если ПараметрыСоединения.Схема = "https" Тогда
			ПараметрыСоединения.Порт = 443;
		Иначе
			ПараметрыСоединения.Порт = 80;
		КонецЕсли;
	КонецЕсли;
	
	ЗащищенноеСоединение = Неопределено;
	Если ПараметрыСоединения.Схема = "https" Тогда
		ЗащищенноеСоединение = ПолучитьЗащищенноеСоединение(ДополнительныеПараметры);
	КонецЕсли;
	
	Пользователь = "";
	Пароль = "";
	Если ЗначениеЗаполнено(Аутентификация) Тогда
		Если Аутентификация.Свойство("Пользователь") И Аутентификация.Свойство("Пароль") Тогда
			Пользователь = Аутентификация.Пользователь;
			Пароль = Аутентификация.Пароль;
		КонецЕсли;
	КонецЕсли;
	
	ИспользоватьАутентификациюОС = Аутентификация.Свойство("ИспользоватьАутентификациюОС") 
		И Аутентификация.ИспользоватьАутентификациюОС = Истина;
		
	ПараметрыДляРасчетаИдентификатора = Новый Массив;
	ПараметрыДляРасчетаИдентификатора.Добавить(ПараметрыСоединения.Сервер);
	ПараметрыДляРасчетаИдентификатора.Добавить(ПараметрыСоединения.Порт);
	ПараметрыДляРасчетаИдентификатора.Добавить(Пользователь);
	ПараметрыДляРасчетаИдентификатора.Добавить(Пароль);
	ПараметрыДляРасчетаИдентификатора.Добавить(ДополнительныеПараметры.Таймаут);
	ПараметрыДляРасчетаИдентификатора.Добавить(ИспользоватьАутентификациюОС);
	ПараметрыДляРасчетаИдентификатора.Добавить(ЗащищенноеСоединение);
	ПараметрыДляРасчетаИдентификатора.Добавить(ДополнительныеПараметры.Прокси);
	
	Если Не Сессия.Свойство("СлужебныеДанные") ИЛИ ТипЗнч(Сессия.СлужебныеДанные) <> Тип("Структура") Тогда
		Сессия.Вставить("СлужебныеДанные", Новый Структура);
	КонецЕсли;
	Если Не Сессия.СлужебныеДанные.Свойство("ПулСоединений") Тогда
		Сессия.СлужебныеДанные.Вставить("ПулСоединений", Новый Соответствие);
	КонецЕсли;
	ПулСоединений = Сессия.СлужебныеДанные.ПулСоединений;
	
	ИдентификаторСоединения = ПолучитьИдентификаторСоединения(ПараметрыДляРасчетаИдентификатора);
	
	Если ПулСоединений.Получить(ИдентификаторСоединения) = Неопределено Тогда
		НовоеСоединение = Новый HTTPСоединение(
			ПараметрыСоединения.Сервер,
			ПараметрыСоединения.Порт,
			Пользователь, Пароль,
			ДополнительныеПараметры.Прокси, 
			ДополнительныеПараметры.Таймаут, 
			ЗащищенноеСоединение,
			ИспользоватьАутентификациюОС);
		ПулСоединений.Вставить(ИдентификаторСоединения, НовоеСоединение);
	КонецЕсли;
	
	Возврат ПулСоединений[ИдентификаторСоединения];
	
КонецФункции

Функция ПолучитьИдентификаторСоединения(ПараметрыСоединения)
	
	ПараметрыДляРасчетаИдентификатора = Новый Массив;
	
	Для Каждого Элемент Из ПараметрыСоединения Цикл
		ТипЭлемента = ТипЗнч(Элемент);
		Если ТипЭлемента = Тип("ИнтернетПрокси") Тогда
			ПараметрыДляРасчетаИдентификатора.Добавить(СтрСоединить_Костыль(Элемент.НеИспользоватьПроксиДляАдресов, ""));
			ПараметрыДляРасчетаИдентификатора.Добавить(XMLСтрока(Элемент.НеИспользоватьПроксиДляЛокальныхАдресов));
			ПараметрыДляРасчетаИдентификатора.Добавить(Элемент.Пользователь);
			ПараметрыДляРасчетаИдентификатора.Добавить(Элемент.Пароль);
		ИначеЕсли ТипЭлемента = Тип("ЗащищенноеСоединениеOpenSSL") Тогда
			// Для упрощения будет считать, что сертификаты в рамках сессии не меняются
			Если Элемент.СертификатКлиента = Неопределено Тогда
				ПараметрыДляРасчетаИдентификатора.Добавить("");
			Иначе
				ПараметрыДляРасчетаИдентификатора.Добавить(Строка(ТипЗнч(Элемент.СертификатКлиента)));
			КонецЕсли;
			Если Элемент.СертификатыУдостоверяющихЦентров = Неопределено Тогда
				ПараметрыДляРасчетаИдентификатора.Добавить("");
			Иначе
				ПараметрыДляРасчетаИдентификатора.Добавить(Строка(ТипЗнч(Элемент.СертификатыУдостоверяющихЦентров)));
			КонецЕсли;
		Иначе
			ПараметрыДляРасчетаИдентификатора.Добавить(XMLСтрока(Элемент));
		КонецЕсли;
	КонецЦикла;
	
	Возврат ХешированиеДанных(ХешФункция.MD5, СтрСоединить_Костыль(ПараметрыДляРасчетаИдентификатора, ""));
	
КонецФункции

Функция ВыбратьЗначение(ОсновноеЗначение, ДополнительныеЗначения, Ключ, ЗначениеПоУмолчанию)
	
	Если ОсновноеЗначение <> Неопределено Тогда
		Возврат ОсновноеЗначение;
	КонецЕсли;
	
	Значение = ПолучитьЗначениеПоКлючу(ДополнительныеЗначения, Ключ);
	Если Значение <> Неопределено Тогда
		Возврат Значение;
	КонецЕсли;
	
	Возврат ЗначениеПоУмолчанию;
	
КонецФункции

Функция ЗаполнитьПараметрыЗапроса(Путь)

	ПараметрыЗапроса = Новый Соответствие;
	
	Запрос = "";
	РазбитьСтрокуПоРазделителю(Запрос, Путь, "?", Истина);
	Для Каждого СтрокаКлючРавноПараметр Из СтрРазделить_Костыль(Запрос, "&", Ложь) Цикл
		СтрокаКлючРавноПараметр = РаскодироватьСтроку(
			СтрокаКлючРавноПараметр, СпособКодированияСтроки.URLВКодировкеURL);
        //Начало изменений Рогачев Алексей
		//ПозицияРавно = СтрНайти(СтрокаКлючРавноПараметр, "=");
		ПозицияРавно = Найти(СтрокаКлючРавноПараметр, "=");
		//Конец изменений Рогачев Алексей
		Если ПозицияРавно = 0 Тогда
			Ключ = СтрокаКлючРавноПараметр;
			Значение = Неопределено;
		Иначе
			Ключ = Лев(СтрокаКлючРавноПараметр, ПозицияРавно - 1);
			Значение = Сред(СтрокаКлючРавноПараметр, ПозицияРавно + 1);
		КонецЕсли;

		Если ПараметрыЗапроса.Получить(Ключ) <> Неопределено Тогда
			Если ТипЗнч(ПараметрыЗапроса[Ключ]) = Тип("Массив") Тогда
				ПараметрыЗапроса[Ключ].Добавить(Значение);
			Иначе
				Значения = Новый Массив;
				Значения.Добавить(ПараметрыЗапроса[Ключ]);
				Значения.Добавить(Значение);
				ПараметрыЗапроса[Ключ] = Значения;
			КонецЕсли;
		Иначе
			ПараметрыЗапроса.Вставить(Ключ, Значение);
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат ПараметрыЗапроса;

КонецФункции

Процедура РазбитьСтрокуПоРазделителю(ИзвлекаемаяЧасть, ОстальнаяЧасть, Разделитель, Инверсия = Ложь)
	//Начало изменений Рогачев Алексей
	//Индекс = СтрНайти(ОстальнаяЧасть, Разделитель);
	Индекс = Найти(ОстальнаяЧасть, Разделитель);
	//Конец изменений Рогачев Алексей
	Если Индекс Тогда
		ИзвлекаемаяЧасть = Лев(ОстальнаяЧасть, Индекс - 1);
		ОстальнаяЧасть = Сред(ОстальнаяЧасть, Индекс + СтрДлина(Разделитель));
		Если Инверсия Тогда
			ДляОбмена = ИзвлекаемаяЧасть;
			ИзвлекаемаяЧасть = ОстальнаяЧасть;
			ОстальнаяЧасть = ДляОбмена;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

Функция РазделитьПоПервомуНайденномуРазделителю(Строка, Разделители)
	
	МинимальныйИндекс = СтрДлина(Строка);
	ПервыйРазделитель = "";
	
	Для Каждого Разделитель Из Разделители Цикл
		//Начало изменений Рогачев Алексей
		//Индекс = СтрНайти(Строка, Разделитель);
		Индекс = Найти(Строка, Разделитель);
		//Конец изменений Рогачев Алексей
		Если Индекс = 0 Тогда
			Продолжить;
		КонецЕсли;
		Если Индекс < МинимальныйИндекс Тогда
			МинимальныйИндекс = Индекс;
			ПервыйРазделитель = Разделитель;
		КонецЕсли;
	КонецЦикла;
	
	Результат = Новый Массив;
	Если ЗначениеЗаполнено(ПервыйРазделитель) Тогда
		Результат.Добавить(Лев(Строка, МинимальныйИндекс - 1));
		Результат.Добавить(Сред(Строка, МинимальныйИндекс + СтрДлина(ПервыйРазделитель)));
		Результат.Добавить(ПервыйРазделитель);
	Иначе
		Результат.Добавить(Строка);
		Результат.Добавить("");
		Результат.Добавить(Неопределено);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Функция ДополнитьПараметрыПреобразованияJSON(ПараметрыПреобразования)
	
	ПараметрыПреобразованияJSON = ПолучитьПараметрыПреобразованияJSONПоУмолчанию();
	Если ЗначениеЗаполнено(ПараметрыПреобразования) Тогда
		Для Каждого Параметр Из ПараметрыПреобразования Цикл
			Если ПараметрыПреобразованияJSON.Свойство(Параметр.Ключ) Тогда
				ПараметрыПреобразованияJSON.Вставить(Параметр.Ключ, Параметр.Значение);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат ПараметрыПреобразованияJSON;
	
КонецФункции

Функция ДополнитьПараметрыЗаписиJSON(ПараметрыЗаписи)
	
	ПараметрыЗаписиJSON = ПолучитьПараметрыЗаписиJSONПоУмолчанию();
	Если ЗначениеЗаполнено(ПараметрыЗаписи) Тогда
		Для Каждого Параметр Из ПараметрыЗаписи Цикл
			Если ПараметрыЗаписиJSON.Свойство(Параметр.Ключ) Тогда
				ПараметрыЗаписиJSON.Вставить(Параметр.Ключ, Параметр.Значение);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат ПараметрыЗаписиJSON;
	
КонецФункции

#Область АутентификацияAWS4

Функция ПолучитьКлючПодписиAWS4(СекретныйКлюч, Дата, Регион, Сервис)
	
	КлючДата = ПодписатьСообщениеHMAC("AWS4" + СекретныйКлюч, Дата);
	КлючРегион = ПодписатьСообщениеHMAC(КлючДата, Регион);
	КлючСервис = ПодписатьСообщениеHMAC(КлючРегион, Сервис);
	
	Возврат ПодписатьСообщениеHMAC(КлючСервис, "aws4_request");
	
КонецФункции

Функция ПодписатьСообщениеHMAC(Знач Ключ, Знач Сообщение, Знач Алгоритм = Неопределено)
	
	Если Алгоритм = Неопределено Тогда
		Алгоритм = ХешФункция.SHA256;
	КонецЕсли;
	
	Если ТипЗнч(Ключ) = Тип("Строка") Тогда
		Ключ = ПолучитьДвоичныеДанныеИзСтроки(Ключ, КодировкаТекста.UTF8, Ложь);
	КонецЕсли;
	Если ТипЗнч(Сообщение) = Тип("Строка") Тогда
		Сообщение = ПолучитьДвоичныеДанныеИзСтроки(Сообщение, КодировкаТекста.UTF8, Ложь);
	КонецЕсли;

	Возврат HMAC(Ключ, Сообщение, Алгоритм);
	
КонецФункции

Процедура ПодготовитьАутентификациюAWS4(ПодготовленныйЗапрос)

	ЗначениеЗаголовка = ПолучитьЗначениеЗаголовка("x-amz-date", ПодготовленныйЗапрос.Заголовки);
	Если ЗначениеЗаголовка <> Ложь Тогда
		ТекущееВремя = Дата(СтрЗаменить(СтрЗаменить(ЗначениеЗаголовка, "T", ""), "Z", ""));
	Иначе
		ТекущееВремя = ТекущаяУниверсальнаяДата();
	КонецЕсли;
	ПодготовленныйЗапрос.Заголовки["x-amz-date"] = Формат(ТекущееВремя, "ДФ=yyyyMMddTHHmmssZ");
	ОбластьДействияДата = Формат(ТекущееВремя, "ДФ=yyyyMMdd");
	
	ПодготовленныйЗапрос.Заголовки["x-amz-content-sha256"] = 
		ХешированиеДанных(ХешФункция.SHA256, ПодготовленныйЗапрос.HTTPЗапрос.ПолучитьТелоКакПоток());
		
	СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
	
	КаноническиеЗаголовки = ПолучитьКаноническиеЗаголовкиAWS4(ПодготовленныйЗапрос.Заголовки, СтруктураURL);
	
	КаноническийПуть = СтруктураURL.Путь;
	КаноническиеПараметрыЗапроса = ПолучитьКаноническиеПараметрыЗапросаAWS4(СтруктураURL.ПараметрыЗапроса);
	
	ЧастиЗапроса = Новый Массив;
	ЧастиЗапроса.Добавить(ПодготовленныйЗапрос.Метод);
	ЧастиЗапроса.Добавить(КаноническийПуть);
	ЧастиЗапроса.Добавить(КаноническиеПараметрыЗапроса);
	ЧастиЗапроса.Добавить(КаноническиеЗаголовки.КаноническиеЗаголовки);
	ЧастиЗапроса.Добавить(КаноническиеЗаголовки.ПодписываемыеЗаголовки);
	ЧастиЗапроса.Добавить(ПодготовленныйЗапрос.Заголовки["x-amz-content-sha256"]);
	КаноническийЗапрос = СтрСоединить_Костыль(ЧастиЗапроса, Символы.ПС);
	
	ЧастиОбластиДействия = Новый Массив;
	ЧастиОбластиДействия.Добавить(ОбластьДействияДата);
	ЧастиОбластиДействия.Добавить(ПодготовленныйЗапрос.Аутентификация.Регион);
	ЧастиОбластиДействия.Добавить(ПодготовленныйЗапрос.Аутентификация.Сервис);
	ЧастиОбластиДействия.Добавить("aws4_request");
	ОбластьДействия = СтрСоединить_Костыль(ЧастиОбластиДействия, "/");
	
	ЧастиСтрокиДляПодписи = Новый Массив;
	ЧастиСтрокиДляПодписи.Добавить(ПодготовленныйЗапрос.Аутентификация.Тип);
	ЧастиСтрокиДляПодписи.Добавить(ПодготовленныйЗапрос.Заголовки["x-amz-date"]);
	ЧастиСтрокиДляПодписи.Добавить(ОбластьДействия);
	ЧастиСтрокиДляПодписи.Добавить(ХешированиеДанных(ХешФункция.SHA256, КаноническийЗапрос));
	СтрокаДляПодписи = СтрСоединить_Костыль(ЧастиСтрокиДляПодписи, Символы.ПС);
	
	Ключ = ПолучитьКлючПодписиAWS4(
		ПодготовленныйЗапрос.Аутентификация.СекретныйКлюч,
		ОбластьДействияДата,
		ПодготовленныйЗапрос.Аутентификация.Регион,
		ПодготовленныйЗапрос.Аутентификация.Сервис);
	Подпись = НРег(ПолучитьHexСтрокуИзДвоичныхДанных(ПодписатьСообщениеHMAC(Ключ, СтрокаДляПодписи)));
	
	ПодготовленныйЗапрос.Заголовки["Authorization"] = СтрШаблон_Костыль(
		"%1 Credential=%2/%3, SignedHeaders=%4, Signature=%5",
		ПодготовленныйЗапрос.Аутентификация.Тип,
		ПодготовленныйЗапрос.Аутентификация.ИдентификаторКлючаДоступа,
		ОбластьДействия,
		КаноническиеЗаголовки.ПодписываемыеЗаголовки,
		Подпись);
	
	ПодготовленныйЗапрос.HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки;

КонецПроцедуры

Функция ЭтоСтандартныйПорт(СтруктураURL)
	
	СтандартныйПортHTTP = 80;
	СтандартныйПортHTTPS = 443;
	
	Возврат (СтруктураURL.Схема = "http" И СтруктураURL.Порт = СтандартныйПортHTTP) 
		ИЛИ (СтруктураURL.Схема = "https" И СтруктураURL.Порт = СтандартныйПортHTTPS);
	
КонецФункции

Функция СформироватьЗначениеЗаголовкаHost(СтруктураURL)
	
	Host = СтруктураURL.Сервер;
	Если ЗначениеЗаполнено(СтруктураURL.Порт) И НЕ ЭтоСтандартныйПорт(СтруктураURL) Тогда
		Host = Host + ":" + Формат(СтруктураURL.Порт, "ЧРГ=; ЧГ=");
	КонецЕсли;
	
	Возврат Host;
	
КонецФункции

Функция ПолучитьКаноническиеЗаголовкиAWS4(Заголовки, СтруктураURL)
	
	Список = Новый СписокЗначений;
	
	ЗаголовокHostЕстьВЗапросе = Ложь;
	ЗаголовкиПоУмолчанию = ЗаголовкиПоУмолчаниюAWS4();
	Для Каждого ОчереднойЗаголовок Из Заголовки Цикл
		Заголовок = НРег(ОчереднойЗаголовок.Ключ);
		Если ЗаголовкиПоУмолчанию.Исключения.Найти(Заголовок) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ЗаголовокHostЕстьВЗапросе = Макс(ЗаголовокHostЕстьВЗапросе, Заголовок = "host");
		
		Если ЗаголовкиПоУмолчанию.Равно.Найти(Заголовок) <> Неопределено Тогда
			Список.Добавить(Заголовок, СокрЛП(ОчереднойЗаголовок.Значение));
		Иначе
			Для Каждого Префикс Из ЗаголовкиПоУмолчанию.НачинаетсяС Цикл
				Если СтрНачинаетсяС_Костыль(Заголовок, Префикс) Тогда
					Список.Добавить(Заголовок, СокрЛП(ОчереднойЗаголовок.Значение));
					Прервать;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
	КонецЦикла;
	
	Если Не ЗаголовокHostЕстьВЗапросе Тогда
		Список.Добавить("host", СформироватьЗначениеЗаголовкаHost(СтруктураURL));
	КонецЕсли;
	
	Список.СортироватьПоЗначению(НаправлениеСортировки.Возр);
	
	КаноническиеЗаголовки = Новый Массив;
	ПодписываемыеЗаголовки = Новый Массив;
	Для Каждого ЭлементСписка Из Список Цикл
		КаноническиеЗаголовки.Добавить(ЭлементСписка.Значение + ":" + ЭлементСписка.Представление);
		ПодписываемыеЗаголовки.Добавить(ЭлементСписка.Значение);
	КонецЦикла;
	КаноническиеЗаголовки.Добавить("");
	
	КаноническиеЗаголовки = СтрСоединить_Костыль(КаноническиеЗаголовки, Символы.ПС);
	ПодписываемыеЗаголовки = СтрСоединить_Костыль(ПодписываемыеЗаголовки, ";");
	Возврат Новый Структура(
		"КаноническиеЗаголовки, ПодписываемыеЗаголовки",
		КаноническиеЗаголовки, ПодписываемыеЗаголовки);
	
КонецФункции

Функция ПолучитьКаноническиеПараметрыЗапросаAWS4(ПараметрыЗапроса)
	
	Список = Новый СписокЗначений;
	Для Каждого ОчереднойПараметрЗапроса Из ПараметрыЗапроса Цикл
		Список.Добавить(ОчереднойПараметрЗапроса.Ключ, СокрЛП(ОчереднойПараметрЗапроса.Значение));
	КонецЦикла;
	Список.СортироватьПоЗначению(НаправлениеСортировки.Возр);
	
	КаноническиеПараметры = Новый Массив;
	Для Каждого ЭлементСписка Из Список Цикл
		ЗначениеПараметра = КодироватьСтроку(ЭлементСписка.Представление, СпособКодированияСтроки.КодировкаURL);
		КаноническиеПараметры.Добавить(ЭлементСписка.Значение + "=" + ЗначениеПараметра);
	КонецЦикла;
	
	Возврат СтрСоединить_Костыль(КаноническиеПараметры, "&");
		
КонецФункции

Функция ЗаголовкиПоУмолчаниюAWS4()
	
	Заголовки = Новый Структура;
	Заголовки.Вставить("Равно", СтрРазделить_Костыль("host,content-type,date", ",", Истина));
	Заголовки.Вставить("НачинаетсяС", СтрРазделить_Костыль("x-amz-", ",", Истина));
	Заголовки.Вставить("Исключения", СтрРазделить_Костыль("x-amz-client-context", ",", Истина));
	
	Возврат Заголовки;
	
КонецФункции

#КонецОбласти

#Область КодированиеДекодированиеДанных

#Область СлужебныеСтруктурыZip

// Описание структур см. здесь https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT

Функция ZipРазмерLFH()
	
	Возврат 34;
	
КонецФункции

Функция ZipРазмерDD()
	
	Возврат 16;
	
КонецФункции

Функция ZipРазмерCDH()
	
	Возврат 50;
	
КонецФункции

Функция ZipРазмерEOCD()
	
	Возврат 22;
	
КонецФункции

Функция ZipLFH()
	
	// Local file header
	Буфер = Новый БуферДвоичныхДанных(ZipРазмерLFH());
	Буфер.ЗаписатьЦелое32(0, 67324752); // signature 0x04034b50
	Буфер.ЗаписатьЦелое16(4, 20);       // version
	Буфер.ЗаписатьЦелое16(6, 10);       // bit flags	
	Буфер.ЗаписатьЦелое16(8, 8);        // compression method
	Буфер.ЗаписатьЦелое16(10, 0);       // time
	Буфер.ЗаписатьЦелое16(12, 0);       // date
	Буфер.ЗаписатьЦелое32(14, 0);       // crc-32
	Буфер.ЗаписатьЦелое32(18, 0);       // compressed size
	Буфер.ЗаписатьЦелое32(22, 0);       // uncompressed size
	Буфер.ЗаписатьЦелое16(26, 4);       // filename legth - "data"
	Буфер.ЗаписатьЦелое16(28, 0);       // extra field length
	Буфер.Записать(30, ПолучитьБуферДвоичныхДанныхИзСтроки("data", "ascii", Ложь));
	
	Возврат Буфер;
	
КонецФункции

Функция ZipDD(CRC32, РазмерСжатыхДанных, РазмерНесжатыхДанных)
	
	// Data descriptor
	Буфер = Новый БуферДвоичныхДанных(ZipРазмерDD());
	Буфер.ЗаписатьЦелое32(0, 134695760);
	Буфер.ЗаписатьЦелое32(4, CRC32);
	Буфер.ЗаписатьЦелое32(8, РазмерСжатыхДанных);
	Буфер.ЗаписатьЦелое32(12, РазмерНесжатыхДанных);
	
	Возврат Буфер;
	
КонецФункции

Функция ZipCDH(CRC32, РазмерСжатыхДанных, РазмерНесжатыхДанных)
	
	// Central directory header
	Буфер = Новый БуферДвоичныхДанных(ZipРазмерCDH());
	Буфер.ЗаписатьЦелое32(0, 33639248);              // signature 0x02014b50
	Буфер.ЗаписатьЦелое16(4, 798);                   // version made by
	Буфер.ЗаписатьЦелое16(6, 20);                    // version needed to extract
	Буфер.ЗаписатьЦелое16(8, 10);                    // bit flags
	Буфер.ЗаписатьЦелое16(10, 8);                    // compression method
	Буфер.ЗаписатьЦелое16(12, 0);                    // time
	Буфер.ЗаписатьЦелое16(14, 0);                    // date
	Буфер.ЗаписатьЦелое32(16, CRC32);                // crc-32
	Буфер.ЗаписатьЦелое32(20, РазмерСжатыхДанных);   // compressed size
	Буфер.ЗаписатьЦелое32(24, РазмерНесжатыхДанных); // uncompressed size
	Буфер.ЗаписатьЦелое16(28, 4);                    // file name length
	Буфер.ЗаписатьЦелое16(30, 0);                    // extra field length
	Буфер.ЗаписатьЦелое16(32, 0);                    // file comment length
	Буфер.ЗаписатьЦелое16(34, 0);                    // disk number start
	Буфер.ЗаписатьЦелое16(36, 0);                    // internal file attributes
	Буфер.ЗаписатьЦелое32(38, 2176057344);           // external file attributes
	Буфер.ЗаписатьЦелое32(42, 0);                    // relative offset of local header
	Буфер.Записать(46, ПолучитьБуферДвоичныхДанныхИзСтроки("data", "ascii", Ложь));
	
	Возврат Буфер;

КонецФункции

Функция ZipEOCD(РазмерСжатыхДанных)
	
	// End of central directory
	РазмерCDH = 50;
	Буфер = Новый БуферДвоичныхДанных(ZipРазмерEOCD());
	Буфер.ЗаписатьЦелое32(0, 101010256); // signature 0x06054b50
	Буфер.ЗаписатьЦелое16(4, 0); // number of this disk
	Буфер.ЗаписатьЦелое16(6, 0); // number of the disk with the start of the central directory
	Буфер.ЗаписатьЦелое16(8, 1); // total number of entries in the central directory on this disk
	Буфер.ЗаписатьЦелое16(10, 1); // total number of entries in the central directory
	Буфер.ЗаписатьЦелое32(12, РазмерCDH); // size of the central directory	
	// offset of start of central directory with respect to the starting disk number
	Буфер.ЗаписатьЦелое32(16, ZipРазмерLFH() + РазмерСжатыхДанных + ZipРазмерDD()); 
	Буфер.ЗаписатьЦелое16(20, 0); // the starting disk number
	
	Возврат Буфер;
	
КонецФункции

#КонецОбласти

#Область СлужебныеСтруктурыGZip

// Описание структур см. здесь https://www.ietf.org/rfc/rfc1952.txt

Функция GZipРазмерHeader()

	Возврат 10;

КонецФункции

Функция GZipРазмерFooter()

	Возврат 8;

КонецФункции

Функция GZipHeader()

	Буфер = Новый БуферДвоичныхДанных(GZipРазмерHeader());
	Буфер[0] = 31;               // ID1 0x1f
	Буфер[1] = 139;              // ID2 0x8b
	Буфер[2] = 8;                // compression method (08 for DEFLATE)
	Буфер[3] = 0;                // header flags
	Буфер.ЗаписатьЦелое32(4, 0); // timestamp
	Буфер[8] = 0;                // compression flags
	Буфер[9] = 255;              // operating system ID

	Возврат Буфер;

КонецФункции

Функция GZipFooter(CRC32, РазмерИсходныхДанных)
	
	Буфер = Новый БуферДвоичныхДанных(GZipРазмерFooter());
	Буфер.ЗаписатьЦелое32(0, CRC32);
	Буфер.ЗаписатьЦелое32(4, РазмерИсходныхДанных);

	Возврат Буфер;

КонецФункции

#КонецОбласти

Функция ПрочитатьZip(СжатыеДанные, ТекстОшибки = Неопределено)
	
#Если МобильноеПриложениеСервер Тогда
	ВызватьИсключение(НСтр("ru = 'Работа с Zip-файлами в мобильной платформе не поддерживается'"));
#Иначе
	Каталог = ПолучитьИмяВременногоФайла();
	ЧтениеZip = Новый ЧтениеZipФайла(СжатыеДанные);
	ИмяФайла = ЧтениеZip.Элементы[0].Имя;
	Попытка
		ЧтениеZip.Извлечь(ЧтениеZip.Элементы[0], Каталог, РежимВосстановленияПутейФайловZIP.НеВосстанавливать);
	Исключение
		// Игнорируем проверку целостности архива, просто читаем результат
		ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
	КонецПопытки;
	ЧтениеZip.Закрыть();
	
	Результат = Новый ДвоичныеДанные(Каталог + ПолучитьРазделительПути() + ИмяФайла);
	УдалитьФайлы(Каталог);
	
	Возврат Результат;
#КонецЕсли
	
КонецФункции

Функция ЗаписатьZip(Данные)

#Если МобильноеПриложениеСервер Тогда
	ВызватьИсключение(НСтр("ru = 'Работа с Zip-файлами в мобильной платформе не поддерживается'"));
#Иначе
	ВременныйФайл = ПолучитьИмяВременногоФайла(".bin");
	Данные.Записать(ВременныйФайл);
	ПотокZip = Новый ПотокВПамяти;
	ЗаписьZip = Новый ЗаписьZipФайла(ПотокZip);
	ЗаписьZip.Добавить(ВременныйФайл);
	ЗаписьZip.Записать();
	УдалитьФайлы(ВременныйФайл);

	Возврат ПотокZip.ЗакрытьИПолучитьДвоичныеДанные();
#КонецЕсли

КонецФункции

#КонецОбласти

#Область ОбработчикиСобытий

Процедура ОбработкаОтветаСКодом401(Сессия, ПодготовленныйЗапрос, Настройки, Ответ)
	
	Если ЭтоРедирект(Ответ.КодСостояния, Ответ.Заголовки) Тогда
		Возврат;
	КонецЕсли;
	
	КодыСостоянияHTTP = КодыСостоянияHTTP();
	Если Ответ.КодСостояния < КодыСостоянияHTTP.НеверныйЗапрос_400 
		ИЛИ Ответ.КодСостояния >= КодыСостоянияHTTP.ВнутренняяОшибкаСервера_500 Тогда
		Возврат;
	КонецЕсли;
	
	Значение = ПолучитьЗначениеЗаголовка("www-authenticate", Ответ.Заголовки);
	// Начало изменений Рогачев Алексей
	//Если Значение <> Ложь И СтрНайти(НРег(Значение), "digest") Тогда
	Если Значение <> Ложь И Найти(НРег(Значение), "digest") Тогда
		//Позиция = СтрНайти(НРег(Значение), "digest");
		Позиция = Найти(НРег(Значение), "digest");
	// Конец изменений Рогачев Алексей
		Значение = Сред(Значение, Позиция + СтрДлина("digest") + 1);
		Значение = СтрЗаменить(Значение, """", "");
		Значение = СтрЗаменить(Значение, Символы.ПС, "");
		
		ПараметрыDigest = Новый Структура("algorithm,realm,nonce,qop,opaque");
		Для Каждого Часть Из РазбитьСтрокуПоСтроке(Значение, ", ") Цикл
			КлючЗначение = СтрРазделить_Костыль(Часть, "=", Истина);
			ПараметрыDigest.Вставить(КлючЗначение[0], КлючЗначение[1]);
		КонецЦикла;
		
		Сессия.СлужебныеДанные.ПараметрыDigest = ПараметрыDigest;
		
		ПодготовленныйЗапрос.Заголовки.Вставить("Authorization", ПодготовитьЗаголовокDigest(Сессия, ПодготовленныйЗапрос));
		ПодготовленныйЗапрос.HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки;
		
		Ответ = ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки);
	КонецЕсли;
	
КонецПроцедуры

Функция ОпределитьХешФункцию(Знач Алгоритм)
	
	Алгоритм = ВРег(Алгоритм);
	Если Не ЗначениеЗаполнено(Алгоритм) ИЛИ Алгоритм = "MD5" ИЛИ Алгоритм = "MD5-SESS" Тогда
		Возврат ХешФункция.MD5;
	ИначеЕсли Алгоритм = "SHA" Тогда
		Возврат ХешФункция.SHA1;
	ИначеЕсли Алгоритм = "SHA-256" Тогда
		Возврат ХешФункция.SHA256;
	Иначе
		Возврат Неопределено;
	КонецЕсли;
	
КонецФункции

Функция ПодготовитьЗаголовокDigest(Сессия, ПодготовленныйЗапрос)
	
	ПараметрыDigest = Сессия.СлужебныеДанные.ПараметрыDigest;
	
	Алгоритм = ОпределитьХешФункцию(ПараметрыDigest.algorithm);
	АлгоритмСтрокой = ВРег(ПараметрыDigest.algorithm);
	Если Алгоритм = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
	Путь = СтруктураURL.Путь;
	Если ЗначениеЗаполнено(СтруктураURL.ПараметрыЗапроса) Тогда
		Путь = Путь + "?" + КодироватьПараметрыЗапроса(СтруктураURL.ПараметрыЗапроса);
	КонецЕсли;
	
	A1 = СтрШаблон_Костыль("%1:%2:%3", 
		ПодготовленныйЗапрос.Аутентификация.Пользователь,
		ПараметрыDigest.realm,
		ПодготовленныйЗапрос.Аутентификация.Пароль);
	A2 = СтрШаблон_Костыль("%1:%2", ПодготовленныйЗапрос.Метод, Путь);
	
	HA1 = ХешированиеДанных(Алгоритм, A1);
	HA2 = ХешированиеДанных(Алгоритм, A2);
	
	Если Не ПараметрыDigest.Свойство("last_nonce") Тогда
		ПараметрыDigest.Вставить("last_nonce");
	КонецЕсли;
	
	Если ПараметрыDigest.nonce = ПараметрыDigest.last_nonce Тогда
		ПараметрыDigest.nonce_count = ПараметрыDigest.nonce_count + 1;
	Иначе
		ПараметрыDigest.Вставить("nonce_count", 1);
	КонецЕсли;
	
	ЗначениеNC = Формат(ПараметрыDigest.nonce_count, "ЧЦ=8; ЧВН=; ЧГ=");
	ЗначениеNonce = Лев(СтрЗаменить(НРег(Новый УникальныйИдентификатор), "-", ""), 16);

	Если АлгоритмСтрокой = "MD5-SESS" Тогда
		HA1 = ХешированиеДанных(Алгоритм, СтрШаблон_Костыль("%1:%2:%3", HA1, ПараметрыDigest.nonce, ЗначениеNonce)); 
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ПараметрыDigest.qop) Тогда
		ЗначениеResponse = ХешированиеДанных(Алгоритм, СтрШаблон_Костыль("%1:%2:%3", HA1, ПараметрыDigest.nonce, HA2));
	ИначеЕсли ПараметрыDigest.qop = "auth"
		ИЛИ СтрРазделить_Костыль(ПараметрыDigest.qop, ",", Ложь).Найти("auth") <> Неопределено Тогда
		ЗначениеNonceBit = СтрШаблон_Костыль("%1:%2:%3:%4:%5", ПараметрыDigest.nonce, ЗначениеNC, ЗначениеNonce, "auth", HA2);
		ЗначениеResponse = ХешированиеДанных(Алгоритм, СтрШаблон_Костыль("%1:%2", HA1, ЗначениеNonceBit));
	Иначе
		// INFO: auth-int не реализовано
		Возврат Неопределено;
	КонецЕсли;
	
	ПараметрыDigest.last_nonce = ПараметрыDigest.nonce;

	База = СтрШаблон_Костыль("username=""%1"", realm=""%2"", nonce=""%3"", uri=""%4"", response=""%5""", 
		ПодготовленныйЗапрос.Аутентификация.Пользователь,
		ПараметрыDigest.realm,
		ПараметрыDigest.nonce,
		Путь,
		ЗначениеResponse);
	Строки = Новый Массив;
	Строки.Добавить(База);
		
	Если ЗначениеЗаполнено(ПараметрыDigest.opaque) Тогда
		Строки.Добавить(СтрШаблон_Костыль(", opaque=""%1""", ПараметрыDigest.opaque));
	КонецЕсли;
	Если ЗначениеЗаполнено(ПараметрыDigest.algorithm) Тогда
		Строки.Добавить(СтрШаблон_Костыль(", algorithm=""%1""", ПараметрыDigest.algorithm));
	КонецЕсли;
	Если ЗначениеЗаполнено(ПараметрыDigest.qop) Тогда
		Строки.Добавить(СтрШаблон_Костыль(", qop=""auth"", nc=%1, cnonce=""%2""", ЗначениеNC, ЗначениеNonce));
	КонецЕсли;
	
	// Начало изменений Рогачев Алексей
	//Возврат СтрШаблон("Digest %1", СтрСоединить(Строки, ""));
	Возврат СтрШаблон_Костыль("Digest %1", СтрСоединить_Костыль(Строки, ""));
	// Конец изменений Рогачев Алексей
	
КонецФункции

Функция ХешированиеДанных(Знач Алгоритм, Знач Данные)
	
	Если ТипЗнч(Данные) = Тип("Строка") Тогда
		Данные = ПолучитьДвоичныеДанныеИзСтроки(Данные, КодировкаТекста.UTF8, Ложь);
	КонецЕсли;
	
	Хеширование = Новый ХешированиеДанных(Алгоритм);
	Хеширование.Добавить(Данные);
	
	Возврат НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма));
	
КонецФункции

Функция РазбитьСтрокуПоСтроке(Знач Строка, Разделитель)
	
	Результат = Новый Массив;
	Пока Истина Цикл
		//Начало изменений Рогачев Алексей
		//Позиция = СтрНайти(Строка, Разделитель);
		Позиция = Найти(Строка, Разделитель);
		//Конец изменений Рогачев Алексей
		Если Позиция = 0 И ЗначениеЗаполнено(Строка) Тогда
			Результат.Добавить(Строка);
			Прервать;
		КонецЕсли;
		
		ПерваяЧасть = Лев(Строка, Позиция - СтрДлина(Разделитель) + 1);
		Результат.Добавить(ПерваяЧасть);
		Строка = Сред(Строка, Позиция + СтрДлина(Разделитель));
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

#КонецОбласти

Функция РаспаковатьОтвет(Ответ)
	
	Заголовок = ПолучитьЗначениеЗаголовка("content-encoding", Ответ.Заголовки);
	Если Заголовок <> Ложь Тогда
		Если НРег(Заголовок) = "gzip" Тогда
			Возврат ПрочитатьGZip(Ответ.Тело);
		КонецЕсли;
	КонецЕсли;
	
	Возврат Ответ.Тело;
	
КонецФункции

Процедура УпаковатьЗапрос(Запрос)

	Заголовок = ПолучитьЗначениеЗаголовка("content-encoding", Запрос.Заголовки);
	Если Заголовок <> Ложь Тогда
		Если НРег(Заголовок) = "gzip" Тогда
			Запрос.УстановитьТелоИзДвоичныхДанных(ЗаписатьGZip(Запрос.ПолучитьТелоКакДвоичныеДанные()));
		КонецЕсли;
	КонецЕсли;

КонецПроцедуры

#Область ПараметрыПоУмолчанию

Функция ЗаголовкиПоУмолчанию()
	
	Заголовки = Новый Соответствие;
#Если МобильноеПриложениеСервер Тогда
	Заголовки.Вставить("Accept-Encoding", "identity");
#Иначе
	Заголовки.Вставить("Accept-Encoding", "gzip");
#КонецЕсли
	Заголовки.Вставить("Accept", "*/*");
	Заголовки.Вставить("Connection", "keep-alive");
	
	Возврат Заголовки;
	
КонецФункции

Функция МаксимальноеКоличествоПеренаправлений()
	
	Возврат 30;
	
КонецФункции

Функция СтандартныйТаймаут()
	
	Возврат 30;
	
КонецФункции

Функция ПолучитьПараметрыПреобразованияJSONПоУмолчанию()
	
	ПараметрыПреобразованияПоУмолчанию = Новый Структура;
	ПараметрыПреобразованияПоУмолчанию.Вставить("ПрочитатьВСоответствие", Истина);
	ПараметрыПреобразованияПоУмолчанию.Вставить("ФорматДатыJSON", ФорматДатыJSON.ISO);
	ПараметрыПреобразованияПоУмолчанию.Вставить("ИменаСвойствСоЗначениямиДата", Неопределено);
	
	Возврат ПараметрыПреобразованияПоУмолчанию;
	
КонецФункции

Функция ПолучитьПараметрыЗаписиJSONПоУмолчанию()
	
	ПараметрыЗаписиJSONПоУмолчанию = Новый Структура;
	ПараметрыЗаписиJSONПоУмолчанию.Вставить("ПереносСтрок", ПереносСтрокJSON.Авто);
	ПараметрыЗаписиJSONПоУмолчанию.Вставить("СимволыОтступа", " ");
	ПараметрыЗаписиJSONПоУмолчанию.Вставить("ИспользоватьДвойныеКавычки", Истина);
	ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранированиеСимволов", ЭкранированиеСимволовJSON.Нет);
	ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранироватьУгловыеСкобки", Ложь);
	ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранироватьРазделителиСтрок", Истина);
	ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранироватьАмперсанд", Ложь);
	ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранироватьОдинарныеКавычки", Ложь);
	ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранироватьСлеш", Ложь);
	
	Возврат ПараметрыЗаписиJSONПоУмолчанию;
	
КонецФункции

#КонецОбласти

Процедура ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, ПараметрыЗапроса, Данные, Json)

	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = Новый Структура();
	КонецЕсли;
	Если Не ДополнительныеПараметры.Свойство("ПараметрыЗапроса") Тогда
		ДополнительныеПараметры.Вставить("ПараметрыЗапроса", ПараметрыЗапроса);
	КонецЕсли;
	Если Не ДополнительныеПараметры.Свойство("Данные") Тогда
		ДополнительныеПараметры.Вставить("Данные", Данные);
	КонецЕсли;
	Если Не ДополнительныеПараметры.Свойство("Json") Тогда
		ДополнительныеПараметры.Вставить("Json", Json);
	КонецЕсли;

КонецПроцедуры

// Когда-нибудь в платформе сделают паузу и это можно будет выкинуть
//
Процедура Приостановить(ДлительностьОстановкиВСекундах) Экспорт

	Если ДлительностьОстановкиВСекундах < 1 Тогда
		Возврат;
	КонецЕсли;
	
	ТекущаяДата = ТекущаяУниверсальнаяДата();
	ЖдатьДо = ТекущаяДата + ДлительностьОстановкиВСекундах;
	
	// BSLLS:UsingHardcodeNetworkAddress-off
	ЛокальныйХост = "127.0.0.0";
	КакойНибудьСвободныйПорт = 56476;
	// BSLLS:UsingHardcodeNetworkAddress-on
	Пока ТекущаяДата < ЖдатьДо Цикл
		Таймаут = ЖдатьДо - ТекущаяДата;
		Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
		Попытка
			Соединение = Новый HTTPСоединение(
				ЛокальныйХост, КакойНибудьСвободныйПорт, Неопределено, Неопределено, Неопределено, Таймаут);
			Соединение.Получить(Новый HTTPЗапрос("/does_not_matter"));
		Исключение
			РеальныйТаймаут = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало;
		КонецПопытки;
		МинимальныйТаймаутВМиллисекундах = 500;
		Если РеальныйТаймаут < МинимальныйТаймаутВМиллисекундах Тогда
			ВызватьИсключение(НСтр("ru = 'Процедура Приостановить не работает должным образом'"));
		КонецЕсли;
		ТекущаяДата = ТекущаяУниверсальнаяДата();
	КонецЦикла;
	
КонецПроцедуры

#КонецОбласти

#Область РогачевАлексей

//По причине старой конфигурации нужно написать замену СтрНайти() СтрСоединить() 
//СтрРазделить() СтрШаблон() СтрЗаканчиваетсяНа() СтрНачинаетсяС()

Функция СтрНайти_Костыль(ТекСтрока, ТекПодстрокаПоиска, НапрПоиска = 0, НачПоз = 1, НомВхождения = 1) Экспорт
	
	Если Не ЗначениеЗаполнено(ТекСтрока) ИЛИ Не ЗначениеЗаполнено(ТекПодстрокаПоиска) Тогда
		Возврат 1;
	КонецЕсли;
	
	ТекСтрока =  Сред(ТекСтрока, НачПоз);
	
	Если НапрПоиска = 0 Тогда
		Поз = Найти(ТекСтрока, ТекПодстрокаПоиска);
		Возврат Поз;
	Иначе
		ПозПоиска = 1;
		НакопительнаяПоз = 0;
		Для н = 1 По СтрЧислоВхождений(ТекСтрока, ТекПодстрокаПоиска) Цикл
			
			ПромежуточнаяСтр = Сред(ТекСтрока, ПозПоиска);
			ПозПоиска = Найти(ПромежуточнаяСтр, ТекПодстрокаПоиска);
			НакопительнаяПоз = НакопительнаяПоз + ПозПоиска;
			
		КонецЦикла;
		Возврат НакопительнаяПоз;
	КонецЕсли;
	
КонецФункции

Функция СтрСоединить_Костыль(ТекМассивСтрок, ТекРазделительСтрок) Экспорт
	
	Если Не ЗначениеЗаполнено(ТекМассивСтрок) Тогда
		Возврат "";
	КонецЕсли;
	
	ИтСтрока = "";
	КолЭл = 0;
	Для Каждого ТекЭл Из ТекМассивСтрок Цикл
		
		КолЭл = КолЭл + 1;
		Если КолЭл = ТекМассивСтрок.Количество() Тогда
			ИтСтрока = ИтСтрока + СокрЛП(ТекЭл);
		Иначе
			ИтСтрока = ИтСтрока + СокрЛП(ТекЭл) + ТекРазделительСтрок;
		КонецЕсли;
		
	КонецЦикла;
	Возврат ИтСтрока;
	
КонецФункции

Функция СтрРазделить_Костыль(ТекСтрока, ТекРазделительСтрок, ВклПустые) Экспорт
	
	ТекМассив = Новый Массив;
	ПромежуточнаяСтр = СокрЛП(ТекСтрока);
	Для н = 1 По СтрЧислоВхождений(ТекСтрока, ТекРазделительСтрок) Цикл
		
		ПозПоиска = Найти(ПромежуточнаяСтр, ТекРазделительСтрок);
		СтрокаДляМассива = Сред(ПромежуточнаяСтр, 1, ПозПоиска - 1);
		ПромежуточнаяСтр = Сред(ПромежуточнаяСтр, ПозПоиска + 1);
		Если ВклПустые = Ложь И НЕ ЗначениеЗаполнено(СтрокаДляМассива) Тогда
			Продолжить;
		Иначе	
			ТекМассив.Добавить(СокрЛП(СтрокаДляМассива));
		КонецЕсли;
			
	КонецЦикла;
	
	Если ВклПустые = Ложь И НЕ ЗначениеЗаполнено(ПромежуточнаяСтр) Тогда
		Возврат ТекМассив;
	Иначе	
		ТекМассив.Добавить(СокрЛП(ПромежуточнаяСтр));
	КонецЕсли;
	
	Возврат ТекМассив;
	
КонецФункции

Функция СтрШаблон_Костыль(Шаблон, п1 = "", п2 = "", п3 = "", п4 = "", п5 = "",
			п6 = "", п7 = "", п8 = "", п9 = "", п10 = "") Экспорт
	
	Результат = Шаблон;
	
	Для сч = 1 По СтрЧислоВхождений(Шаблон, "%") Цикл
		
		ИмяПараметра = "%" + СокрЛП(сч);
		
		Результат = СтрЗаменить(Результат, ИмяПараметра, Вычислить("п" + сч));
		
	КонецЦикла;
		
	Возврат Результат;
	
КонецФункции

Функция СтрЗаканчиваетсяНа_Костыль(ТекСтрока, ТекСтрокаПоиска) Экспорт
	
	Если Не ЗначениеЗаполнено(ТекСтрокаПоиска) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ТекПодстрока = Прав(ТекСтрока, СтрДлина(ТекСтрокаПоиска));
	Если Найти(ТекПодстрока, ТекСтрокаПоиска) > 0 Тогда
		Возврат Истина;
	Иначе
		Возврат Ложь;
	КонецЕсли;
	
КонецФункции

Функция СтрНачинаетсяС_Костыль(ТекСтрока, ТекСтрокаПоиска) Экспорт
	
	Если Не ЗначениеЗаполнено(ТекСтрокаПоиска) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ТекПодстрока = Лев(ТекСтрока, СтрДлина(ТекСтрокаПоиска));
	Если Найти(ТекПодстрока, ТекСтрокаПоиска) > 0 Тогда
		Возврат Истина;
	Иначе
		Возврат Ложь;
	КонецЕсли;
	
КонецФункции

#КонецОбласти

// https://github.com/NuclearAPK/Simple-Kafka_Adapter
#Область Кафка

// Функция - Создать компоненту кафка клиент
// 
// Возвращаемое значение:
//  ТипВнешнейКомпоненты.Native, Неопределено - Внешняя компонента
//
Функция СоздатьКомпонентуКафкаКлиент() Экспорт
	Если ПодключитьВнешнююКомпоненту("ОбщийМакет.SimpleKafkaAdapter64", "KafkaClient", ТипВнешнейКомпоненты.Native) Тогда
		
		Попытка
			Возврат Новый("AddIn.KafkaClient.simpleKafka1C");
		Исключение
			ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
			ЗаписьЖурналаРегистрации("Компонента из общего макета SimpleKafkaAdapter64 подключилась, но не создалась",
				УровеньЖурналаРегистрации.Ошибка, , ,
				ТекстОшибки);
			Возврат Неопределено;
		КонецПопытки;
	Иначе
		ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		ЗаписьЖурналаРегистрации("Не удалось подключить компоненту из общего макета SimpleKafkaAdapter64",
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
		Возврат Неопределено;
	КонецЕсли;
КонецФункции

// Процедура - Отправить в кафку
//
// Параметры:
//  СтруктураПодключения - Структура - полученная через
//           РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения("Наименование настройки")
//  Данные				 - Строка - строка в формате Json
//
Процедура ОтправитьВКафку(СтруктураПодключения, Данные) Экспорт
	
	Компонента = СоздатьКомпонентуКафкаКлиент();
	Если Компонента = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(Данные) <> Тип("Массив") Тогда
		Сообщения = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(Данные);
	Иначе
		Сообщения = Данные;
	КонецЕсли;
	
	//Если ТипЗнч(Параметры) = Тип("Соответствие") ИЛИ (ТипЗнч(Параметры) = Тип("Структура")) Тогда
	//	Для Каждого Параметр Из Параметры Цикл
	//		Компонента.УстановитьПараметр(Строка(Параметр.Ключ), Строка(Параметр.Значение));
	//	КонецЦикла;
	//КонецЕсли;
	
	Если СтруктураПодключения.Параметры.Свойство("КаталогЛогов")
		И ЗначениеЗаполнено(СтруктураПодключения.Параметры.КаталогЛогов) Тогда
		
		Компонента.КаталогЛогов = СтруктураПодключения.Параметры.КаталогЛогов;
	КонецЕсли;
	
	Брокеры = СтрЗаменить(СтруктураПодключения.Хост, "http://", "");
	Топик = СтруктураПодключения.ИмяТопика;
	// инициализируем подключение к брокеру
	// достаточно указать одного брокера из кластера, либо есть возможность перечислить брокеров через ,
	РезультатИнициализации = Компонента.ИнициализироватьПродюсера(Брокеры);
	//РезультатИнициализации = Компонента.ИнициализироватьПродюсера("89.248.195.244:50086,89.248.195.244:50087,89.248.195.244:50088");
	
	Если РезультатИнициализации Тогда
		
		Для Каждого СообщениеВКафку Из Сообщения Цикл
			// Parametr1 - Тело сообщения (строка)
			// Parametr2 - Топик (строка)
			// Parametr3 - Номер партиции, по умолчанию = -1 (число)
			// Parametr4 - Произвольный ключ, идентифицирующий сообщение, например GUID (строка)
			// Parametr5 - Заголовки (строка), "ключ1,значение1;ключ2,значение2"
			//ИДТранзакции = Строка(Новый УникальныйИдентификатор);
			//Заголовки = ""; //"key1,test1;key2,test2";
			РезультатОтправки = Компонента.ОтправитьСообщение(СообщениеВКафку, Топик, 0);

			Если Не РезультатОтправки Тогда
				ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаписьЖурналаРегистрации(Компонента.ПолучитьСообщениеОбОшибке(),
					УровеньЖурналаРегистрации.Ошибка, , ,
					ТекстОшибки);
			КонецЕсли;
		КонецЦикла;
		
		Компонента.ОстановитьПродюсера();
		Компонента = Неопределено;
		
	КонецЕсли;
	
КонецПроцедуры

// Процедуру запускает регламентное задание ЗапуститьЧитателяКафкиСсылкиНаОплату
Процедура ЗапуститьЧитателяКафкиСсылкиНаОплату() Экспорт
	
	Компонента = СоздатьКомпонентуКафкаКлиент();
	Если Компонента = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ИмяСервиса = "_КафкаЧтениеСсылокНаОплату";
	
	СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
	
	Компонента.УстановитьПараметр("group.id", СтруктураПодключения.Параметры.ГруппаПодписчика);
	Компонента.УстановитьПараметр("enable.auto.commit", "false");
	Компонента.УстановитьПараметр("enable.auto.offset.store", "false");
	Компонента.УстановитьПараметр("enable.partition.eof", "false");
	Если СтруктураПодключения.Параметры.Свойство("КаталогЛогов")
		И ЗначениеЗаполнено(СтруктураПодключения.Параметры.КаталогЛогов) Тогда
		
		Компонента.КаталогЛогов = СтруктураПодключения.Параметры.КаталогЛогов;
	КонецЕсли;
	
	Брокер = СтрЗаменить(СтруктураПодключения.Хост, "http://", "");
	Топики = СтруктураПодключения.ИмяТопика;
	Результат = Компонента.ИнициализироватьКонсьюмера(Брокер, Топики);
	Компонента.УстановитьТаймаутОжидания(5000); // установка таймаута для ожидания сообщений - 5 сек.
	
	Если Не Результат Тогда
		ТекстОшибки = СтрШаблон("Не удалось инициализировать консьюмера для топиков: %1", Топики);
		ЗаписьЖурналаРегистрации("Интеграция Kafka. Consumer",
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
		Возврат;
	КонецЕсли;
	
	Пока Истина Цикл
		СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
		Если НЕ СтруктураПодключения.Параметры.РазрешеноСлушать Тогда
			Прервать;
		КонецЕсли;
		Попытка
			ЕстьСообщение = Компонента.ПрочитатьСообщение();
			Если Не ЕстьСообщение Тогда
				Продолжить;
			КонецЕсли;
		Исключение
			Компонента = Неопределено;
			ВызватьИсключение ОписаниеОшибки();
		КонецПопытки;
		
		Сообщение = Компонента.ПолучитьДанныеСообщения();
		Если НЕ ЗначениеЗаполнено(Сообщение) Тогда
			ОписаниеОшибкиКомпонента = Компонента.ПолучитьСообщениеОбОшибке();
			Если ЗначениеЗаполнено(ОписаниеОшибкиКомпонента) Тогда
				ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаписьЖурналаРегистрации(ОписаниеОшибкиКомпонента,
					УровеньЖурналаРегистрации.Ошибка, , ,
					ТекстОшибки);
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		ОбъектЧтение = Новый ЧтениеJSON;
		ОбъектЧтение.УстановитьСтроку(Сообщение);
		СтруктураДанных = ПрочитатьJSON(ОбъектЧтение);
		ОбъектЧтение.Закрыть();
		
		Топик = Компонента.ПолучитьТопикСообщения();
		Смещение = Компонента.ПолучитьСмещениеСообщения();
		Партиция = Компонента.ПолучитьРазделСообщения();
		
		Если СтруктураДанных.Свойство("order_num") И ЗначениеЗаполнено(СтруктураДанных.order_num) Тогда
			ЗаписатьСсылкуНаОплатуВЗаказКлиента(СтруктураДанных);
		Иначе
			ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
			ЗаписьЖурналаРегистрации("Пустое значение order_num",
				УровеньЖурналаРегистрации.Ошибка, , ,
				ТекстОшибки);
		КонецЕсли;
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		Компонента.ЗафиксироватьСмещение(Топик, Смещение + 1, Партиция);
		
	КонецЦикла;
	
	Компонента.ОстановитьКонсьюмера();
	Компонента = Неопределено;
	
КонецПроцедуры

Процедура ЗаписатьСсылкуНаОплатуВЗаказКлиента(СтруктураДанных)
	
	ДокСсылка = Документы.ЗаказДляКлиента.НайтиПоНомеру(СтруктураДанных.order_num);
	Если Не ДокСсылка.Пустая() Тогда
		ДокОбъект = ДокСсылка.ПолучитьОбъект();
		НоваяСтрокаПлатежи = ДокОбъект.Платежи.Добавить();
		НоваяСтрокаПлатежи.Дата = ТекущаяДатаСеанса();
		Если СтруктураДанных.response.status = "error" Тогда
			НоваяСтрокаПлатежи.СсылкаНаПлатеж = СтруктураДанных.response.message;
		Иначе
			НоваяСтрокаПлатежи.СсылкаНаПлатеж = СтруктураДанных.response.link;
		КонецЕсли;
		НоваяСтрокаПлатежи.Сумма = СтруктураДанных.sum;
		Если СтруктураДанных.type = "partial" Тогда
			Признак = "Предоплата";
		ИначеЕсли СтруктураДанных.type = "full" Тогда
			Признак = "ПолнаяПредоплата";
		ИначеЕсли СтруктураДанных.type = "surcharge" Тогда
			Признак = "Доплата";
		Иначе
			Признак = "Не определен";
		КонецЕсли;
		НоваяСтрокаПлатежи.Признак = Признак;
		ДокОбъект.Записать();
	Иначе
		ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		ЗаписьЖурналаРегистрации("Не найден документ с номером " + СтруктураДанных.order_num,
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
	КонецЕсли;

КонецПроцедуры

Процедура ЗапуститьЧитателяКафкиПланыДашборд() Экспорт
	
	Компонента = СоздатьКомпонентуКафкаКлиент();
	Если Компонента = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ИмяСервиса = "_КафкаЧтениеПлановДашборд";
	
	СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
	
	Компонента.УстановитьПараметр("group.id", СтруктураПодключения.Параметры.ГруппаПодписчика);
	Компонента.УстановитьПараметр("enable.auto.commit", "false");
	Компонента.УстановитьПараметр("enable.auto.offset.store", "false");
	Компонента.УстановитьПараметр("enable.partition.eof", "false");
	//Компонента.УстановитьПараметр("statistics.interval.ms", 15000);
	Если СтруктураПодключения.Параметры.Свойство("КаталогЛогов")
		И ЗначениеЗаполнено(СтруктураПодключения.Параметры.КаталогЛогов) Тогда
		
		Компонента.КаталогЛогов = СтруктураПодключения.Параметры.КаталогЛогов;
	КонецЕсли;
	
	Брокер = СтрЗаменить(СтруктураПодключения.Хост, "http://", "");
	//Топики = СтруктураПодключения.Параметры.ИмяТопика;
	Топики = СтруктураПодключения.ИмяТопика;
	
	Результат = Компонента.ИнициализироватьКонсьюмера(Брокер, Топики);
	
	// установка таймаута для ожидания сообщений - 5 сек. 
	Компонента.УстановитьТаймаутОжидания(5000);
	
	Если Не Результат Тогда
		ТекстОшибки = СтрШаблон("Не удалось инициализировать консьюмера для топиков: %1", Топики);
		ЗаписьЖурналаРегистрации("Интеграция Kafka. Consumer",
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
		Возврат;
	КонецЕсли;
	
	Пока Истина Цикл
		СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
		Если НЕ СтруктураПодключения.Параметры.РазрешеноСлушать Тогда
			Прервать;
		КонецЕсли;
		
		Попытка
			Сообщение = Компонента.Слушать();
		Исключение
			Компонента = Неопределено;
			ВызватьИсключение ОписаниеОшибки();
		КонецПопытки;
		
		Если НЕ ЗначениеЗаполнено(Сообщение) Тогда
			ОписаниеОшибкиКомпонента = Компонента.ПолучитьСообщениеОбОшибке();
			Если ЗначениеЗаполнено(ОписаниеОшибкиКомпонента) Тогда
				ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаписьЖурналаРегистрации(ОписаниеОшибкиКомпонента,
					УровеньЖурналаРегистрации.Ошибка, , ,
					ТекстОшибки);
				//Компонента = Неопределено;
				//Возврат;
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		ОбъектЧтение = Новый ЧтениеJSON;
		ОбъектЧтение.УстановитьСтроку(Сообщение);
		СтруктураJSON = ПрочитатьJSON(ОбъектЧтение);
		ОбъектЧтение.Закрыть();
		
		// ВременнаяМетка = Число(СтруктураJSON.timestamp);
		Смещение = Число(СтруктураJSON.offset);
		Партиция = Число(СтруктураJSON.partition);
		
		Чтение = Новый ЧтениеJSON;
		Чтение.УстановитьСтроку(СтруктураJSON.message);
		СтруктураДанных = ПрочитатьJSON(Чтение);
		
		кор_ДашбордыОбменКафка.СоздатьДокументПланаИзСтруктуры(СтруктураДанных);
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		Компонента.ЗафиксироватьСмещение(СтруктураJSON.topic, Смещение + 1, Партиция);
		
	КонецЦикла;
	
	Компонента.ОстановитьКонсьюмера();
	Компонента = Неопределено;
	
КонецПроцедуры

Процедура ЗапуститьЧитателяКафкиЗН() Экспорт
	//++НП / 29.03.2024 / № 45716 Дашборд
	Если НЕ Константы.НоваяМотивацияВключена.Получить() Тогда
		Возврат;
	КонецЕсли;	
	
	Компонента = СоздатьКомпонентуКафкаКлиент();
	Если Компонента = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ИмяСервиса = "_КафкаЧтениеЗНДашборд";
	
	СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
	//СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения_2("_ЧтениеКафка");
	
	Компонента.УстановитьПараметр("group.id", СтруктураПодключения.Параметры.ГруппаПодписчика);
	Компонента.УстановитьПараметр("enable.auto.commit", "false");
	Компонента.УстановитьПараметр("enable.auto.offset.store", "false");
	Компонента.УстановитьПараметр("enable.partition.eof", "false");
	//Компонента.УстановитьПараметр("statistics.interval.ms", 15000);
	Если СтруктураПодключения.Параметры.Свойство("КаталогЛогов")
		И ЗначениеЗаполнено(СтруктураПодключения.Параметры.КаталогЛогов) Тогда
		
		Компонента.КаталогЛогов = СтруктураПодключения.Параметры.КаталогЛогов;
	КонецЕсли;
	
	Брокер = СтрЗаменить(СтруктураПодключения.Хост, "http://", "");
	Топики = СтруктураПодключения.ИмяТопика;
	
	//Компонента.УстановитьПозициюЧтения("abcp-to-1c-dev", 5,0);
	//Если корПолучитьЗначениеРеквизита("_ЧитаемЗаказыСКафки", ПланыВидовХарактеристик.ДопРеквизиты.Настройка) = Истина Тогда
		Компонента.УстановитьПозициюЧтения(Топики, Цел(СтруктураПодключения.КафкаОффсет), 0);
	//КонецЕсли;
	
	Результат = Компонента.ИнициализироватьКонсьюмера(Брокер, Топики);
	
	// установка таймаута для ожидания сообщений - 5 сек. 
	Компонента.УстановитьТаймаутОжидания(5000);
	
	Если Не Результат Тогда
		ТекстОшибки = СтрШаблон("Не удалось инициализировать консьюмера для топиков: %1", Топики);
		ЗаписьЖурналаРегистрации("Интеграция Kafka. Consumer",
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
		Возврат;
	КонецЕсли;
	
	Пока Истина Цикл
		СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
		Если НЕ СтруктураПодключения.Параметры.РазрешеноСлушать Тогда
			Прервать;
		КонецЕсли;
		Попытка
			ЕстьСообщение = Компонента.ПрочитатьСообщение();
			Если Не ЕстьСообщение Тогда
				Продолжить;
			КонецЕсли;
		Исключение
			Компонента = Неопределено;
			ВызватьИсключение ОписаниеОшибки();
		КонецПопытки;
		
		Сообщение = Компонента.ПолучитьДанныеСообщения();
		Если НЕ ЗначениеЗаполнено(Сообщение) Тогда
			ОписаниеОшибкиКомпонента = Компонента.ПолучитьСообщениеОбОшибке();
			Если ЗначениеЗаполнено(ОписаниеОшибкиКомпонента) Тогда
				ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаписьЖурналаРегистрации(ОписаниеОшибкиКомпонента,
					УровеньЖурналаРегистрации.Ошибка, , ,
					ТекстОшибки);
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		ОбъектЧтение = Новый ЧтениеJSON;
		ОбъектЧтение.УстановитьСтроку(Сообщение);
		СтруктураДанных = ПрочитатьJSON(ОбъектЧтение);
		ОбъектЧтение.Закрыть();
		
		Топик = Компонента.ПолучитьТопикСообщения();
		Смещение = Компонента.ПолучитьСмещениеСообщения();
		Партиция = Компонента.ПолучитьРазделСообщения();
		
		кор_ДашбордыОбменКафка.СоздатьЗаказ(СтруктураДанных); 
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		//Компонента.ЗафиксироватьСмещение(СтруктураJSON.topic, Смещение + 1, Партиция);
		РеквизитыОбъекта = РегистрыСведений.ДопРеквизиты.СоздатьНаборЗаписей();
		РеквизитыОбъекта.Отбор.ТипОбъекта.Установить(ИмяСервиса);
		РеквизитыОбъекта.Отбор.Реквизит.Установить(ПланыВидовХарактеристик.ДопРеквизиты.КафкаОффсет);
		РеквизитыОбъекта.Прочитать();
		Если РеквизитыОбъекта.Количество() > 0 Тогда
			РеквизитыОбъекта[0].Значение = Смещение + 1;
			РеквизитыОбъекта.Записать();
		КонецЕсли;
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		Компонента.ЗафиксироватьСмещение(Топик, Смещение + 1, Партиция);
		
	КонецЦикла;
	
	Компонента.ОстановитьКонсьюмера();
	Компонента = Неопределено;

КонецПроцедуры

Процедура ЗапуститьЧитателяКафкиПолучитьОплатуДашборд() Экспорт
	//++НП / 29.03.2024 / № 45716 Дашборд
	Если НЕ Константы.НоваяМотивацияВключена.Получить() Тогда
		Возврат;
	КонецЕсли;	
	
	Компонента = СоздатьКомпонентуКафкаКлиент();
	Если Компонента = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ИмяСервиса = "_КафкаЧтениеЗНПолучитьОплатуДашборд";
	
	СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
	//СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения_2("_ЧтениеКафка");
	
	Компонента.УстановитьПараметр("group.id", СтруктураПодключения.Параметры.ГруппаПодписчика);
	Компонента.УстановитьПараметр("enable.auto.commit", "false");
	Компонента.УстановитьПараметр("enable.auto.offset.store", "false");
	Компонента.УстановитьПараметр("enable.partition.eof", "false");
	//Компонента.УстановитьПараметр("statistics.interval.ms", 15000);
	Если СтруктураПодключения.Параметры.Свойство("КаталогЛогов")
		И ЗначениеЗаполнено(СтруктураПодключения.Параметры.КаталогЛогов) Тогда
		
		Компонента.КаталогЛогов = СтруктураПодключения.Параметры.КаталогЛогов;
	КонецЕсли;
	
	Брокер = СтрЗаменить(СтруктураПодключения.Хост, "http://", "");
	Топики = СтруктураПодключения.ИмяТопика;
	
	//Компонента.УстановитьПозициюЧтения("abcp-to-1c-dev", 5,0);
	//Если корПолучитьЗначениеРеквизита("_ЧитаемЗаказыСКафки", ПланыВидовХарактеристик.ДопРеквизиты.Настройка) = Истина Тогда
		Компонента.УстановитьПозициюЧтения(Топики, Цел(СтруктураПодключения.КафкаОффсет), 0);
	//КонецЕсли;
	
	Результат = Компонента.ИнициализироватьКонсьюмера(Брокер, Топики);
	
	// установка таймаута для ожидания сообщений - 5 сек. 
	Компонента.УстановитьТаймаутОжидания(5000);
	
	Если Не Результат Тогда
		ТекстОшибки = СтрШаблон("Не удалось инициализировать консьюмера для топиков: %1", Топики);
		ЗаписьЖурналаРегистрации("Интеграция Kafka. Consumer",
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
		Возврат;
	КонецЕсли;
	
	Пока Истина Цикл
		
		Попытка
			ЕстьСообщение = Компонента.ПрочитатьСообщение();
			Если Не ЕстьСообщение Тогда
				Продолжить;
			КонецЕсли;
		Исключение
			Компонента = Неопределено;
			ВызватьИсключение ОписаниеОшибки();
		КонецПопытки;
		
		Сообщение = Компонента.ПолучитьДанныеСообщения();
		Если НЕ ЗначениеЗаполнено(Сообщение) Тогда
			ОписаниеОшибкиКомпонента = Компонента.ПолучитьСообщениеОбОшибке();
			Если ЗначениеЗаполнено(ОписаниеОшибкиКомпонента) Тогда
				ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаписьЖурналаРегистрации(ОписаниеОшибкиКомпонента,
					УровеньЖурналаРегистрации.Ошибка, , ,
					ТекстОшибки);
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		ОбъектЧтение = Новый ЧтениеJSON;
		ОбъектЧтение.УстановитьСтроку(Сообщение);
		СтруктураДанных = ПрочитатьJSON(ОбъектЧтение);
		ОбъектЧтение.Закрыть();
		
		Топик = Компонента.ПолучитьТопикСообщения();
		Смещение = Компонента.ПолучитьСмещениеСообщения();
		Партиция = Компонента.ПолучитьРазделСообщения();
		
		кор_ДашбордыОбменКафка.ПолучитьОплатуПоЗН(СтруктураДанных);  
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		//Компонента.ЗафиксироватьСмещение(СтруктураJSON.topic, Смещение + 1, Партиция);
		РеквизитыОбъекта = РегистрыСведений.ДопРеквизиты.СоздатьНаборЗаписей();
		РеквизитыОбъекта.Отбор.ТипОбъекта.Установить(ИмяСервиса);
		РеквизитыОбъекта.Отбор.Реквизит.Установить(ПланыВидовХарактеристик.ДопРеквизиты.КафкаОффсет);
		РеквизитыОбъекта.Прочитать();
		Если РеквизитыОбъекта.Количество() > 0 Тогда
			РеквизитыОбъекта[0].Значение = Смещение + 1;
			РеквизитыОбъекта.Записать();
		КонецЕсли;
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		Компонента.ЗафиксироватьСмещение(Топик, Смещение + 1, Партиция);
		
	КонецЦикла;
	
	Компонента.ОстановитьКонсьюмера();
	Компонента = Неопределено;

КонецПроцедуры

Процедура ЗапуститьЧитателяКафкиДатаОтчетаПоПродажам() Экспорт
	//++НП / Дашборд директора
	Если НЕ Константы.НоваяМотивацияВключена.Получить() Тогда
		Возврат;
	КонецЕсли;	
	
	Компонента = СоздатьКомпонентуКафкаКлиент();
	Если Компонента = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ИмяСервиса = "_КафкаЧтениеДатыДляОтчетаПоПродажам";
	
	СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
	//СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения_2("_ЧтениеКафка");
	
	Компонента.УстановитьПараметр("group.id", СтруктураПодключения.Параметры.ГруппаПодписчика);
	Компонента.УстановитьПараметр("enable.auto.commit", "false");
	Компонента.УстановитьПараметр("enable.auto.offset.store", "false");
	Компонента.УстановитьПараметр("enable.partition.eof", "false");
	//Компонента.УстановитьПараметр("statistics.interval.ms", 15000);
	Если СтруктураПодключения.Параметры.Свойство("КаталогЛогов")
		И ЗначениеЗаполнено(СтруктураПодключения.Параметры.КаталогЛогов) Тогда
		
		Компонента.КаталогЛогов = СтруктураПодключения.Параметры.КаталогЛогов;
	КонецЕсли;
	
	Брокер = СтрЗаменить(СтруктураПодключения.Хост, "http://", "");
	Топики = СтруктураПодключения.ИмяТопика;
	
	//Компонента.УстановитьПозициюЧтения("abcp-to-1c-dev", 5,0);
	//Если корПолучитьЗначениеРеквизита("_ЧитаемЗаказыСКафки", ПланыВидовХарактеристик.ДопРеквизиты.Настройка) = Истина Тогда
		Компонента.УстановитьПозициюЧтения(Топики, Цел(СтруктураПодключения.КафкаОффсет), 0);
	//КонецЕсли;
	
	Результат = Компонента.ИнициализироватьКонсьюмера(Брокер, Топики);
	
	// установка таймаута для ожидания сообщений - 5 сек. 
	Компонента.УстановитьТаймаутОжидания(5000);
	
	Если Не Результат Тогда
		ТекстОшибки = СтрШаблон("Не удалось инициализировать консьюмера для топиков: %1", Топики);
		ЗаписьЖурналаРегистрации("Интеграция Kafka. Consumer",
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
		Возврат;
	КонецЕсли;
	
	Пока Истина Цикл
		
		Попытка
			ЕстьСообщение = Компонента.ПрочитатьСообщение();
			Если Не ЕстьСообщение Тогда
				Продолжить;
			КонецЕсли;
		Исключение
			Компонента = Неопределено;
			ВызватьИсключение ОписаниеОшибки();
		КонецПопытки;
		
		Сообщение = Компонента.ПолучитьДанныеСообщения();
		Если НЕ ЗначениеЗаполнено(Сообщение) Тогда
			ОписаниеОшибкиКомпонента = Компонента.ПолучитьСообщениеОбОшибке();
			Если ЗначениеЗаполнено(ОписаниеОшибкиКомпонента) Тогда
				ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаписьЖурналаРегистрации(ОписаниеОшибкиКомпонента,
					УровеньЖурналаРегистрации.Ошибка, , ,
					ТекстОшибки);
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		ОбъектЧтение = Новый ЧтениеJSON;
		ОбъектЧтение.УстановитьСтроку(Сообщение);
		СтруктураДанных = ПрочитатьJSON(ОбъектЧтение);
		ОбъектЧтение.Закрыть();
		
		Топик = Компонента.ПолучитьТопикСообщения();
		Смещение = Компонента.ПолучитьСмещениеСообщения();
		Партиция = Компонента.ПолучитьРазделСообщения();
		
		кор_ДашбордыОбменКафка.ПолучитьДатуДляФормированияОтчета(СтруктураДанных);
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		//Компонента.ЗафиксироватьСмещение(СтруктураJSON.topic, Смещение + 1, Партиция);
		РеквизитыОбъекта = РегистрыСведений.ДопРеквизиты.СоздатьНаборЗаписей();
		РеквизитыОбъекта.Отбор.ТипОбъекта.Установить(ИмяСервиса);
		РеквизитыОбъекта.Отбор.Реквизит.Установить(ПланыВидовХарактеристик.ДопРеквизиты.КафкаОффсет);
		РеквизитыОбъекта.Прочитать();
		Если РеквизитыОбъекта.Количество() > 0 Тогда
			РеквизитыОбъекта[0].Значение = Смещение + 1;
			РеквизитыОбъекта.Записать();
		КонецЕсли;
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		Компонента.ЗафиксироватьСмещение(Топик, Смещение + 1, Партиция);
		
	КонецЦикла;
	
	Компонента.ОстановитьКонсьюмера();
	Компонента = Неопределено;

КонецПроцедуры
//rialist+++
Процедура ЗапуститьЧитателяКафкиПолучитьСтатусОплатыЗК() Экспорт
	//++НП / 29.03.2024 / № 45716 Дашборд
	Если НЕ Константы.ВключенИнвестПроект2.Получить() Тогда
		Возврат;
	КонецЕсли;	
	
	Компонента = СоздатьКомпонентуКафкаКлиент();
	Если Компонента = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ИмяСервиса = "_КафкаЧтениеЗНПолучитьСтатусОплаты";
	
	СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
	//СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения_2("_ЧтениеКафка");
	
	Компонента.УстановитьПараметр("group.id", СтруктураПодключения.Параметры.ГруппаПодписчика);
	Компонента.УстановитьПараметр("enable.auto.commit", "false");
	Компонента.УстановитьПараметр("enable.auto.offset.store", "false");
	Компонента.УстановитьПараметр("enable.partition.eof", "false");
	//Компонента.УстановитьПараметр("statistics.interval.ms", 15000);
	Если СтруктураПодключения.Параметры.Свойство("КаталогЛогов")
		И ЗначениеЗаполнено(СтруктураПодключения.Параметры.КаталогЛогов) Тогда
		
		Компонента.КаталогЛогов = СтруктураПодключения.Параметры.КаталогЛогов;
	КонецЕсли;
	
	Брокер = СтрЗаменить(СтруктураПодключения.Хост, "http://", "");
	Топики = СтруктураПодключения.ИмяТопика;
	
	//Компонента.УстановитьПозициюЧтения("abcp-to-1c-dev", 5,0);
	//Если корПолучитьЗначениеРеквизита("_ЧитаемЗаказыСКафки", ПланыВидовХарактеристик.ДопРеквизиты.Настройка) = Истина Тогда
		Компонента.УстановитьПозициюЧтения(Топики, Цел(СтруктураПодключения.КафкаОффсет), 0);
	//КонецЕсли;
	
	Результат = Компонента.ИнициализироватьКонсьюмера(Брокер, Топики);
	
	// установка таймаута для ожидания сообщений - 5 сек. 
	Компонента.УстановитьТаймаутОжидания(5000);
	
	Если Не Результат Тогда
		ТекстОшибки = СтрШаблон("Не удалось инициализировать консьюмера для топиков: %1", Топики);
		ЗаписьЖурналаРегистрации("Интеграция Kafka. Consumer",
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
		Возврат;
	КонецЕсли;
	
	Пока Истина Цикл
		
		Попытка
			ЕстьСообщение = Компонента.ПрочитатьСообщение();
			Если Не ЕстьСообщение Тогда
				Продолжить;
			КонецЕсли;
		Исключение
			Компонента = Неопределено;
			ВызватьИсключение ОписаниеОшибки();
		КонецПопытки;
		
		Сообщение = Компонента.ПолучитьДанныеСообщения();
		Если НЕ ЗначениеЗаполнено(Сообщение) Тогда
			ОписаниеОшибкиКомпонента = Компонента.ПолучитьСообщениеОбОшибке();
			Если ЗначениеЗаполнено(ОписаниеОшибкиКомпонента) Тогда
				ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаписьЖурналаРегистрации(ОписаниеОшибкиКомпонента,
					УровеньЖурналаРегистрации.Ошибка, , ,
					ТекстОшибки);
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		ОбъектЧтение = Новый ЧтениеJSON;
		ОбъектЧтение.УстановитьСтроку(Сообщение);
		СтруктураДанных = ПрочитатьJSON(ОбъектЧтение);
		ОбъектЧтение.Закрыть();
		
		Топик = Компонента.ПолучитьТопикСообщения();
		Смещение = Компонента.ПолучитьСмещениеСообщения();
		Партиция = Компонента.ПолучитьРазделСообщения();
		
		кор_ДашбордыОбменКафка.ПолучитьСтатусОплатыПоЗК(СтруктураДанных); 
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		//Компонента.ЗафиксироватьСмещение(СтруктураJSON.topic, Смещение + 1, Партиция);
		РеквизитыОбъекта = РегистрыСведений.ДопРеквизиты.СоздатьНаборЗаписей();
		РеквизитыОбъекта.Отбор.ТипОбъекта.Установить(ИмяСервиса);
		РеквизитыОбъекта.Отбор.Реквизит.Установить(ПланыВидовХарактеристик.ДопРеквизиты.КафкаОффсет);
		РеквизитыОбъекта.Прочитать();
		Если РеквизитыОбъекта.Количество() > 0 Тогда
			РеквизитыОбъекта[0].Значение = Смещение + 1;
			РеквизитыОбъекта.Записать();
		КонецЕсли;
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		Компонента.ЗафиксироватьСмещение(Топик, Смещение + 1, Партиция);
		
	КонецЦикла;
	
	Компонента.ОстановитьКонсьюмера();
	Компонента = Неопределено;

КонецПроцедуры
//---rialist
//Спиридонов АИ 45970
Процедура ЗапуститьЧитателяКафкиЗаказыABCP() Экспорт
	
	Компонента = СоздатьКомпонентуКафкаКлиент();
	Если Компонента = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ИмяСервиса = "_ЧитаемЗаказыСКафки";
	
	СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
	//СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения_2("_ЧтениеКафка");
	
	Компонента.УстановитьПараметр("group.id", СтруктураПодключения.Параметры.ГруппаПодписчика);
	Компонента.УстановитьПараметр("enable.auto.commit", "false");
	Компонента.УстановитьПараметр("enable.auto.offset.store", "false");
	Компонента.УстановитьПараметр("enable.partition.eof", "false");
	//Компонента.УстановитьПараметр("statistics.interval.ms", 15000);
	Если СтруктураПодключения.Параметры.Свойство("КаталогЛогов")
		И ЗначениеЗаполнено(СтруктураПодключения.Параметры.КаталогЛогов) Тогда
		
		Компонента.КаталогЛогов = СтруктураПодключения.Параметры.КаталогЛогов;
	КонецЕсли;
	
	Брокер = СтрЗаменить(СтруктураПодключения.Хост, "http://", "");
	Топики = СтруктураПодключения.ИмяТопика;
	
	//Компонента.УстановитьПозициюЧтения("abcp-to-1c-dev", 5,0);
	//Если корПолучитьЗначениеРеквизита("_ЧитаемЗаказыСКафки", ПланыВидовХарактеристик.ДопРеквизиты.Настройка) = Истина Тогда
		Компонента.УстановитьПозициюЧтения(Топики, Цел(СтруктураПодключения.КафкаОффсет), 0);
	//КонецЕсли;
	
	Результат = Компонента.ИнициализироватьКонсьюмера(Брокер, Топики);
	
	// установка таймаута для ожидания сообщений - 5 сек. 
	Компонента.УстановитьТаймаутОжидания(5000);
	
	Если Не Результат Тогда
		ТекстОшибки = СтрШаблон("Не удалось инициализировать консьюмера для топиков: %1", Топики);
		ЗаписьЖурналаРегистрации("Интеграция Kafka. Consumer",
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
		Возврат;
	КонецЕсли;
	
	Пока Истина Цикл
		
		Попытка
			ЕстьСообщение = Компонента.ПрочитатьСообщение();
			Если Не ЕстьСообщение Тогда
				Продолжить;
			КонецЕсли;
		Исключение
			Компонента = Неопределено;
			ВызватьИсключение ОписаниеОшибки();
		КонецПопытки;
		
		Сообщение = Компонента.ПолучитьДанныеСообщения();
		Если НЕ ЗначениеЗаполнено(Сообщение) Тогда
			ОписаниеОшибкиКомпонента = Компонента.ПолучитьСообщениеОбОшибке();
			Если ЗначениеЗаполнено(ОписаниеОшибкиКомпонента) Тогда
				ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаписьЖурналаРегистрации(ОписаниеОшибкиКомпонента,
					УровеньЖурналаРегистрации.Ошибка, , ,
					ТекстОшибки);
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		ОбъектЧтение = Новый ЧтениеJSON;
		ОбъектЧтение.УстановитьСтроку(Сообщение);
		СтруктураДанных = ПрочитатьJSON(ОбъектЧтение);
		ОбъектЧтение.Закрыть();
		
		Топик = Компонента.ПолучитьТопикСообщения();
		Смещение = Компонента.ПолучитьСмещениеСообщения();
		Партиция = Компонента.ПолучитьРазделСообщения();
		
		Если СтруктураДанных.Свойство("number") И ЗначениеЗаполнено(СтруктураДанных.number) Тогда
			Попытка
				рсОбменССайтомABCP.ВыполнитьОбменЗаказамиИзКафки(СтруктураДанных, Смещение);
			Исключение
				рсОбменССайтомABCP.СообщитьОПроблемеЗаписиЗаказаСАВСР(СтруктураДанных.number, Строка(ОписаниеОшибки()));
			КонецПопытки;
		Иначе
			ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
			ЗаписьЖурналаРегистрации("Пустое значение number",
				УровеньЖурналаРегистрации.Ошибка, , ,
				ТекстОшибки);
		КонецЕсли;
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		//Компонента.ЗафиксироватьСмещение(СтруктураJSON.topic, Смещение + 1, Партиция);
		РеквизитыОбъекта = РегистрыСведений.ДопРеквизиты.СоздатьНаборЗаписей();
		РеквизитыОбъекта.Отбор.ТипОбъекта.Установить(ИмяСервиса);
		РеквизитыОбъекта.Отбор.Реквизит.Установить(ПланыВидовХарактеристик.ДопРеквизиты.КафкаОффсет);
		РеквизитыОбъекта.Прочитать();
		Если РеквизитыОбъекта.Количество() > 0 Тогда
			РеквизитыОбъекта[0].Значение = Смещение + 1;
			РеквизитыОбъекта.Записать();
		КонецЕсли;
		
		// вручную фиксируем смещения
		// !!! второй параметр - смещение, должно быть на 1 больше текушего прочитанного смещения
		Компонента.ЗафиксироватьСмещение(Топик, Смещение + 1, Партиция);
		
	КонецЦикла;
	
	Компонента.ОстановитьКонсьюмера();
	Компонента = Неопределено;
КонецПроцедуры

Процедура ЗапуститьЧитателяКафкиВерификацииДисконтныхКарт() Экспорт
	
	Компонента = СоздатьКомпонентуКафкаКлиент();
	Если Компонента = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ИмяСервиса = "_КафкаЧтениеВерификацииДисконтныхКарт";
	
	СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
	
	Компонента.УстановитьПараметр("group.id", СтруктураПодключения.Параметры.ГруппаПодписчика);
	Компонента.УстановитьПараметр("enable.auto.commit", "false");
	Компонента.УстановитьПараметр("enable.auto.offset.store", "false");
	Компонента.УстановитьПараметр("enable.partition.eof", "false");
	//Компонента.УстановитьПараметр("statistics.interval.ms", 15000);
	Если СтруктураПодключения.Параметры.Свойство("КаталогЛогов")
		И ЗначениеЗаполнено(СтруктураПодключения.Параметры.КаталогЛогов) Тогда
		
		Компонента.КаталогЛогов = СтруктураПодключения.Параметры.КаталогЛогов;
	КонецЕсли;
	
	Брокер = СтрЗаменить(СтруктураПодключения.Хост, "http://", "");

	Топики = СтруктураПодключения.ИмяТопика;
	
	Компонента.УстановитьПозициюЧтения(Топики, Цел(СтруктураПодключения.КафкаОффсет), 0);

	Результат = Компонента.ИнициализироватьКонсьюмера(Брокер, Топики);
	
	// установка таймаута для ожидания сообщений - 5 сек. 
	Компонента.УстановитьТаймаутОжидания(5000);
	
	Если Не Результат Тогда
		ТекстОшибки = СтрШаблон("Не удалось инициализировать консьюмера для топиков: %1", Топики);
		ЗаписьЖурналаРегистрации("Интеграция Kafka. Consumer",
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
		Возврат;
	КонецЕсли;
	
	Пока Истина Цикл
		
		Попытка
			Сообщение = Компонента.Слушать();
		Исключение
			Компонента = Неопределено;
			ВызватьИсключение ОписаниеОшибки();
		КонецПопытки;
		
		Если НЕ ЗначениеЗаполнено(Сообщение) Тогда
			ОписаниеОшибкиКомпонента = Компонента.ПолучитьСообщениеОбОшибке();
			Если ЗначениеЗаполнено(ОписаниеОшибкиКомпонента) Тогда
				ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаписьЖурналаРегистрации(ОписаниеОшибкиКомпонента,
					УровеньЖурналаРегистрации.Ошибка, , ,
					ТекстОшибки);
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		ОбъектЧтение = Новый ЧтениеJSON;
		ОбъектЧтение.УстановитьСтроку(Сообщение);
		СтруктураJSON = ПрочитатьJSON(ОбъектЧтение);
		ОбъектЧтение.Закрыть();
		
		// ВременнаяМетка = Число(СтруктураJSON.timestamp);
		Смещение = Число(СтруктураJSON.offset);
		Партиция = Число(СтруктураJSON.partition);
		
		Чтение = Новый ЧтениеJSON;
		Чтение.УстановитьСтроку(СтруктураJSON.message);
		СтруктураДанных = ПрочитатьJSON(Чтение);
		
		Если СтруктураДанных.Свойство("code_id") И ЗначениеЗаполнено(СтруктураДанных.code_id) Тогда
			ЗаписатьВерификацииДисконтныхКарт(СтруктураДанных);
		Иначе
			ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
			ЗаписьЖурналаРегистрации("Пустое значение code_id",
				УровеньЖурналаРегистрации.Ошибка, , ,
				ТекстОшибки);
		КонецЕсли;   	
			
		// вручную фиксируем смещения
		РеквизитыОбъекта = РегистрыСведений.ДопРеквизиты.СоздатьНаборЗаписей();
		РеквизитыОбъекта.Отбор.ТипОбъекта.Установить(ИмяСервиса);
		РеквизитыОбъекта.Отбор.Реквизит.Установить(ПланыВидовХарактеристик.ДопРеквизиты.КафкаОффсет);
		РеквизитыОбъекта.Прочитать();
		Если РеквизитыОбъекта.Количество() > 0 Тогда
			РеквизитыОбъекта[0].Значение = Смещение + 1;
			РеквизитыОбъекта.Записать();
		КонецЕсли;
	
		// вручную фиксируем смещения
		Компонента.ЗафиксироватьСмещение(СтруктураJSON.topic, Смещение + 1, Партиция);
		
	КонецЦикла;
	
	Компонента.ОстановитьКонсьюмера();
	Компонента = Неопределено;
	
КонецПроцедуры

Процедура ЗаписатьВерификацииДисконтныхКарт(СтруктураДанных)
	
	Интервал = 1800;   //Срок Действия
	
	Если СтруктураДанных.status = 0 Тогда
		
		ВерификацияНабор = РегистрыСведений.ВерификацииДисконтныхКарт.СоздатьНаборЗаписей();
		ВерификацияНабор.Отбор.Code_id.Установить(СтруктураДанных.code_id);
		ВерификацияНабор.Прочитать();
		Если ВерификацияНабор.Количество() > 0 Тогда
			ВерификацияНабор[0].ПризнакВерификации = Истина;
		КонецЕсли;
		
		Попытка
			ВерификацияНабор.Записать();
		Исключение
			ТекстОшибки = ОписаниеОшибки();
				ЗаписьЖурналаРегистрации("В РС ВерификацииДисконтныхКарт запись неудалась ",
				УровеньЖурналаРегистрации.Ошибка, , ,
				ТекстОшибки);
		КонецПопытки;

	ИначеЕсли СтруктураДанных.status = 1 Тогда
		
		МенеджерЗаписи = РегистрыСведений.ВерификацииДисконтныхКарт.СоздатьМенеджерЗаписи();
		
		Пробел = 160;
		
		МенеджерЗаписи.ДатаСоздания   = ИнтеграцияЕБК.ПолучитьДату(СтруктураДанных.created);
		МенеджерЗаписи.ДатаДействия   = ИнтеграцияЕБК.ПолучитьДату(СтруктураДанных.created) + Интервал;
		МенеджерЗаписи.КодКлиента     = СтрЗаменить(СтруктураДанных.ebk_id, Символ(Пробел), "");
		МенеджерЗаписи.Код            = СтрЗаменить(СтруктураДанных.code,   Символ(Пробел), "");
		МенеджерЗаписи.Code_id        = СтруктураДанных.code_id;
		МенеджерЗаписи.mobile_user_id = СтруктураДанных.mobile_user_id;
		
		Попытка
			МенеджерЗаписи.Записать();
		Исключение
			ТекстОшибки = ОписаниеОшибки();
				ЗаписьЖурналаРегистрации("В РС ВерификацииДисконтныхКарт запись неудалась ",
				УровеньЖурналаРегистрации.Ошибка, , ,
				ТекстОшибки);
		КонецПопытки;

	КонецЕсли;
		
КонецПроцедуры

Функция ПризнакВерифмкацииОтправитьВКафку(КодИД, ИмяСервиса) Экспорт 
	
	Статус = Ложь;	
	
	СтруктураОтправки = Новый Структура;
	СтруктураОтправки.Вставить("Code_id", КодИД);
	СтруктураОтправки.Вставить("status", 0);
	
	Попытка
		Джсон = КоннекторHTTP.ОбъектВJson(СтруктураОтправки);
		Статус = Истина;
	Исключение
		ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		ЗаписьЖурналаРегистрации("Верификация отправка Кафка",
		УровеньЖурналаРегистрации.Ошибка, , ,
		ТекстОшибки);
	КонецПопытки;
	
	СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);

	КоннекторHTTP.ОтправитьВКафку(СтруктураПодключения,СтрЗаменить(Джсон, Символы.ПС,""));
	
	Возврат Статус;
	
КонецФункции

Процедура ЗапуститьЧитателяКафкиСделки() Экспорт
	
	Компонента = СоздатьКомпонентуКафкаКлиент();
	
	Если Компонента = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ИмяСервиса = "_КафкаЧтениеСделок";
	
	СтруктураПодключения = РегламентныхЗаданийОбщий.ПолучитьСтруктуруПодключения(ИмяСервиса);
	
	Компонента.УстановитьПараметр("group.id", СтруктураПодключения.Параметры.ГруппаПодписчика);
	Компонента.УстановитьПараметр("enable.auto.commit", "false");
	Компонента.УстановитьПараметр("enable.auto.offset.store", "false");
	Компонента.УстановитьПараметр("enable.partition.eof", "false");
	
	Если СтруктураПодключения.Параметры.Свойство("КаталогЛогов")
		И ЗначениеЗаполнено(СтруктураПодключения.Параметры.КаталогЛогов) Тогда
		Компонента.КаталогЛогов = СтруктураПодключения.Параметры.КаталогЛогов;
	КонецЕсли;
	
	Брокер = СтрЗаменить(СтруктураПодключения.Хост, "http://", "");
	Топики = СтруктураПодключения.ИмяТопика;
	Компонента.УстановитьПозициюЧтения(Топики, Цел(СтруктураПодключения.КафкаОффсет), 0);
	Результат = Компонента.ИнициализироватьКонсьюмера(Брокер, Топики);
	Компонента.УстановитьТаймаутОжидания(5000);
	
	ЗаписьЖурналаРегистрации("УстановитьПозициюЧтения " + Строка(Топики),
		УровеньЖурналаРегистрации.Информация, , ,
		Топики + " " + Строка(СтруктураПодключения.КафкаОффсет));
	
	Если Не Результат Тогда
		ТекстОшибки = СтрШаблон("Не удалось инициализировать консьюмера для топиков: %1", Топики);
		ЗаписьЖурналаРегистрации("Интеграция Kafka. Consumer",
		УровеньЖурналаРегистрации.Ошибка, , ,
		ТекстОшибки);
		Возврат;
	КонецЕсли;
	
	Пока Истина Цикл
		
		Попытка
			Сообщение = Компонента.Слушать();
		Исключение
			Компонента = Неопределено;
			ВызватьИсключение ОписаниеОшибки();
		КонецПопытки;
		
		Если НЕ ЗначениеЗаполнено(Сообщение) Тогда
			ОписаниеОшибкиКомпонента = Компонента.ПолучитьСообщениеОбОшибке();
			Если ЗначениеЗаполнено(ОписаниеОшибкиКомпонента) Тогда
				ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаписьЖурналаРегистрации(ОписаниеОшибкиКомпонента,
				УровеньЖурналаРегистрации.Ошибка, , ,
				ТекстОшибки);
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		ОбъектЧтение = Новый ЧтениеJSON;
		ОбъектЧтение.УстановитьСтроку(Сообщение);
		СтруктураJSON = ПрочитатьJSON(ОбъектЧтение);
		ОбъектЧтение.Закрыть();
		
		Смещение = Число(СтруктураJSON.offset);
		Партиция = Число(СтруктураJSON.partition);
		Топик = СтруктураJSON.topic;
		
		Чтение = Новый ЧтениеJSON;
		Чтение.УстановитьСтроку(СтруктураJSON.message);
		СтруктураДанных = ПрочитатьJSON(Чтение);    
		
		ЗаписьЖурналаРегистрации("ЧтениеСообщенияКафка " + Строка(Топик),
		УровеньЖурналаРегистрации.Информация, , ,
		"Смещение " + Строка(Смещение) + 
		" Партиция " + Строка(Партиция) + 
		" Топик " + Строка(Топик) +
		" Сообщение " + Строка(Сообщение));
		
		ПроверкиПройдены = ВоронкиПродажСервер.ПроверкаСтруктурыОбращения(Компонента, Топик, Смещение, Партиция, ИмяСервиса, СтруктураДанных);
		
		Если Не ПроверкиПройдены Тогда
			Продолжить;
		КонецЕсли;
		
		Попытка
			ВоронкиПродажСервер.СоздатьДокументОбращениеВоронкиУдленногоОП(СтруктураДанных);
		Исключение
			ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
			ЗаписьЖурналаРегистрации("Ошибка создания обращения из сделки через кафку, оффсет: " + Смещение,
			УровеньЖурналаРегистрации.Ошибка, , ,
			ТекстОшибки);
		КонецПопытки;
		
		ВоронкиПродажСервер.ЗафиксироватьСмешение (Компонента, Топик, Смещение, Партиция, ИмяСервиса);
		
	КонецЦикла;
	
	Компонента.ОстановитьКонсьюмера();
	Компонента = Неопределено;
	
КонецПроцедуры


#КонецОбласти