Записки программиста 1с

Ошибки при работе сервиса 1С-Отчетность и их решения

Сертификат проверки электронной подписи не пригоден для шифрования

В 1С необходимо перейти в настройки учетной записи https://help.astral.ru/v/129672362 и нажать кнопку «Настроить автоматически сейчас».
После переотправьте отчет.

Произошла ошибка при выполнении криптоопераций над запросом к серверу СЭДО ФСС

Проверьте, пожалуйста, сертификаты СФР (бывший ФСС) по инструкции  https://help.astral.ru/v/158111787.
Проверить доступность, там где расположена база 1C до ресурса: sedo.fss.ru (193.148.44.114) — используется для получения и отправки СЭДО.
Необходимо открыть ресурс https://sedo.fss.ru/sedo-gateway/api/soap/SedoGateway?wsdl в браузере, у Вас откроется страница с XML кодом.
Проверить доступность до сервера, измените регистрационный номер СФР (бывший ФСС) в реквизитах организации (например, поменяйте один символ).
Если при нажатии на «Получить из ФСС» поступит ошибка, связанная с некорректным регистрационным номером, значит доступность до сервера есть.
При получении извещений от СФР (бывший ФСС), в разделе Извещения от ФСС нажмите Ещё → «Получить сообщения за период», «Выберите период»
и нажмите «Получить сообщения».

Сертификат проверки электронной подписи не пригоден для шифрования

В 1С необходимо перейти в настройки учетной записи https://help.astral.ru/v/129672362 и нажать кнопку «Настроить автоматически сейчас».
После переотправьте отчет.

Cведения об операторе отличаются от сведений об операторе, к которому подключен страхователь

Перед отправкой отчетов в СФР (бывший ПФР) необходимо отправить «Заявление на подключение к ЭДО СФР»
по инструкции в нашей базе знаний https://help.astral.ru/v/129668635.
Далее дождитесь обработки и повторите отправку отчёта.

Не удалось проверить этот сертификат, поскольку не получен правильный список отзыва сертификатов от центра сертификации

Ошибка поступает с приемного комплекса СФР (бывш.ПФР), если по отчету есть квитанция — ошибку нужно игнорировать, если квитанции нет — ожидать 4 рабочих дня с момента отправки отчета

Как подписать pdf файл электронно-цифровой подпись вне сервиса 1С-Отчетность

Можно подписать с помощью КриптоАРМ https://help.astral.ru/v/151552830 или КриптоПро CSP 5 версии
https://help.astral.ru/1c-o/podklyuchenie-k-servisu/algoritm-podklyucheniya-k-1s-otchetnosti#id-Алгоритмподключенияк1СОтчетности-Решение2.Загрузкаэлектроннойдоверенности,подписаннойстороннимисредствами



// Коннектор: удобный HTTP-клиент для 1С:Предприятие 8
//
// Copyright 2017-2023 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.6.0
//
// Требования: платформа 1С версии 8.3.10 и выше

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

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

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

// Отправляет GET запрос
//
// Параметры:
// URL — Строка — URL ресурса, к которому будет отправлен запрос.
// ПараметрыЗапроса — Структура, Соответствие — параметры, которые будут отправлены в URL (часть после ?):
// * Ключ — Строка — ключ параметра в URL.
// * Значение — Строка — значение параметра URL
// — Массив — сформирует строку из нескольких параметров: key=value1&key=value2 и т.д.
// ДополнительныеПараметры — См. НовыеПараметры
// Сессия — См. СоздатьСессию
//
// Возвращаемое значение:
// См. ВызватьМетод
//
Функция 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, Параметры);

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

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

ТекущаяСессия = ТекущаяСессия(Сессия);

Параметры = НовыеПараметры();
Дополнить(Параметры, ДополнительныеПараметры);
Дополнить(Параметры, ПараметрыИзАргументов(Неопределено, Данные, Неопределено));

Возврат ВызватьHTTPМетод(ТекущаяСессия, "MKCOL", URL, Параметры);

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

// Отправляет данные на указанный адрес для обработки с использованием указанного HTTP-метода.
//
// Параметры:
// Метод — Строка — имя HTTP-метода для запроса.
// URL — Строка — URL ресурса, к которому будет отправлен запрос.
// ДополнительныеПараметры — См. НовыеПараметры
// Сессия — См. СоздатьСессию
//
// Возвращаемое значение:
// Структура — ответ на выполненный запрос:
// * ВремяВыполнения — Число — время выполнения запроса в миллисекундах.
// * Cookies — Соответствие — cookies полученные с сервера.
// * Заголовки — Соответствие — HTTP заголовки ответа.
// * ЭтоПостоянныйРедирект — Булево — признак постоянного редиректа.
// * ЭтоРедирект — Булево — признак редиректа.
// * Кодировка — Строка — кодировка текста ответа.
// * Тело — ДвоичныеДанные — тело ответа.
// * КодСостояния — Число — код состояния ответа.
// * URL — Строка — итоговый URL, по которому был выполнен запрос.
//
Функция ВызватьМетод(Метод, URL, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт

ТекущаяСессия = ТекущаяСессия(Сессия);

Параметры = НовыеПараметры();
Дополнить(Параметры, ПараметрыИзАргументов(Неопределено, Неопределено, Неопределено));
Дополнить(Параметры, ДополнительныеПараметры);

Возврат ВызватьHTTPМетод(ТекущаяСессия, Метод, 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);

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

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

Область Конструкторы

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

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

Возврат Параметры;

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

// Конструктор описания отправляемого файла.
//
// Параметры:
// Имя — Строка — имя поля формы.
// ИмяФайла — Строка — имя файла.
// Данные — ДвоичныеДанные — двоичные данные файла.
// Тип — Строка — MIME-тип файла
//
// Возвращаемое значение:
// Структура:
// * Имя — Строка — имя поля формы.
// * ИмяФайла — Строка — имя файла.
// * Данные — ДвоичныеДанные — двоичные данные файла.
// * Тип — Строка — MIME-тип файла.
// * Заголовки — Соответствие — HTTP заголовки запроса.
//
Функция НовыйОтправляемыйФайл(Имя, ИмяФайла, Данные = Неопределено, Тип = Неопределено) Экспорт

Файл = Новый Структура;
Файл.Вставить("Имя", Имя);
Файл.Вставить("ИмяФайла", ИмяФайла);
Файл.Вставить("Данные", ?(Данные = Неопределено, Base64Значение(""), Данные));
Файл.Вставить("Тип", Тип);
Файл.Вставить("Заголовки", Новый Соответствие);

Возврат Файл;

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

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

Сессия = Новый Структура;
Сессия.Вставить("Заголовки", ЗаголовкиПоУмолчанию());
Сессия.Вставить("Аутентификация", Неопределено);
Сессия.Вставить("Прокси", Неопределено);
Сессия.Вставить("ПараметрыЗапроса", Новый Соответствие);
Сессия.Вставить("ПроверятьSSL", Истина);
Сессия.Вставить("КлиентскийСертификатSSL", Неопределено);
Сессия.Вставить("МаксимальноеКоличествоПеренаправлений", МаксимальноеКоличествоПеренаправлений());
Сессия.Вставить("Cookies", Новый Соответствие);
Сессия.Вставить("СлужебныеДанные", Новый Структура("ПараметрыDigest"));

Возврат Сессия;

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

// Пакет ответа результата вызова метода HTTP.
//
// Возвращаемое значение:
// * Метод — Строка — имя HTTP-метода запроса
// * URL — Строка — итоговый URL, по которому был выполнен запрос.
// * КодСостояния — Число — Код состояния ответа..
// * Заголовки — Соответствие — Заголовки ответа.
// * Тело — ДвоичныеДанные — Тело ответа.
// * Кодировка — Строка — код кодировки ответа.
// * ВремяВыполнения — Число — время выполнения запроса в миллисекундах.
// * ЭтоПостоянныйРедирект — Булево — указывает что это постоянный редирект.
// * ЭтоРедирект — Булево — указывает что это редирект.
// * Cookies — Соответствие — хранилище cookies.
// * Ошибки — Массив Из Строка — Список ошибок возникших в ходе выполнения запроса.
//
Функция НовыйОтвет() Экспорт

Результат = Новый Структура;
Результат.Вставить("Метод", "GET");
Результат.Вставить("URL", "");
Результат.Вставить("КодСостояния", 600); // Сетевая ошибка (>500)
Результат.Вставить("Заголовки", Новый Соответствие);
Результат.Вставить("Тело", Base64Значение(""));
Результат.Вставить("Кодировка", "utf-8");
Результат.Вставить("ВремяВыполнения", Неопределено);
Результат.Вставить("ЭтоПостоянныйРедирект", Ложь);
Результат.Вставить("ЭтоРедирект", Ложь);
Результат.Вставить("Cookies", Новый Соответствие);
Результат.Вставить("Ошибки", Новый Массив);

Возврат Результат;

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

// Конструктор аутентификации операционной системы
//
// Возвращаемое значение:
// Структура:
// * ИспользоватьАутентификациюОС — Строка — включает использование аутентификации NTLM или Negotiate.
//
Функция НоваяАутентификацияОС() Экспорт

Результат = Новый Структура;
Результат.Вставить("ИспользоватьАутентификациюОС", Истина);

Возврат Результат;

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

// Конструктор аутентификации Basic
//
// Возвращаемое значение:
// Структура:
// * Пользователь — Строка — имя пользователя.
// * Пароль — Строка — пароль пользователя.
//
Функция НоваяАутентификацияBasic(Пользователь = «», Пароль = «») Экспорт

Результат = Новый Структура;
Результат.Вставить("Тип", "Basic");
Результат.Вставить("Пользователь", Пользователь);
Результат.Вставить("Пароль", Пароль);

Возврат Результат;

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

// Конструктор аутентификации Digest
//
// Возвращаемое значение:
// Структура:
// * Тип — Строка — Код типа аутентификации. Всегда «Digest».
// * Пользователь — Строка — имя пользователя.
// * Пароль — Строка — пароль пользователя.
//
Функция НоваяАутентификацияDigest(Пользователь = «», Пароль = «») Экспорт

Результат = Новый Структура;
Результат.Вставить("Тип", "Digest");
Результат.Вставить("Пользователь", Пользователь);
Результат.Вставить("Пароль", Пароль);

Возврат Результат;

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

// Конструктор аутентификации Bearer
//
// Возвращаемое значение:
// Структура:
// * Тип — Строка — Код типа аутентификации. Всегда «Bearer».
// * Токен — Строка — Bearer-токен.
//
Функция НоваяАутентификацияBearer(Токен = «») Экспорт

Результат = Новый Структура;
Результат.Вставить("Тип", "Bearer");
Результат.Вставить("Токен", Токен);

Возврат Результат;

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

// Конструктор аутентификации AWS4-HMAC-SHA256
//
// Возвращаемое значение:
// Структура:
// * Тип — Строка — Код типа аутентификации. Всегда «AWS4-HMAC-SHA256».
// * ИдентификаторКлючаДоступа — Строка — идентификатор ключа доступа (AccessKey).
// * СекретныйКлюч — Строка — секретный ключ (SecretKey).
// * Сервис — Строка — сервис, к которому выполняется подключение.
// * Регион — Строка — регион, к которому выполняется подключение.
//
Функция НоваяАутентификацияAWS4(КлючДоступа = «», СекретныйКлюч = «», Сервис = «», Регион = «») Экспорт

Результат = Новый Структура;
Результат.Вставить("Тип", "AWS4-HMAC-SHA256");
Результат.Вставить("ИдентификаторКлючаДоступа", КлючДоступа);
Результат.Вставить("СекретныйКлюч", СекретныйКлюч);
Результат.Вставить("Сервис", Сервис);
Результат.Вставить("Регион", Регион);

Возврат Результат;

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

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

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

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

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

Попытка
    Возврат JsonВОбъект(РаспаковатьОтвет(Ответ), Ответ.Кодировка, ПараметрыПреобразованияJSON);
Исключение
    ВызватьИсключение КакИсключение(Ответ, НСтр("ru = 'Ошибка при десериализации JSON.'"));
КонецПопытки;

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

// Возвращает ответ сервера в виде текста.
//
// Параметры:
// Ответ — См. НовыйОтвет
// Кодировка — Строка, КодировкаТекста — определяет кодировку текста.
// Если значение не задано, то кодировка извлекается из Ответ.Кодировка.
//
// Возвращаемое значение:
// Строка — ответ сервера в виде текста.
//
Функция КакТекст(Ответ, Кодировка = Неопределено) Экспорт

Если Не ЗначениеЗаполнено(Кодировка) Тогда
    Кодировка = Ответ.Кодировка;
КонецЕсли;

ЧтениеТекста = Новый ЧтениеТекста(РаспаковатьОтвет(Ответ).ОткрытьПотокДляЧтения(), Кодировка);
Текст = ЧтениеТекста.Прочитать();
ЧтениеТекста.Закрыть();

Если Текст = Неопределено Тогда
    Текст = "";
КонецЕсли;

Возврат Текст;

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

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

Возврат РаспаковатьОтвет(Ответ);

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

// Возвращает ответ сервера в XDTO.
//
// Параметры:
// Ответ — См. НовыйОтвет
// ПараметрыЧтенияXML — ПараметрыЧтенияXML — Параметры чтения, которые будут использованы при чтении данных XML
// Подробнее см. в синтакс помощнике метод ЧтениеXML.ОткрытьПоток
// НаборСхемXML — НаборСхемXML — Набор схем XML, используемых при проверке читаемого документа XML.
// Если набор схем указан, но не проверен и включена проверка документа XML,
// то будет выполнена проверка набора схем
// Подробнее см. в синтакс помощнике метод ЧтениеXML.ОткрытьПоток
// Кодировка — Строка, КодировкаТекста — Позволяет задать кодировку.
// Подробнее см. в синтакс помощнике метод ЧтениеXML.ОткрытьПоток
//
// Возвращаемое значение:
// ОбъектXDTO, СписокXDTO — тип возвращаемого значения может быть любым из поддерживаемых сериализацию в XDTO.
//
Функция КакXDTO(Ответ,
ПараметрыЧтенияXML = Неопределено,
НаборСхемXML = Неопределено,
Кодировка = Неопределено) Экспорт

Попытка
    ДвоичныеДанные = РаспаковатьОтвет(Ответ);

    ПотокДляЧтения = ДвоичныеДанные.ОткрытьПотокДляЧтения();

    Если Не ЗначениеЗаполнено(Кодировка) Тогда
        Кодировка = Ответ.Кодировка;
    КонецЕсли;

    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.ОткрытьПоток(ПотокДляЧтения, ПараметрыЧтенияXML, НаборСхемXML, Кодировка);

    ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
Исключение
    ВызватьИсключение КакИсключение(Ответ, НСтр("ru = 'Ошибка при десериализации XDTO.'"));
КонецПопытки;

Возврат ОбъектXDTO;

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

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

ТекстИсключения = СтрШаблон(
    НСтр("ru = 'HTTP %1 %2
               |%3'"),
    Ответ.Метод,
    Ответ.URL,
    ПредставлениеКодаСостоянияHTTP(Ответ.КодСостояния)
);

ТелоОтвета = ВырезатьТекст(КакТекст(Ответ));

Если Не ПустаяСтрока(ТелоОтвета) Тогда
    ТекстИсключения = ТекстИсключения + Символы.ПС + СтрШаблон(
        НСтр("ru = 'Тело ответа:
                   |%1'"),
        ТелоОтвета);
КонецЕсли;

Если Ответ.Ошибки.Количество() Тогда
    ТекстИсключения = ТекстИсключения + Символы.ПС + Символы.ПС
        + СтрСоединить(Ответ.Ошибки, Символы.ПС + Символы.ПС);
КонецЕсли;

Если Не ПустаяСтрока(ТекстДляПользователя) Тогда
    ТекстИсключения = ТекстДляПользователя + Символы.ПС + Символы.ПС + ТекстИсключения;
КонецЕсли;

Возврат ТекстИсключения;

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

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

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

// Возвращает структурированное представление 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 Тогда
        Аутентификация.Пароль = АутентификацияЧасти[1];
    КонецЕсли;
КонецЕсли;

// IPv6
РазбитьСтрокуПоРазделителю(Сервер, URL, "]");
Если ЗначениеЗаполнено(Сервер) Тогда
    Сервер = Сервер + "]";
КонецЕсли;

URL = СтрЗаменить(URL, "/", "");

РазбитьСтрокуПоРазделителю(Порт, URL, ":", Истина);

Если Не ЗначениеЗаполнено(Сервер) Тогда
    Сервер = URL;
КонецЕсли;

Если ЗначениеЗаполнено(Порт) Тогда
    Порт = Число(Порт);
Иначе
    Порт = 0;
КонецЕсли;

РазбитьСтрокуПоРазделителю(Фрагмент, Путь, "#", Истина);

ПараметрыЗапроса = ЗаполнитьПараметрыЗапроса(Путь);

Если Не ЗначениеЗаполнено(Схема) Тогда
    Схема = "http";
КонецЕсли;

Если Не ЗначениеЗаполнено(Путь) Тогда
    Путь = "/";
КонецЕсли;

Результат = Новый Структура;
Результат.Вставить("Схема", Схема);
Результат.Вставить("Аутентификация", Аутентификация);
Результат.Вставить("Сервер", Сервер);
Результат.Вставить("Порт", Порт);
Результат.Вставить("Путь", Путь);
Результат.Вставить("ПараметрыЗапроса", ПараметрыЗапроса);
Результат.Вставить("Фрагмент", Фрагмент);

Возврат Результат;

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

// Преобразование Объекта в JSON.
//
// Параметры:
// Объект — Произвольный — данные, которые необходимо преобразовать в JSON.
// ПараметрыПреобразования — Структура — кодировка текста JSON. Значение по умолчанию — utf-8.
// * ФорматДатыJSON — ФорматДатыJSON — определяет формат сериализации дат JSON-объектов.
// * ВариантЗаписиДатыJSON — ВариантЗаписиДатыJSON — определяет вариант записи даты в формате JSON.
// * ИмяФункцииПреобразования — Строка — функция, которая вызывается для всех свойств,
// тип которых не поддерживает автоматическую сериализацию в 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.МодульФункцииПреобразования,
        ПараметрыПреобразованияJSON.ДополнительныеПараметрыФункцииПреобразования);
Иначе
    ЗаписатьJSON(ЗаписьJSON, Объект, НастройкиСериализации);
КонецЕсли;

Возврат ЗаписьJSON.Закрыть();

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

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

ПараметрыПреобразованияJSON = Объединить(ПараметрыПреобразованияJSONПоУмолчанию(), ПараметрыПреобразования);

ЧтениеJSON = Новый ЧтениеJSON;
Если ТипЗнч(Json) = Тип("ДвоичныеДанные") Тогда
    ЧтениеJSON.ОткрытьПоток(Json.ОткрытьПотокДляЧтения(), Кодировка);
ИначеЕсли ТипЗнч(Json) = Тип("Строка") Тогда
    Чтение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() Экспорт

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

Возврат КодыСостояния;

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

// Возвращает текстовое представление переданного кода состояния HTTP.
//
// Параметры:
// КодСостояния — Число — код состояния HTTP, для которого нужно получить текстовое представление.
//
// Возвращаемое значение:
// Строка — текстовое представление кода состояния HTTP.
//
Функция ПредставлениеКодаСостоянияHTTP(КодСостояния) Экспорт

ОписаниеКодаСостояния = Неопределено;
Для Каждого Описание Из ОписанияКодовСостоянийHTTP() Цикл
    Если Описание.Код = КодСостояния Тогда
        ОписаниеКодаСостояния = Описание;
        Прервать;
    КонецЕсли;
КонецЦикла;

Если ОписаниеКодаСостояния = Неопределено Тогда
    Возврат СтрШаблон(НСтр("ru = '%1: Неизвестный код состояния HTTP'"), КодСостояния);
Иначе
    Возврат СтрШаблон("%1: %2", ОписаниеКодаСостояния.Код, ОписаниеКодаСостояния.Описание);
КонецЕсли;

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

// Выполняет чтение данных из архива 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);
ПодготовитьАутентификацию(ПодготовленныйЗапрос);

Возврат ПодготовленныйЗапрос;

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

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

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

Область РаботаСHTTPЗапросами

Функция ПараметрыИзАргументов(ПараметрыЗапроса, Данные, Json)

Результат = Новый Структура;
Результат.Вставить("ПараметрыЗапроса", ПараметрыЗапроса);
Результат.Вставить("Данные", Данные);
Результат.Вставить("Json", Json);

Возврат Результат;

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

Функция ВызватьHTTPМетод(Сессия, Метод, URL, ДополнительныеПараметры)

ПодготовленныйЗапрос = ПодготовитьЗапрос(Сессия, Метод, URL, ДополнительныеПараметры);

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

Ответ = ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, НастройкиПодключения);

Если НастройкиПодключения.РазрешитьПеренаправление И Ответ.ЭтоРедирект Тогда
    // INFO: по хорошему аутентификацию нужно привести к новых параметрам, но пока будем игнорировать.
    Ответ = ПеренаправитьЗапрос(Сессия, НастройкиПодключения, ПодготовленныйЗапрос, Ответ);
КонецЕсли;

Возврат Ответ;

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

Функция ПеренаправитьЗапрос(Сессия, НастройкиПодключения, ПодготовленныйЗапрос, ПеренаправленныйОтвет)

Перенаправление = 0;

Пока ПеренаправленныйОтвет.ЭтоРедирект Цикл

    ПодготовитьЗапросДляРедиректа(Сессия, ПодготовленныйЗапрос, ПеренаправленныйОтвет);

    ПеренаправленныйОтвет = ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, НастройкиПодключения);

    Перенаправление = Перенаправление + 1;

    Если Перенаправление > Сессия.МаксимальноеКоличествоПеренаправлений Тогда
        ВызватьИсключение("СлишкомМногоПеренаправлений");
    КонецЕсли;

КонецЦикла;

Возврат ПеренаправленныйОтвет;

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

Процедура ПодготовитьЗапросДляРедиректа(Сессия, ПодготовленныйЗапрос, ПеренаправленныйОтвет)

КодыСостоянияHTTP = КодыСостоянияHTTP();

Новый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(ПодготовленныйЗапрос);

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

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

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

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

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

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

СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);

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

HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки;

УпаковатьЗапрос(HTTPЗапрос);

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

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

Функция ЗакодироватьФайлы(HTTPЗапрос, Файлы, Данные)

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

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

ТелоЗапроса = HTTPЗапрос.ПолучитьТелоКакПоток();
ЗаписьДанных = Новый ЗаписьДанных(ТелоЗапроса, КодировкаТекста.UTF8, ПорядокБайтов.LittleEndian, "", "", Ложь);
Для Каждого Часть Из Части Цикл
    ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + РазделительСтрок);
    ЗаписьДанных.ЗаписатьСтроку(ЗаголовкиВСтроку(Часть.Заголовки));
    Если ТипЗнч(Часть.Данные) = Тип("ДвоичныеДанные") Тогда
        ЗаписьДанных.Записать(Часть.Данные);
    Иначе
        ЗаписьДанных.ЗаписатьСтроку(Часть.Данные);
    КонецЕсли;
    ЗаписьДанных.ЗаписатьСтроку(РазделительСтрок);
КонецЦикла;
ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + "--" + РазделительСтрок);
ЗаписьДанных.Закрыть();

Возврат СтрШаблон("multipart/form-data; boundary=%1", Разделитель);

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

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

Поле = Новый Структура("Имя,ИмяФайла,Данные,Тип,Заголовки");
Поле.Имя = ИсходныеПараметры.Имя;
Поле.Данные = ИсходныеПараметры.Данные;

Поле.Тип = ЗначениеПоКлючу(ИсходныеПараметры, "Тип");
Поле.Заголовки = ЗначениеПоКлючу(ИсходныеПараметры, "Заголовки", Новый Соответствие);
Поле.ИмяФайла = ЗначениеПоКлючу(ИсходныеПараметры, "ИмяФайла");

Ключ = "Content-Disposition";
Если ЗначениеЗаголовка("content-disposition", Поле.Заголовки, Ключ) = Ложь Тогда
    Поле.Заголовки.Вставить("Content-Disposition", "form-data");
КонецЕсли;

Части = Новый Массив;
Части.Добавить(Поле.Заголовки[Ключ]);
Части.Добавить(СтрШаблон("name=""%1""", Поле.Имя));
Если ЗначениеЗаполнено(Поле.ИмяФайла) Тогда
    Части.Добавить(СтрШаблон("filename=""%1""", Поле.ИмяФайла));
КонецЕсли;

Поле.Заголовки[Ключ] = СтрСоединить(Части, "; ");
Поле.Заголовки["Content-Type"] = Поле.Тип;

Возврат Поле;

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

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

ПараметрыЗапроса = Новый Соответствие;

Запрос = "";
РазбитьСтрокуПоРазделителю(Запрос, Путь, "?", Истина);
Для Каждого СтрокаКлючРавноПараметр Из СтрРазделить(Запрос, "&", Ложь) Цикл
    СтрокаКлючРавноПараметр = РаскодироватьСтроку(
        СтрокаКлючРавноПараметр, СпособКодированияСтроки.URLВКодировкеURL);

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

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

КонецЦикла;

Возврат ПараметрыЗапроса;

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

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

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

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

Возврат СтрСоединить(ЧастиПараметрыЗапроса, "&");

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

Процедура ПереопределитьМетод(ПодготовленныйЗапрос, Ответ)

КодыСостоянияHTTP = КодыСостоянияHTTP();

Метод = ПодготовленныйЗапрос.Метод;

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

// Поведение браузеров
Если Ответ.КодСостояния = КодыСостоянияHTTP.ПеремещеноВременно_302 И Метод <> "HEAD" Тогда
    Метод = "GET";
КонецЕсли;

ПодготовленныйЗапрос.Метод = Метод;

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

Функция ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, Настройки)

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

Повтор = 0;
Длительность = 0;
Ошибки = Новый Массив;

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

        ТекстОшибки = СтрШаблон(
            НСтр("ru = 'HTTP %1 %2
                       |Network error:
                       |%3'"),
            ПодготовленныйЗапрос.Метод,
            ПодготовленныйЗапрос.URL,
            ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())
        );
        Ошибки.Добавить(ТекстОшибки);
    КонецПопытки;

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

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

    Если Повтор > Настройки.МаксимальноеКоличествоПовторов
        ИЛИ Длительность > Настройки.МаксимальноеВремяПовторов Тогда
        Прервать;
    КонецЕсли;

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

    ДлительностьПриостановки = РассчитатьДлительностьПриостановки(
        Повтор,
        Настройки.КоэффициентЭкспоненциальнойЗадержки,
        ЗаголовокRetryAfter,
        Настройки.МаксимальноеВремяПовторов - Длительность);
    Приостановить(ДлительностьПриостановки);
КонецЦикла;

Если ОшибкаВыполненияЗапроса <> Неопределено Тогда
    ВызватьИсключение(ПодробноеПредставлениеОшибки(ОшибкаВыполненияЗапроса));
КонецЕсли;

ЗаголовокContentType = ЗначениеЗаголовка("content-type", Ответ.Заголовки);
Если ЗаголовокContentType = Ложь Тогда
    ЗаголовокContentType = "";
КонецЕсли;

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

Сессия.Cookies = ОбъединитьCookies(Сессия.Cookies, ПодготовленныйОтвет.Cookies);

Возврат ПодготовленныйОтвет;

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

Функция ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки)

СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
Соединение = Соединение(СтруктураURL, ПодготовленныйЗапрос.Аутентификация, Настройки, Сессия);
Ответ = Соединение.ВызватьHTTPМетод(ПодготовленныйЗапрос.Метод, ПодготовленныйЗапрос.HTTPЗапрос);

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

Возврат Ответ;

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

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

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

Возврат ПовторитьЗапрос;

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

Функция ПовторятьПриКодеСостояния(КодСостояния, Настройки)

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

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

Функция ЭтоПостоянныйРедирект(КодСостояния, Заголовки)

КодыСостоянияHTTP = КодыСостоянияHTTP();

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

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

Функция ЭтоРедирект(КодСостояния, Заголовки)

КодыСостоянияHTTP = КодыСостоянияHTTP();

СостоянияРедиректа = Новый Массив;
СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПеремещеноНавсегда_301);
СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПеремещеноВременно_302);
СостоянияРедиректа.Добавить(КодыСостоянияHTTP.СмотретьДругое_303);
СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ВременноеПеренаправление_307);
СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПостоянноеПеренаправление_308);

Возврат ЕстьЗаголовокLocation(Заголовки) И СостоянияРедиректа.Найти(КодСостояния) <> Неопределено;

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

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

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

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

Функция РаспаковатьОтвет(Ответ)

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

Возврат Ответ.Тело;

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

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

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

Процедура ОбработкаОтветаСКодом401(Сессия, ПодготовленныйЗапрос, Настройки, Ответ)

Если ЭтоРедирект(Ответ.КодСостояния, Ответ.Заголовки) Тогда
    Возврат;
КонецЕсли;

КодыСостоянияHTTP = КодыСостоянияHTTP();
Если Ответ.КодСостояния < КодыСостоянияHTTP.НеверныйЗапрос_400
    ИЛИ Ответ.КодСостояния >= КодыСостоянияHTTP.ВнутренняяОшибкаСервера_500 Тогда
    Возврат;
КонецЕсли;

Значение = ЗначениеЗаголовка("www-authenticate", Ответ.Заголовки);
Если Значение <> Ложь И СтрНайти(НРег(Значение), "digest") Тогда
    Позиция = СтрНайти(НРег(Значение), "digest");
    Значение = Сред(Значение, Позиция + СтрДлина("digest") + 1);
    Значение = СтрЗаменить(Значение, """", "");
    Значение = СтрЗаменить(Значение, Символы.ПС, "");

    ПараметрыDigest = Новый Структура("algorithm,realm,nonce,qop,opaque");
    Для Каждого Часть Из РазбитьСтрокуПоСтроке(Значение, ", ") Цикл
        КлючЗначение = СтрРазделить(Часть, "=");
        ПараметрыDigest.Вставить(КлючЗначение[0], КлючЗначение[1]);
    КонецЦикла;

    Сессия.СлужебныеДанные.ПараметрыDigest = ПараметрыDigest;

    ПодготовленныйЗапрос.Заголовки.Вставить("Authorization", ПодготовитьЗаголовокDigest(Сессия, ПодготовленныйЗапрос));
    ПодготовленныйЗапрос.HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки;

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

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

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

Область 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, ПараметрыЗапроса);

Возврат ПодготовленныйURL;

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

Функция СобратьАдресРесурса(СтруктураURL, ПараметрыЗапроса)

АдресРесурса = СтруктураURL.Путь;

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

Возврат АдресРесурса;

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

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

НовыйURL = ЗначениеЗаголовка("location", Ответ.Заголовки);
НовыйURL = РаскодироватьСтроку(НовыйURL, СпособКодированияСтроки.URLВКодировкеURL);

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

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

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

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

Функция ЭтоСтандартныйПорт(СтруктураURL)

СтандартныйПортHTTP = 80;
СтандартныйПортHTTPS = 443;

Возврат (СтруктураURL.Схема = "http" И СтруктураURL.Порт = СтандартныйПортHTTP)
    ИЛИ (СтруктураURL.Схема = "https" И СтруктураURL.Порт = СтандартныйПортHTTPS);

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

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

Область РаботаССоединением

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

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

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

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

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

Функция Соединение(ПараметрыСоединения, Аутентификация, ДополнительныеПараметры, Сессия)

Если Не ЗначениеЗаполнено(ПараметрыСоединения.Порт) Тогда
    Если ПараметрыСоединения.Схема = "https" Тогда
        ПараметрыСоединения.Порт = 443;
    Иначе
        ПараметрыСоединения.Порт = 80;
    КонецЕсли;
КонецЕсли;

ЗащищенноеСоединение = Неопределено;
Если ПараметрыСоединения.Схема = "https" Тогда
    ЗащищенноеСоединение = ОбъектЗащищенногоСоединения(ДополнительныеПараметры);
КонецЕсли;

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

ИспользоватьАутентификациюОС = Аутентификация.Свойство("ИспользоватьАутентификациюОС")
    И Аутентификация.ИспользоватьАутентификациюОС = Истина;

ПараметрыДляРасчетаИдентификатора = Новый Массив;
ПараметрыДляРасчетаИдентификатора.Добавить(ПараметрыСоединения.Сервер);
ПараметрыДляРасчетаИдентификатора.Добавить(ПараметрыСоединения.Порт);
ПараметрыДляРасчетаИдентификатора.Добавить(Пользователь);
ПараметрыДляРасчетаИдентификатора.Добавить(Пароль);
ПараметрыДляРасчетаИдентификатора.Добавить(ДополнительныеПараметры.Таймаут);
ПараметрыДляРасчетаИдентификатора.Добавить(ИспользоватьАутентификациюОС);
ПараметрыДляРасчетаИдентификатора.Добавить(ЗащищенноеСоединение);
ПараметрыДляРасчетаИдентификатора.Добавить(ДополнительныеПараметры.Прокси);

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

ИдентификаторСоединения = ИдентификаторСоединения(ПараметрыДляРасчетаИдентификатора);

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

Возврат ПулСоединений[ИдентификаторСоединения];

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

Функция ИдентификаторСоединения(ПараметрыСоединения)

ПараметрыДляРасчетаИдентификатора = Новый Массив;

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

Возврат ХешированиеДанных(ХешФункция.MD5, СтрСоединить(ПараметрыДляРасчетаИдентификатора, ""));

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

Функция ОбъектЗащищенногоСоединения(ДополнительныеПараметры)

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

Возврат Новый ЗащищенноеСоединениеOpenSSL(КлиентскийСертификат, СертификатыУЦ);

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

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

Если ДополнительныеПараметры.Свойство("Таймаут") И ДополнительныеПараметры.Таймаут <> Неопределено Тогда
    Таймаут = ДополнительныеПараметры.Таймаут;
Иначе
    Таймаут = СтандартныйТаймаут();
КонецЕсли;

Возврат Таймаут;

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

Функция ПроксиПоУмолчанию(URL)

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

Возврат ПроксиПоУмолчанию;

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

Функция ТекущаяСессия(Сессия)

Если Сессия = Неопределено Тогда
    Сессия = СоздатьСессию();
КонецЕсли;

Возврат Сессия;

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

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

Область Заголовки

Функция ЗаголовкиВСтроку(Заголовки)

РазделительСтрок = Символы.ВК + Символы.ПС;
Строки = Новый Массив;

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

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

Возврат СтрСоединить(Строки, РазделительСтрок);

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

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

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

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

Функция ЕстьЗаголовокLocation(Заголовки)

Возврат ЗначениеЗаголовка("location", Заголовки) <> Ложь;

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

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

Кодировка = Неопределено;

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

Если Кодировка = Неопределено И СтрНайти(ТипСодержимого, "text") Тогда
    Кодировка = "iso-8859-1";
КонецЕсли;

Возврат Кодировка;

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

Функция ЗначениеЗаголовка(Заголовок, ВсеЗаголовки, Ключ = Неопределено)

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

Возврат Ложь;

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

Функция СформироватьЗначениеЗаголовкаHost(СтруктураURL)

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

Возврат Host;

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

Функция ПодготовитьЗаголовок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", СтрСоединить(Строки, ""));

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

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

Область Cookies

Процедура ПодготовитьCookies(ПодготовленныйЗапрос)

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

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

Функция ПодготовитьЗаголовокCookie(ПодготовленныйЗапрос)

СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);

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

Возврат СтрСоединить(Cookies, "; ");

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

Функция Объединить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;

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

Функция ОтобратьCookiesДляЗапроса(СтруктураURL, Cookies)

СерверВЗапросе = ДобавитьЛидирующуюТочку(СтруктураURL.Сервер);

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

Возврат Результат;

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

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

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

    Список.Добавить(Cookie.Значение);
КонецЦикла;

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

Функция Дозаполнить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;

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

Функция ИзвлечьCookies(Заголовки, URL)

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

Возврат Cookies;

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

Функция РазбитьНаОтдельныеЗаголовкиCookies(Знач Заголовок)

Заголовки = Новый Массив;

Если Не ЗначениеЗаполнено(Заголовок) Тогда
    Возврат Заголовки;
КонецЕсли;

ЗапчастиЗаголовков = СтрРазделить(Заголовок, ",", Ложь);

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

Возврат Заголовки;

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

Процедура ДобавитьCookieВХранилище(ХранилищеCookies, Cookie, Замещать = Ложь)

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

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

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

Если ХранилищеCookies.Получить(Cookie.Домен) <> Неопределено
    И ХранилищеCookies[Cookie.Домен].Получить(Cookie.Путь) <> Неопределено
    И ХранилищеCookies[Cookie.Домен][Cookie.Путь].Получить(Cookie.Наименование) <> Неопределено Тогда
    ХранилищеCookies[Cookie.Домен][Cookie.Путь].Удалить(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ИЗаполнитьОсновныеПараметры(Параметр)

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

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

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

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

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

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

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

Функция КонструкторCookie(Наименование = «», Значение = Неопределено)

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

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

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

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

Область ПараметрыРаботыСJSON

// Преобразует значение типа к типу, сериализация которого поддерживается.
//
// Параметры:
// Свойство — Строка — имя свойства, если выполняется запись структуры или соответствия.
// Значение — Произвольный — исходное значение.
// ДополнительныеПараметры — Произвольный — дополнительные параметры, которые указаны в вызове метода ЗаписатьJSON.
// Отказ — Булево — отказ от записи свойства.
//
// Возвращаемое значение:
// Произвольный — см. типы ЗаписатьJSON.
//
Функция ПреобразованиеJson(Свойство, Значение, ДополнительныеПараметры, Отказ) Экспорт

Если ТипЗнч(Значение) = Тип("УникальныйИдентификатор") Тогда
    Возврат Строка(Значение);
ИначеЕсли ТипЗнч(Значение) = Тип("ДвоичныеДанные") Тогда
    Возврат ПолучитьBase64СтрокуИзДвоичныхДанных(Значение);
Иначе
    // Если значение не поддерживает сериализацию в JSON, то будет выброшено исключение
    Возврат Значение;
КонецЕсли;

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

// Восстанавливает значение типа, десериализация которого не поддерживается.
//
// Параметры:
// Свойство — Строка — имя свойства, значение которого нужно восстановить.
// Значение — Строка — значение, которое нужно восстановить.
// ТипыСвойств — Соответствие — типы свойств, которые нужно восстановить.
// * Ключ — Строка — имя свойства. Равно значению параметра Свойство.
// * Значение — Тип — исходный тип значения.
//
// Возвращаемое значение:
// Произвольный — восстановленное значение.
//
Функция ВосстановлениеJson(Свойство, Значение, ТипыСвойств) Экспорт

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

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

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

Область Аутентификация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Запрос.Заголовки = ПодготовленныйЗапрос.Заголовки;

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

Функция КаноническиеЗаголовкиAWS4(Заголовки, СтруктураURL)

Список = Новый СписокЗначений;

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

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

Если Не ЗаголовокHostЕстьВЗапросе Тогда
    Список.Добавить("host", СформироватьЗначениеЗаголовкаHost(СтруктураURL));
КонецЕсли;

Список.СортироватьПоЗначению(НаправлениеСортировки.Возр);

КаноническиеЗаголовки = Новый Массив;
ПодписываемыеЗаголовки = Новый Массив;
Для Каждого ЭлементСписка Из Список Цикл
    КаноническиеЗаголовки.Добавить(ЭлементСписка.Значение + ":" + ЭлементСписка.Представление);
    ПодписываемыеЗаголовки.Добавить(ЭлементСписка.Значение);
КонецЦикла;
КаноническиеЗаголовки.Добавить("");

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

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

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

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

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

Возврат СтрСоединить(КаноническиеПараметры, "&");

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

Функция ЗаголовкиПоУмолчаниюAWS4()

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

Возврат Заголовки;

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

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

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

Если Не ПодготовленныйЗапрос.Аутентификация.Свойство("Токен") или не ЗначениеЗаполнено(ПодготовленныйЗапрос.Аутентификация.Токен) Тогда 
    // Токен не заполнен.
    Возврат;    
КонецЕсли;

ПодготовленныйЗапрос.Заголовки.Вставить("Authorization", СтрШаблон("Bearer %1", ПодготовленныйЗапрос.Аутентификация.Токен));

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

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

Область СлужебныеСтруктуры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.ЗакрытьИПолучитьДвоичныеДанные();

КонецЕсли

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

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

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

Функция ЗаголовкиПоУмолчанию()

Заголовки = Новый Соответствие;

Если МобильноеПриложениеСервер Тогда

Заголовки.Вставить("Accept-Encoding", "identity");

Иначе

Заголовки.Вставить("Accept-Encoding", "gzip");

КонецЕсли

Заголовки.Вставить("Accept", "*/*");
Заголовки.Вставить("Connection", "keep-alive");

Возврат Заголовки;

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

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

Возврат 30;

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

Функция СтандартныйТаймаут()

Возврат 30;

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

Функция ПараметрыПреобразованияJSONПоУмолчанию()

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

Возврат ПараметрыПреобразованияПоУмолчанию;

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

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

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

Возврат ПараметрыЗаписиJSONПоУмолчанию;

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

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

Область КодыСостояний

Функция ОписанияКодовСостоянийHTTP()

Коды = Новый Массив;
Коды.Добавить(НовыйКодHTTP(100, "Продолжай_100", "Continue"));
Коды.Добавить(НовыйКодHTTP(101, "ПереключениеПротокола_101", "Switching Protocols"));
Коды.Добавить(НовыйКодHTTP(102, "ИдетОбработка_102", "Processing"));
Коды.Добавить(НовыйКодHTTP(103, "РанняяМетаинформация_103", "Early Hints"));

Коды.Добавить(НовыйКодHTTP(200, "ОК_200", "OK"));
Коды.Добавить(НовыйКодHTTP(201, "Создано_201", "Created"));
Коды.Добавить(НовыйКодHTTP(202, "Принято_202", "Accepted"));
Коды.Добавить(НовыйКодHTTP(203, "ИнформацияНеАвторитетна_203", "Non-Authoritative Information"));
Коды.Добавить(НовыйКодHTTP(204, "НетСодержимого_204", "No Content"));
Коды.Добавить(НовыйКодHTTP(205, "СброситьСодержимое_205", "Reset Content"));
Коды.Добавить(НовыйКодHTTP(206, "ЧастичноеСодержимое_206", "Partial Content"));
Коды.Добавить(НовыйКодHTTP(207, "Многостатусный_207", "Multi-Status"));
Коды.Добавить(НовыйКодHTTP(208, "УжеСообщалось_208", "Already Reported"));
Коды.Добавить(НовыйКодHTTP(226, "ИспользованоIM_226", "IM Used"));

Коды.Добавить(НовыйКодHTTP(300, "МножествоВыборов_300", "Multiple Choices"));
Коды.Добавить(НовыйКодHTTP(301, "ПеремещеноНавсегда_301", "Moved Permanently"));
Коды.Добавить(НовыйКодHTTP(302, "ПеремещеноВременно_302", "Moved Temporarily"));
Коды.Добавить(НовыйКодHTTP(303, "СмотретьДругое_303", "See Other"));
Коды.Добавить(НовыйКодHTTP(304, "НеИзменялось_304", "Not Modified"));
Коды.Добавить(НовыйКодHTTP(305, "ИспользоватьПрокси_305", "Use Proxy"));
Коды.Добавить(НовыйКодHTTP(307, "ВременноеПеренаправление_307", "Temporary Redirect"));
Коды.Добавить(НовыйКодHTTP(308, "ПостоянноеПеренаправление_308", "Permanent Redirect"));

Коды.Добавить(НовыйКодHTTP(400, "НеверныйЗапрос_400", "Bad Request"));
Коды.Добавить(НовыйКодHTTP(401, "НеАвторизован_401", "Unauthorized"));
Коды.Добавить(НовыйКодHTTP(402, "НеобходимаОплата_402", "Payment Required"));
Коды.Добавить(НовыйКодHTTP(403, "Запрещено_403", "Forbidden"));
Коды.Добавить(НовыйКодHTTP(404, "НеНайдено_404", "Not Found"));
Коды.Добавить(НовыйКодHTTP(405, "МетодНеПоддерживается_405", "Method Not Allowed"));
Коды.Добавить(НовыйКодHTTP(406, "Неприемлемо_406", "Not Acceptable"));
Коды.Добавить(НовыйКодHTTP(407, "НеобходимаАутентификацияПрокси_407", "Proxy Authentication Required"));
Коды.Добавить(НовыйКодHTTP(408, "ИстеклоВремяОжидания_408", "Request Timeout"));
Коды.Добавить(НовыйКодHTTP(409, "Конфликт_409", "Conflict"));
Коды.Добавить(НовыйКодHTTP(410, "Удален_410", "Gone"));
Коды.Добавить(НовыйКодHTTP(411, "НеобходимаДлина_411", "Length Required"));
Коды.Добавить(НовыйКодHTTP(412, "УсловиеЛожно_412", "Precondition Failed"));
Коды.Добавить(НовыйКодHTTP(413, "ПолезнаяНагрузкаСлишкомВелика_413", "Payload Too Large"));
Коды.Добавить(НовыйКодHTTP(414, "СлишкомДлинныйURI_414", "URI Too Long"));
Коды.Добавить(НовыйКодHTTP(415, "НеподдерживаемыйТипДанных_415", "Unsupported Media Type"));
Коды.Добавить(НовыйКодHTTP(416, "ДиапазонНеДостижим_416", "Range Not Satisfiable"));
Коды.Добавить(НовыйКодHTTP(417, "ОжиданиеНеУдалось_417", "Expectation Failed"));
Коды.Добавить(НовыйКодHTTP(419, "ОшибкаПроверкиCSRF_419", "Authentication Timeout"));
Коды.Добавить(НовыйКодHTTP(421, "НеправильноНаправленныйЗапрос_421", "Misdirected Request"));
Коды.Добавить(НовыйКодHTTP(422, "НеобрабатываемыйЭкземпляр_422", "Unprocessable Entity"));
Коды.Добавить(НовыйКодHTTP(423, "Заблокировано_423", "Locked"));
Коды.Добавить(НовыйКодHTTP(424, "НевыполненнаяЗависимость_424", "Failed Dependency"));
Коды.Добавить(НовыйКодHTTP(425, "СлишкомРано_425", "Too Early"));
Коды.Добавить(НовыйКодHTTP(426, "НеобходимоОбновление_426", "Upgrade Required"));
Коды.Добавить(НовыйКодHTTP(428, "НеобходимоПредусловие_428", "Precondition Required"));
Коды.Добавить(НовыйКодHTTP(429, "СлишкомМногоЗапросов_429", "Too Many Requests"));
Коды.Добавить(НовыйКодHTTP(431, "ПоляЗаголовкаЗапросаСлишкомБольшие_431", "Request Header Fields Too Large"));
Коды.Добавить(НовыйКодHTTP(449, "ПовторитьС_449", "Retry With"));
Коды.Добавить(НовыйКодHTTP(451, "НедоступноПоЮридическимПричинам_451", "Unavailable For Legal Reasons"));
Коды.Добавить(НовыйКодHTTP(499, "КлиентЗакрылСоединение_499", "Client Closed Request"));

Коды.Добавить(НовыйКодHTTP(500, "ВнутренняяОшибкаСервера_500", "Internal Server Error"));
Коды.Добавить(НовыйКодHTTP(501, "НеРеализовано_501", "Not Implemented"));
Коды.Добавить(НовыйКодHTTP(502, "ОшибочныйШлюз_502", "Bad Gateway"));
Коды.Добавить(НовыйКодHTTP(503, "СервисНедоступен_503", "Service Unavailable"));
Коды.Добавить(НовыйКодHTTP(504, "ШлюзНеОтвечает_504", "Gateway Timeout"));
Коды.Добавить(НовыйКодHTTP(505, "ВерсияHTTPНеПоддерживается_505", "HTTP Version Not Supported"));
Коды.Добавить(НовыйКодHTTP(506, "ВариантТожеПроводитСогласование_506", "Variant Also Negotiates"));
Коды.Добавить(НовыйКодHTTP(507, "ПереполнениеХранилища_507", "Insufficient Storage"));
Коды.Добавить(НовыйКодHTTP(508, "ОбнаруженоБесконечноеПеренаправление_508", "Loop Detected"));
Коды.Добавить(НовыйКодHTTP(509, "ИсчерпанаПропускнаяШиринаКанала_509", "Bandwidth Limit Exceeded"));
Коды.Добавить(НовыйКодHTTP(510, "НеРасширено_510", "Not Extended"));
Коды.Добавить(НовыйКодHTTP(511, "ТребуетсяСетеваяАутентификация_511", "Network Authentication Required"));
Коды.Добавить(НовыйКодHTTP(520, "НеизвестнаяОшибка_520", "Unknown Error"));
Коды.Добавить(НовыйКодHTTP(521, "ВебСерверНеРаботает_521", "Web Server Is Down"));
Коды.Добавить(НовыйКодHTTP(522, "СоединениеНеОтвечает_522", "Connection Timed Out"));
Коды.Добавить(НовыйКодHTTP(523, "ИсточникНедоступен_523", "Origin Is Unreachable"));
Коды.Добавить(НовыйКодHTTP(524, "ВремяОжиданияИстекло_524", "A Timeout Occurred"));
Коды.Добавить(НовыйКодHTTP(525, "КвитированиеSSНеУдалось_525", "SSL Handshake Failed"));
Коды.Добавить(НовыйКодHTTP(526, "НедействительныйСертификатSSL_526", "Invalid SSL Certificate"));

Возврат Коды;

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

Функция НовыйКодHTTP(Код, Ключ, Описание)

Возврат Новый Структура("Код, Ключ, Описание", Код, Ключ, Описание);

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

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

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

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

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

Область Прочие

Функция ОпределитьХешФункцию(Знач Алгоритм)

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

Возврат АлгоритмХеширования;

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

Функция ХешированиеДанных(Знач Алгоритм, Знач Данные)

Если ТипЗнч(Данные) = Тип("Строка") Тогда
    Данные = ПолучитьДвоичныеДанныеИзСтроки(Данные, КодировкаТекста.UTF8, Ложь);
КонецЕсли;

Хеширование = Новый ХешированиеДанных(Алгоритм);
Хеширование.Добавить(Данные);

Возврат НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма));

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

Процедура Приостановить(ДлительностьОстановкиВСекундах)

// Когда-нибудь в платформе сделают паузу и это можно будет выкинуть

Если ДлительностьОстановкиВСекундах < 1 Тогда
    Возврат;
КонецЕсли;

ТекущаяДата = ТекущаяУниверсальнаяДата();
ЖдатьДо = ТекущаяДата + ДлительностьОстановкиВСекундах;

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

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

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

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

    Если Длительность = 0 Тогда
        Дата = ДатаИзСтрокиRFC7231(ЗаголовокRetryAfter);
        Если ЗначениеЗаполнено(Дата) Тогда
            Длительность = Дата - ТекущаяУниверсальнаяДата();
        КонецЕсли;

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

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

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

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

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

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

Область УниверсальныеСтруктурыДанных

Функция ВыбратьЗначение(ОсновноеЗначение, ДополнительныеЗначения, Ключ, ЗначениеПоУмолчанию)

Если ОсновноеЗначение <> Неопределено Тогда
    Возврат ОсновноеЗначение;
КонецЕсли;

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

Возврат ЗначениеПоУмолчанию;

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

Функция ЗначениеПоКлючу(Структура, Ключ, ЗначениеПоУмолчанию = Неопределено)

Если ТипЗнч(Структура) = Тип("Структура") И Структура.Свойство(Ключ) Тогда
    Значение = Структура[Ключ];
ИначеЕсли ТипЗнч(Структура) = Тип("Соответствие") И Структура.Получить(Ключ) <> Неопределено Тогда
    Значение = Структура.Получить(Ключ);
Иначе
    Значение = ЗначениеПоУмолчанию;
КонецЕсли;

Возврат Значение;

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

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

Область РаботаСоСтроками

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

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

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

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

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

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

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

Разделители = ",-:/\.";
Для Индекс = 1 По СтрДлина(Разделители) Цикл
    Разделитель = Сред(Разделители, Индекс, 1);
    Строка = СтрЗаменить(Строка, Разделитель, " ");
КонецЦикла;
Строка = СтрЗаменить(Строка, "  ", " ");
СоставляющиеДаты = СтрРазделить(Строка, " ");

Если СоставляющиеДаты.Количество() < 7 Тогда
    Возврат '00010101';
КонецЕсли;

МесяцСтр = СоставляющиеДаты[2];

Месяцы = СтрРазделить("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", ",");
Месяц = Месяцы.Найти(МесяцСтр);
Если Месяц = Неопределено Тогда
    Возврат '00010101';
КонецЕсли;

Дата = СоставляющиеДаты[3] + Формат(Месяц + 1, "ЧЦ=2; ЧВН=;") + СоставляющиеДаты[1];
Время = СоставляющиеДаты[4] + СоставляющиеДаты[5] + СоставляющиеДаты[6];

Возврат ДатаИзСтроки(Дата + Время);

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

Процедура РазбитьСтрокуПоРазделителю(ИзвлекаемаяЧасть, ОстальнаяЧасть, Разделитель, Инверсия = Ложь)

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

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

Функция РазделитьПоПервомуНайденномуРазделителю(Строка, Разделители)

МинимальныйИндекс = СтрДлина(Строка);
ПервыйРазделитель = "";

Для Каждого Разделитель Из Разделители Цикл
    Индекс = СтрНайти(Строка, Разделитель);
    Если Индекс = 0 Тогда
        Продолжить;
    КонецЕсли;
    Если Индекс < МинимальныйИндекс Тогда
        МинимальныйИндекс = Индекс;
        ПервыйРазделитель = Разделитель;
    КонецЕсли;
КонецЦикла;

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

Возврат Результат;

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

Функция РазбитьСтрокуПоСтроке(Знач Строка, Разделитель)

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

    ПерваяЧасть = Лев(Строка, Позиция - СтрДлина(Разделитель) + 1);
    Результат.Добавить(ПерваяЧасть);
    Строка = Сред(Строка, Позиция + СтрДлина(Разделитель));
КонецЦикла;

Возврат Результат;

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

Функция ДобавитьЛидирующуюТочку(Знач Домен)

Если Не СтрНачинаетсяС(Домен, ".") Тогда
    Домен = "." + Домен;
КонецЕсли;

Возврат Домен;

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

Функция ВырезатьТекст(Текст, МаксимальнаяДлинаТекста = 1000)

Если НайтиНедопустимыеСимволыXML(Текст) Тогда
    Возврат НСтр("ru ='<Данные>'");
КонецЕсли;

Если СтрДлина(Текст) <= МаксимальнаяДлинаТекста Тогда
    Результат = Текст;
Иначе
    ПоловинаМаксимальнойДлиныТекста = МаксимальнаяДлинаТекста / 2;
    Результат = Лев(Текст, ПоловинаМаксимальнойДлиныТекста);
    Результат = Результат + Символы.ПС + "..." + Символы.ПС;
    Результат = Результат + Прав(Текст, ПоловинаМаксимальнойДлиныТекста);
КонецЕсли;

Возврат Результат;

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

Функция Объединить(ГлавныйИсточник, ДополнительныйИсточник)

Результат = ГлавныйИсточник;
Дополнить(ГлавныйИсточник, ДополнительныйИсточник);
Возврат Результат;

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

Функция Скопировать(Источник)

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

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

Процедура Дополнить(Приемник, Источник)

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

Для Каждого ЭлементИсточника Из Источник Цикл
    ПараметрНайден = Ложь;

    Если ТипЗнч(Приемник) = Тип("Соответствие") Тогда
        ПараметрНайден = Приемник.Получить(ЭлементИсточника.Ключ) <> Неопределено;
    КонецЕсли;

    Если ТипЗнч(Приемник) = Тип("Структура") Тогда
        ПараметрНайден = Приемник.Свойство(ЭлементИсточника.Ключ);
    КонецЕсли;

    Если Не ПараметрНайден ИЛИ ПараметрНайден И ЭлементИсточника.Значение <> Неопределено Тогда
        Приемник.Вставить(ЭлементИсточника.Ключ, ЭлементИсточника.Значение);
    КонецЕсли;
КонецЦикла;

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

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

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *