Skip to content

Instantly share code, notes, and snippets.

@Kirill
Created November 11, 2020 18:15
Show Gist options
  • Save Kirill/590a855beb850cb184587c8916d56d4f to your computer and use it in GitHub Desktop.
Save Kirill/590a855beb850cb184587c8916d56d4f to your computer and use it in GitHub Desktop.
// Коннектор: удобный HTTP-клиент для 1С:Предприятие 8
//
// Copyright 2017-2020 Vladimir Bondarevskiy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
// URL: https://github.com/vbondarevsky/Connector
// e-mail: vbondarevsky@gmail.com
// Версия: 2.1.3
//
// Требования: платформа 1С версии 8.3.10 и выше
#Область ПрограммныйИнтерфейс
#Область МетодыHTTP
#Область МетодыОбщегоНазначения
// Отправляет GET запрос
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// ПараметрыЗапроса - Структура, Соответствие - параметры, которые будут отправлены в URL (часть после ?).
// См. описание Сессия.ПараметрыЗапроса.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Get(URL, ПараметрыЗапроса = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, ПараметрыЗапроса, Неопределено, Неопределено);
Возврат ВызватьHTTPМетод(ТекущаяСессия, "GET", URL, ДополнительныеПараметры);
КонецФункции
// Отправляет OPTIONS запрос
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Options(URL, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Неопределено);
Возврат ВызватьHTTPМетод(ТекущаяСессия, "OPTIONS", URL, ДополнительныеПараметры);
КонецФункции
// Отправляет HEAD запрос
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Head(URL, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Неопределено);
Возврат ВызватьHTTPМетод(ТекущаяСессия, "HEAD", URL, ДополнительныеПараметры);
КонецФункции
// Отправляет POST запрос
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// Данные - Структура, Соответствие, Строка, ДвоичныеДанные - см. описание ДополнительныеПараметры.Данные.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Post(URL, Данные = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Данные, Неопределено);
Возврат ВызватьHTTPМетод(ТекущаяСессия, "POST", URL, ДополнительныеПараметры);
КонецФункции
// Отправляет PUT запрос
//
// Параметры:
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// Данные - Структура, Соответствие, Строка, ДвоичныеДанные - см. описание ДополнительныеПараметры.Данные.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Put(URL, Данные = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Данные, Неопределено);
Возврат ВызватьHTTPМетод(ТекущаяСессия, "PUT", URL, ДополнительныеПараметры);
КонецФункции
// Отправляет PATCH запрос
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// Данные - Структура, Соответствие, Строка, ДвоичныеДанные - см. описание ДополнительныеПараметры.Данные.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Patch(URL, Данные = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Данные, Неопределено);
Возврат ВызватьHTTPМетод(ТекущаяСессия, "PATCH", URL, ДополнительныеПараметры);
КонецФункции
// Отправляет DELETE запрос
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// Данные - Структура, Соответствие, Строка, ДвоичныеДанные - см. описание ДополнительныеПараметры.Данные.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Структура - ответ на выполненный запрос. См. описание возвращаемого значения в ВызватьМетод.
//
Функция Delete(URL, Данные = Неопределено, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Данные, Неопределено);
Возврат ВызватьHTTPМетод(ТекущаяСессия, "DELETE", URL, ДополнительныеПараметры);
КонецФункции
#КонецОбласти
#Область УпрощенныеМетодыДляРаботыСЗапросамиВФорматеJSON
// Отправляет GET запрос
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// ПараметрыЗапроса - Структура, Соответствие - параметры, которые будут отправлены в URL (часть после ?).
// См. описание Сессия.ПараметрыЗапроса.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Соответствие, Структура - ответ, десериализованный из JSON.
// Параметры преобразования см. ДополнительныеПараметры.ПараметрыПреобразованияJSON.
//
Функция GetJson(URL,
ПараметрыЗапроса = Неопределено,
ДополнительныеПараметры = Неопределено,
Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, ПараметрыЗапроса, Неопределено, Неопределено);
ПараметрыПреобразованияJSON =
ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыПреобразованияJSON", Неопределено);
Возврат КакJson(ВызватьHTTPМетод(ТекущаяСессия, "GET", URL, ДополнительныеПараметры), ПараметрыПреобразованияJSON);
КонецФункции
// Отправляет POST запрос
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// Json - Структура, Соответствие - данные, которые необходимо сериализовать в JSON.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Соответствие, Структура - ответ, десериализованный из JSON.
// Параметры преобразования см. ДополнительныеПараметры.ПараметрыПреобразованияJSON
//
Функция PostJson(URL, Json, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Json);
ПараметрыПреобразованияJSON =
ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыПреобразованияJSON", Неопределено);
Возврат КакJson(ВызватьHTTPМетод(ТекущаяСессия, "POST", URL, ДополнительныеПараметры), ПараметрыПреобразованияJSON);
КонецФункции
// Отправляет PUT запрос
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// Json - Структура, Соответствие - данные, которые необходимо сериализовать в JSON.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Соответствие, Структура - ответ, десериализованный из JSON.
// Параметры преобразования см. ДополнительныеПараметры.ПараметрыПреобразованияJSON
//
Функция PutJson(URL, Json, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Json);
ПараметрыПреобразованияJSON =
ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыПреобразованияJSON", Неопределено);
Возврат КакJson(ВызватьHTTPМетод(ТекущаяСессия, "PUT", URL, ДополнительныеПараметры), ПараметрыПреобразованияJSON);
КонецФункции
// Отправляет DELETE запрос
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// Json - Структура, Соответствие - данные, которые необходимо сериализовать в JSON.
// ДополнительныеПараметры - Структура - см. описание параметра в ВызватьМетод.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Соответствие, Структура - ответ, десериализованный из JSON.
// Параметры преобразования см. ДополнительныеПараметры.ПараметрыПреобразованияJSON
//
Функция DeleteJson(URL, Json, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Json);
ПараметрыПреобразованияJSON =
ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыПреобразованияJSON", Неопределено);
Возврат КакJson(ВызватьHTTPМетод(ТекущаяСессия, "DELETE", URL, ДополнительныеПараметры), ПараметрыПреобразованияJSON);
КонецФункции
#КонецОбласти
// Отправляет данные на указанный адрес для обработки с использованием указанного HTTP-метода.
//
// Параметры:
// Метод - Строка - имя HTTP-метода для запроса.
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
// ДополнительныеПараметры - Структура - позволяет задать дополнительные параметры:
// *Заголовки - Соответствие - см. описание Сессия.Заголовки.
// *Аутентификация - Структура - см. описание Сессия.Аутентификация
// *Прокси - ИнтернетПрокси - см. описание Сессия.Прокси.
// *ПараметрыЗапроса - Структура, Соответствие - см. описание Сессия.ПараметрыЗапроса.
// *ПроверятьSSL - Булево - см. описание Сессия.ПроверятьSSL.
// *КлиентскийСертификатSSL - см. описание Сессия.КлиентскийСертификатSSL.
// *Cookies - Массив - см. описание Сессия.Cookies.
// *Таймаут - Число - время ожидания осуществляемого соединения и операций, в секундах.
// Значение по умолчанию - 30 сек.
// *РазрешитьПеренаправление - Булево - Истина - редиректы будут автоматически разрешены.
// Ложь - будет выполнен только один запрос к серверу.
// *Json - Структура, Соответствие - данные, которые необходимо сериализовать в JSON.
// *ПараметрыПреобразованияJSON - Структура - задает параметры преобразования JSON:
// **ПрочитатьВСоответствие - Булево - Если Истина, чтение объекта JSON будет выполнено в Соответствие.
// Если Ложь, объекты будут считываться в объект типа Структура.
// **ФорматДатыJSON - ФорматДатыJSON - формат, в котором представлена дата в строке,
// подлежащей преобразованию.
// **ИменаСвойствСоЗначениямиДата - Массив, Строка - имена свойств JSON,
// для которых нужно вызывать восстановление даты из строки.
// *ПараметрыЗаписиJSON - Структура - параметры, используемые при записи объекта JSON.
// См. в синтаксис-помощнике описание ПараметрыЗаписиJSON.
// *Данные - Структура, Соответствие - поля формы, которые необходимо отправить в запрос:
// **<Ключ> - Строка - имя поля.
// **<Значение> - Строка - значение поля.
// - Строка, ДвоичныеДанные - произвольные данные, которые необходимо отправить в запросе.
// *Файлы - Структура, Массив - файлы, которые необходимо отправить в запросе:
// **Имя - Строка - имя поля формы.
// **Данные - ДвоичныеДанные - двоичные данные файла.
// **ИмяФайла - Строка - имя файла.
// **Тип - Строка - MIME-тип файла.
// **Заголовки - Соответствие, Неопределено - HTTP заголовки запроса.
// *МаксимальноеКоличествоПовторов - Число - количество повторных попыток соединения/отправки запроса.
// Между попытками выполняется задержка, растущая по экспоненте.
// Но если код состояния один из 413, 429, 503
// и в ответе есть заголовок Retry-After,
// то время задержки формируется из значения этого заголовка
// Значение по умолчанию: 0 - повторы не выполняются.
// *МаксимальноеВремяПовторов - Число - максимальное общее время (в секундах) отправки запроса с учетом повторов.
// Значение по умолчанию: 600.
// *КоэффициентЭкспоненциальнойЗадержки - Число - коэффициент изменения экспоненциальной задержки.
// 1 формирует последовательность задержек: 1, 2, 4, 8 и т.д.
// 2 формируется последовательность задержек: 2, 4, 8, 16 и т.д.
// ...
// Значение по умолчанию: 1.
// *ПовторятьДляКодовСостояний - Неопределено - повторы будут выполняться для кодов состояний >= 500.
// - Массив - повторы будут выполняться для конкретных кодов состояний.
// Значение по умолчанию: Неопределено.
// Сессия - Структура - см. возвращаемое значение функции СоздатьСессию.
//
// Возвращаемое значение:
// Структура - ответ на выполненный запрос:
// *ВремяВыполнения - Число - время выполнения запроса в миллисекундах.
// *Cookies - Соответствие - cookies полученные с сервера.
// *Заголовки - Соответствие - HTTP заголовки ответа.
// *ЭтоПостоянныйРедирект - Булево - признак постоянного редиректа.
// *ЭтоРедирект - Булево - признак редиректа.
// *Кодировка - Строка - кодировка текста ответа.
// *Тело - ДвоичныеДанные - тело ответа.
// *КодСостояния - Число - код состояния ответа.
// *URL - Строка - итоговый URL, по которому был выполнен запрос.
//
Функция ВызватьМетод(Метод, URL, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт
ТекущаяСессия = ТекущаяСессия(Сессия);
ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Неопределено);
Возврат ВызватьHTTPМетод(ТекущаяСессия, Метод, URL, ДополнительныеПараметры);
КонецФункции
// Создает объект для хранения параметров сессии.
//
// Возвращаемое значение:
// Структура - параметры сессии:
// *Заголовки - Соответствие - HTTP заголовки запроса.
// *Аутентификация - Структура - параметры аутентификации запроса.
// *ИспользоватьАутентификациюОС - Булево - включает использование аутентификации NTLM или Negotiate.
// Значение по умолчанию: Ложь.
// *Тип - Строка - тип аутентификации. Для Basic Тип можно не указывать.
// Если Тип = Digest или Basic:
// *Пользователь - Строка - имя пользователя.
// *Пароль - Строка - пароль пользователя.
// Если Тип = AWS4-HMAC-SHA256:
// *ИдентификаторКлючаДоступа - Строка - идентификатор ключа доступа.
// *СекретныйКлюч - Строка - секретный ключ.
// *Сервис - Строка - сервис, к которому выполняется подключение.
// *Регион - Строка - регион, к которому выполняется подключение.
// *Прокси - ИнтернетПрокси - параметры прокси, которые будут использованы при отправке запроса.
// Значение по умолчанию: Неопределено. При этом если в конфигурации используется БСП,
// то значения прокси будет взято из БСП.
// *ПараметрыЗапроса - Структура, Соответствие - параметры, которые будут отправлены в URL (часть после ?):
// *<Ключ> - Строка - ключ параметра в URL.
// *<Значение> - Строка - значение параметра URL
// - Массив - сформирует строку из нескольких параметров: key=value1&key=value2 и т.д.
// *ПроверятьSSL - Булево - Ложь - проверка сертификата сервера не выполняется.
// - Истина - используется значение СертификатыУдостоверяющихЦентровОС.
// - СертификатыУдостоверяющихЦентровФайл - см. в синтаксис-помощнике описание
// СертификатыУдостоверяющихЦентровФайл.
// Значение по умолчанию: Истина.
// *КлиентскийСертификатSSL - СертификатКлиентаФайл - см. в синтаксис-помощнике описание СертификатКлиентаФайл.
// - СертификатКлиентаWindows - см. в синтаксис-помощнике описание
// СертификатКлиентаWindows.
// Значение по умолчанию: Неопределено.
// *МаксимальноеКоличествоПеренаправлений - Число - максимальное количество редиректов. Защита от зацикливания.
// Значение по умолчанию:
// см. функцию МаксимальноеКоличествоПеренаправлений
// *Cookies - Соответствие - хранилище cookies.
//
Функция СоздатьСессию() Экспорт
Сессия = Новый Структура;
Сессия.Вставить("Заголовки", ЗаголовкиПоУмолчанию());
Сессия.Вставить("Аутентификация", Неопределено);
Сессия.Вставить("Прокси", Неопределено);
Сессия.Вставить("ПараметрыЗапроса", Новый Структура);
Сессия.Вставить("ПроверятьSSL", Истина);
Сессия.Вставить("КлиентскийСертификатSSL", Неопределено);
Сессия.Вставить("МаксимальноеКоличествоПеренаправлений", МаксимальноеКоличествоПеренаправлений());
Сессия.Вставить("Cookies", Новый Соответствие);
Сессия.Вставить("СлужебныеДанные", Новый Структура("ПараметрыDigest"));
Возврат Сессия;
КонецФункции
#КонецОбласти
#Область ФорматыОтветов
// Возвращает ответ сервера в виде десериализованного значения JSON.
//
// Параметры:
// Ответ - Структура - ответ сервера на отправленный запрос.
// См. описание возвращаемого значения ВызватьМетод.
// ПараметрыПреобразованияJSON - Структура - задает параметры преобразования JSON.
// *ПрочитатьВСоответствие - Булево - Если Истина, чтение объекта JSON будет выполнено в Соответствие.
// Если Ложь, объекты будут считываться в объект типа Структура.
// *ФорматДатыJSON - ФорматДатыJSON - формат, в котором представлена дата в строке, подлежащей преобразованию.
// *ИменаСвойствСоЗначениямиДата - Массив, Строка - имена свойств JSON,
// для которых нужно вызывать восстановление даты из строки.
//
// Возвращаемое значение:
// Соответствие - ответ сервера в виде десериализованного значения JSON.
// Если ПараметрыПреобразования.ПрочитатьВСоответствие = Истина (по умолчанию).
// Структура - если ПараметрыПреобразования.ПрочитатьВСоответствие = Ложь.
//
Функция КакJson(Ответ, ПараметрыПреобразованияJSON = Неопределено) Экспорт
Возврат JsonВОбъект(РаспаковатьОтвет(Ответ), Ответ.Кодировка, ПараметрыПреобразованияJSON);
КонецФункции
// Возвращает ответ сервера в виде текста.
//
// Параметры:
// Ответ - Структура - ответ сервера на отправленный запрос.
// См. описание возвращаемого значения ВызватьМетод.
// Кодировка - Строка, КодировкаТекста - определяет кодировку текста.
// Если значение не задано, то кодировка извлекается из Ответ.Кодировка.
//
// Возвращаемое значение:
// Строка - ответ сервера в виде текста.
//
Функция КакТекст(Ответ, Кодировка = Неопределено) Экспорт
Если Не ЗначениеЗаполнено(Кодировка) Тогда
Кодировка = Ответ.Кодировка;
КонецЕсли;
ЧтениеТекста = Новый ЧтениеТекста(РаспаковатьОтвет(Ответ).ОткрытьПотокДляЧтения(), Кодировка);
Текст = ЧтениеТекста.Прочитать();
ЧтениеТекста.Закрыть();
Если Текст = Неопределено Тогда
Текст = "";
КонецЕсли;
Возврат Текст;
КонецФункции
// Возвращает ответ сервера в двоичных данных.
//
// Параметры:
// Ответ - Структура - ответ сервера на отправленный запрос.
// См. описание возвращаемого значения ВызватьМетод.
//
// Возвращаемое значение:
// Строка - ответ сервера в виде двоичных данных.
//
Функция КакДвоичныеДанные(Ответ) Экспорт
Возврат РаспаковатьОтвет(Ответ);
КонецФункции
#КонецОбласти
#Область ВспомогательныеФункции
// Возвращает структурированное представление URL.
//
// Параметры:
// URL - Строка - URL ресурса, к которому будет отправлен запрос.
//
// Возвращаемое значение:
// Структура - структура URL:
// *Схема - Строка - схема обращения к серверу (http, https).
// *Аутентификация - Структура - параметры аутентификации:
// *Пользователь - Строка - имя пользователя.
// *Пароль - Строка - пароль пользователя.
// *Сервер - Строка - адрес сервера.
// *Порт - Число - порт сервера.
// *Путь - Строка - адрес ресурса на сервере.
// *ПараметрыЗапроса - Соответствие - параметры запроса передаваемые на сервер в URL (часть после ?):
// *<Ключ> - Строка - ключ параметра в URL.
// *<Значение> - Строка - значение параметра URL;
// - Массив - значения параметра (key=value1&key=value2).
// *Фрагмент - Строка - часть URL после #.
//
Функция РазобратьURL(Знач URL) Экспорт
Схема = "";
Путь = "";
Аутентификация = Новый Структура("Пользователь, Пароль", "", "");
Сервер = "";
Порт = "";
Фрагмент = "";
ДопустимыеСхемы = СтрРазделить("http,https", ",");
URLБезСхемы = URL;
РазбитьСтрокуПоРазделителю(Схема, URLБезСхемы, "://");
Если ДопустимыеСхемы.Найти(НРег(Схема)) <> Неопределено Тогда
URL = URLБезСхемы;
Иначе
Схема = "";
КонецЕсли;
Результат = РазделитьПоПервомуНайденномуРазделителю(URL, СтрРазделить("/,?,#", ","));
URL = Результат[0];
Если ЗначениеЗаполнено(Результат[2]) Тогда
Путь = Результат[2] + Результат[1];
КонецЕсли;
АутентификацияСтрока = "";
РазбитьСтрокуПоРазделителю(АутентификацияСтрока, URL, "@");
Если ЗначениеЗаполнено(АутентификацияСтрока) Тогда
АутентификацияЧасти = СтрРазделить(АутентификацияСтрока, ":");
Аутентификация.Пользователь = АутентификацияЧасти[0];
Аутентификация.Пароль = АутентификацияЧасти[1];
КонецЕсли;
// IPv6
РазбитьСтрокуПоРазделителю(Сервер, URL, "]");
Если ЗначениеЗаполнено(Сервер) Тогда
Сервер = Сервер + "]";
КонецЕсли;
URL = СтрЗаменить(URL, "/", "");
РазбитьСтрокуПоРазделителю(Порт, URL, ":", Истина);
Если Не ЗначениеЗаполнено(Сервер) Тогда
Сервер = URL;
КонецЕсли;
Если ЗначениеЗаполнено(Порт) Тогда
Порт = Число(Порт);
Иначе
Порт = 0;
КонецЕсли;
РазбитьСтрокуПоРазделителю(Фрагмент, Путь, "#", Истина);
ПараметрыЗапроса = ЗаполнитьПараметрыЗапроса(Путь);
Если Не ЗначениеЗаполнено(Схема) Тогда
Схема = "http";
КонецЕсли;
Путь = ?(ЗначениеЗаполнено(Путь), Путь, "/");
Результат = Новый Структура;
Результат.Вставить("Схема", Схема);
Результат.Вставить("Аутентификация", Аутентификация);
Результат.Вставить("Сервер", Сервер);
Результат.Вставить("Порт", Порт);
Результат.Вставить("Путь", Путь);
Результат.Вставить("ПараметрыЗапроса", ПараметрыЗапроса);
Результат.Вставить("Фрагмент", Фрагмент);
Возврат Результат;
КонецФункции
// Преобразование Объекта в JSON.
//
// Параметры:
// Объект - Произвольный - данные, которые необходимо преобразовать в JSON.
// ПараметрыПреобразования - Структура - кодировка текста JSON. Значение по умолчанию - utf-8.
// *ФорматДатыJSON - ФорматДатыJSON - определяет формат сериализации дат JSON-объектов.
// ПараметрыЗаписи - Структура - параметры преобразования JSON:
// *ПереносСтрок - ПереносСтрокJSON - определяет способ переноса строк,
// который будет использован при записи данных JSON.
// *СимволыОтступа - Строка - определяет символы отступа, используемые при записи данных JSON.
// *ИспользоватьДвойныеКавычки - Булево - определяет, будут ли при записи имена свойств JSON
// записываться в двойных кавычках.
// *ЭкранированиеСимволов - ЭкранированиеСимволовJSON - определяет используемый способ экранирования (замены)
// символов при записи данных JSON.
// *ЭкранироватьУгловыеСкобки - Булево - определяет, будут ли при записи экранироваться символы "<" и ">".
// *ЭкранироватьРазделителиСтрок - Булево - определяет, будут ли экранироваться разделители строк
// U+2028 (line-separator) и U+2029 (page-separator).
// *ЭкранироватьАмперсанд - Булево - определяет, будет ли при записи экранироваться символ амперсанда "&".
// *ЭкранироватьОдинарныеКавычки - Булево - определяет, будут ли экранироваться одинарные кавычки.
// *ЭкранироватьСлеш - Булево - определяет, будет ли экранироваться слеш (косая черта) при записи значения.
//
// Возвращаемое значение:
// Строка - объект в формате JSON.
//
Функция ОбъектВJson(Объект, Знач ПараметрыПреобразования = Неопределено, Знач ПараметрыЗаписи = Неопределено) Экспорт
ПараметрыПреобразованияJSON = ДополнитьПараметрыПреобразованияJSON(ПараметрыПреобразования);
НастройкиСериализации = Новый НастройкиСериализацииJSON;
НастройкиСериализации.ФорматСериализацииДаты = ПараметрыПреобразованияJSON.ФорматДатыJSON;
ПараметрыЗаписи = ДополнитьПараметрыЗаписиJSON(ПараметрыЗаписи);
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(
ПараметрыЗаписи.ПереносСтрок,
ПараметрыЗаписи.СимволыОтступа,
ПараметрыЗаписи.ИспользоватьДвойныеКавычки,
ПараметрыЗаписи.ЭкранированиеСимволов,
ПараметрыЗаписи.ЭкранироватьУгловыеСкобки,
ПараметрыЗаписи.ЭкранироватьРазделителиСтрок,
ПараметрыЗаписи.ЭкранироватьАмперсанд,
ПараметрыЗаписи.ЭкранироватьОдинарныеКавычки,
ПараметрыЗаписи.ЭкранироватьСлеш);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписиJSON);
ЗаписатьJSON(ЗаписьJSON, Объект, НастройкиСериализации);
Возврат ЗаписьJSON.Закрыть();
КонецФункции
// Преобразование JSON в Объект.
//
// Параметры:
// Json - Поток, ДвоичныеДанные, Строка - данные в формате JSON.
// Кодировка - Строка - кодировка текста JSON. Значение по умолчанию - utf-8.
// ПараметрыПреобразования - Структура - параметры преобразования 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.Закрыть();
Возврат Объект;
КонецФункции
// Вычисляет HMAC (hash-based message authentication code).
//
// Параметры:
// Ключ - ДвоичныеДанные - секретный ключ.
// Данные - ДвоичныеДанные - данные, для которых нужно посчитать HMAC.
// Алгоритм - ХешФункция - алгоритм, используемый для вычисления хеша.
//
// Возвращаемое значение:
// ДвоичныеДанные - вычисленное значение HMAC.
//
Функция HMAC(Ключ, Данные, Алгоритм) Экспорт
ДлинаБлока = 64;
Если Ключ.Размер() > ДлинаБлока Тогда
Хеширование = Новый ХешированиеДанных(Алгоритм);
Хеширование.Добавить(Ключ);
КлючБуфер = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Хеширование.ХешСумма);
Иначе
КлючБуфер = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Ключ);
КонецЕсли;
ИзмененныйКлюч = Новый БуферДвоичныхДанных(ДлинаБлока);
ИзмененныйКлюч.Записать(0, КлючБуфер);
ВнутреннийКлюч = ИзмененныйКлюч.Скопировать();
ВнешнийКлюч = ИзмененныйКлюч;
ВнутреннееВыравнивание = Новый БуферДвоичныхДанных(ДлинаБлока);
ВнешнееВыравнивание = Новый БуферДвоичныхДанных(ДлинаБлока);
Для Индекс = 0 По ДлинаБлока - 1 Цикл
ВнутреннееВыравнивание.Установить(Индекс, 54);
ВнешнееВыравнивание.Установить(Индекс, 92);
КонецЦикла;
ВнутреннееХеширование = Новый ХешированиеДанных(Алгоритм);
ВнешнееХеширование = Новый ХешированиеДанных(Алгоритм);
ВнутреннийКлюч.ЗаписатьПобитовоеИсключительноеИли(0, ВнутреннееВыравнивание);
ВнешнийКлюч.ЗаписатьПобитовоеИсключительноеИли(0, ВнешнееВыравнивание);
ВнешнееХеширование.Добавить(ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ВнешнийКлюч));
ВнутреннееХеширование.Добавить(ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ВнутреннийКлюч));
Если ЗначениеЗаполнено(Данные) Тогда
ВнутреннееХеширование.Добавить(Данные);
КонецЕсли;
ВнешнееХеширование.Добавить(ВнутреннееХеширование.ХешСумма);
Возврат ВнешнееХеширование.ХешСумма;
КонецФункции
// Возвращает структуру именованных кодов состояний HTTP.
//
// Возвращаемое значение:
// Структура - именованные коды состояний HTTP.
//
Функция КодыСостоянияHTTP() Экспорт
КодыСостояния = Новый Структура;
КодыСостояния.Вставить("ОК_200", 200);
КодыСостояния.Вставить("Принято_202", 202);
КодыСостояния.Вставить("ПеремещеноНавсегда_301", 301);
КодыСостояния.Вставить("ПеремещеноВременно_302", 302);
КодыСостояния.Вставить("СмотретьДругое_303", 303);
КодыСостояния.Вставить("ВременноеПеренаправление_307", 307);
КодыСостояния.Вставить("ПостоянноеПеренаправление_308", 308);
КодыСостояния.Вставить("НеверныйЗапрос_400", 400);
КодыСостояния.Вставить("НеАвторизован_401", 401);
КодыСостояния.Вставить("НеобходимаОплата_402", 402);
КодыСостояния.Вставить("Запрещено_403", 403);
КодыСостояния.Вставить("ПолезнаяНагрузкаСлишкомВелика_413", 413);
КодыСостояния.Вставить("СлишкомМногоЗапросов_429", 429);
КодыСостояния.Вставить("ВнутренняяОшибкаСервера_500", 500);
КодыСостояния.Вставить("ОшибочныйШлюз_502", 502);
КодыСостояния.Вставить("СервисНедоступен_503", 503);
КодыСостояния.Вставить("ШлюзНеОтвечает_504", 504);
Возврат КодыСостояния;
КонецФункции
// Выполняет чтение данных из архива GZip.
//
// Параметры:
// СжатыеДанные - ДвоичныеДанные - данные упакованные GZip.
//
// Возвращаемое значение:
// ДвоичныеДанные - распакованные данные.
//
Функция ПрочитатьGZip(СжатыеДанные) Экспорт
РазмерПрефиксаGZip = 10;
РазмерПостфиксаGZip = 8;
ЧтениеДанных = Новый ЧтениеДанных(СжатыеДанные);
ЧтениеДанных.Пропустить(РазмерПрефиксаGZip);
РазмерСжатыхДанных = ЧтениеДанных.ИсходныйПоток().Размер() - РазмерПрефиксаGZip - РазмерПостфиксаGZip;
ПотокZip = Новый ПотокВПамяти(ZipРазмерLFH() + РазмерСжатыхДанных + ZipРазмерDD() + ZipРазмерCDH() + ZipРазмерEOCD());
ЗаписьДанных = Новый ЗаписьДанных(ПотокZip);
ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(ZipLFH());
ЧтениеДанных.КопироватьВ(ЗаписьДанных, РазмерСжатыхДанных);
ЗаписьДанных.Закрыть();
ЗаписьДанных = Новый ЗаписьДанных(ПотокZip);
CRC32 = ЧтениеДанных.ПрочитатьЦелое32();
РазмерНесжатыхДанных = ЧтениеДанных.ПрочитатьЦелое32();
ЧтениеДанных.Закрыть();
ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(ZipDD(CRC32, РазмерСжатыхДанных, РазмерНесжатыхДанных));
ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(ZipCDH(CRC32, РазмерСжатыхДанных, РазмерНесжатыхДанных));
ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(ZipEOCD(РазмерСжатыхДанных));
ЗаписьДанных.Закрыть();
Возврат ПрочитатьZip(ПотокZip);
КонецФункции
// Выполняет запись данных в архив GZip.
//
// Параметры:
// Данные - ДвоичныеДанные - исходные данные.
//
// Возвращаемое значение:
// ДвоичныеДанные - данные упакованные GZip.
//
Функция ЗаписатьGZip(Данные) Экспорт
ЧтениеДанных = Новый ЧтениеДанных(ЗаписатьZip(Данные));
НачальноеСмещение = 14;
ЧтениеДанных.Пропустить(НачальноеСмещение);
CRC32 = ЧтениеДанных.ПрочитатьЦелое32();
РазмерСжатыхДанных = ЧтениеДанных.ПрочитатьЦелое32();
РазмерИсходныхДанных = ЧтениеДанных.ПрочитатьЦелое32();
РазмерИмениФайла = ЧтениеДанных.ПрочитатьЦелое16();
РазмерДополнительногоПоля = ЧтениеДанных.ПрочитатьЦелое16();
ЧтениеДанных.Пропустить(РазмерИмениФайла + РазмерДополнительногоПоля);
ПотокGZip = Новый ПотокВПамяти;
ЗаписьДанных = Новый ЗаписьДанных(ПотокGZip);
ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(GZipHeader());
ЧтениеДанных.КопироватьВ(ЗаписьДанных, РазмерСжатыхДанных);
ЗаписьДанных.Закрыть();
ЗаписьДанных = Новый ЗаписьДанных(ПотокGZip);
ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(GZipFooter(CRC32, РазмерИсходныхДанных));
Возврат ПотокGZip.ЗакрытьИПолучитьДвоичныеДанные();
КонецФункции
#КонецОбласти
#КонецОбласти
#Область СлужебныйПрограммныйИнтерфейс
Функция ПодготовитьЗапрос(Сессия, Метод, URL, ДополнительныеПараметры) Экспорт
Cookies = ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Cookies", Новый Массив);
Cookies = ОбъединитьCookies(ДозаполнитьCookie(Сессия.Cookies, URL), ДозаполнитьCookie(Cookies, URL));
АутентификацияИзДополнительныхПараметров =
ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Аутентификация", Новый Структура);
ПараметрыЗапросаИзДополнительныхПараметров =
ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыЗапроса", Новый Структура);
ЗаголовкиИзДополнительныхПараметров =
ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Заголовки", Новый Соответствие);
Аутентификация = ОбъединитьПараметрыАутентификации(АутентификацияИзДополнительныхПараметров, Сессия.Аутентификация);
ПараметрыЗапроса = ОбъединитьПараметрыЗапроса(ПараметрыЗапросаИзДополнительныхПараметров, Сессия.ПараметрыЗапроса);
Заголовки = ОбъединитьЗаголовки(ЗаголовкиИзДополнительныхПараметров, Сессия.Заголовки);
ПараметрыПреобразованияJSON =
ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыПреобразованияJSON", Неопределено);
ПодготовленныйЗапрос = Новый Структура;
ПодготовленныйЗапрос.Вставить("Cookies", Cookies);
ПодготовленныйЗапрос.Вставить("Аутентификация", Аутентификация);
ПодготовленныйЗапрос.Вставить("Метод", Метод);
ПодготовленныйЗапрос.Вставить("Заголовки", Заголовки);
ПодготовленныйЗапрос.Вставить("ПараметрыЗапроса", ПараметрыЗапроса);
ПодготовленныйЗапрос.Вставить("URL", ПодготовитьURL(URL, ПараметрыЗапроса));
ПодготовленныйЗапрос.Вставить("ПараметрыПреобразованияJSON", ПараметрыПреобразованияJSON);
ПодготовитьCookies(ПодготовленныйЗапрос);
Данные = ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Данные", Новый Структура);
Файлы = ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Файлы", Новый Массив);
Json = ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "Json", Неопределено);
ПараметрыЗаписиJSON = ВыбратьЗначение(Неопределено, ДополнительныеПараметры, "ПараметрыЗаписиJSON", Неопределено);
ПодготовитьТелоЗапроса(ПодготовленныйЗапрос, Данные, Файлы, Json, ПараметрыЗаписиJSON);
ПодготовитьАутентификацию(ПодготовленныйЗапрос);
Возврат ПодготовленныйЗапрос;
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Функция ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(КодСостояния)
Коды = КодыСостоянияHTTP();
Возврат КодСостояния = Коды.ПолезнаяНагрузкаСлишкомВелика_413
ИЛИ КодСостояния = Коды.СлишкомМногоЗапросов_429
ИЛИ КодСостояния = Коды.СервисНедоступен_503;
КонецФункции
Функция ЧислоИзСтроки(Знач Строка) Экспорт
ОписаниеТипа = Новый ОписаниеТипов("Число");
Возврат ОписаниеТипа.ПривестиЗначение(Строка);
КонецФункции
Функция ДатаИзСтроки(Знач Строка) Экспорт
КвалификаторДаты = Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя);
ОписаниеТипа = Новый ОписаниеТипов("Дата", Неопределено, Неопределено, КвалификаторДаты);
Возврат ОписаниеТипа.ПривестиЗначение(Строка);
КонецФункции
Функция ДатаИзСтрокиRFC7231(Знач Строка) Экспорт
Разделители = ",-:/\.";
Для Индекс = 1 По СтрДлина(Разделители) Цикл
Разделитель = Сред(Разделители, Индекс, 1);
Строка = СтрЗаменить(Строка, Разделитель, " ");
КонецЦикла;
Строка = СтрЗаменить(Строка, " ", " ");
СоставляющиеДаты = СтрРазделить(Строка, " ");
МесяцСтр = СоставляющиеДаты[2];
Месяцы = СтрРазделить("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", ",");
Месяц = Месяцы.Найти(МесяцСтр);
Если Месяц = Неопределено Тогда
Возврат '00010101';
КонецЕсли;
Дата = СоставляющиеДаты[3] + Формат(Месяц + 1, "ЧЦ=2; ЧВН=;") + СоставляющиеДаты[1];
Время = СоставляющиеДаты[4] + СоставляющиеДаты[5] + СоставляющиеДаты[6];
Возврат ДатаИзСтроки(Дата + Время);
КонецФункции
Функция ВызватьHTTPМетод(Сессия, Метод, URL, ДополнительныеПараметры)
КодыСостоянияHTTP = КодыСостоянияHTTP();
ПодготовленныйЗапрос = ПодготовитьЗапрос(Сессия, Метод, URL, ДополнительныеПараметры);
НастройкиПодключения = НастройкиПодключения(Метод, URL, ДополнительныеПараметры);
Ответ = ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, НастройкиПодключения);
Перенаправление = 0;
Пока Перенаправление < Сессия.МаксимальноеКоличествоПеренаправлений Цикл
Если Не НастройкиПодключения.РазрешитьПеренаправление ИЛИ Не Ответ.ЭтоРедирект Тогда
Возврат Ответ;
КонецЕсли;
НовыйURL = СформироватьНовыйURLПриПеренаправлении(Ответ);
ПодготовленныйЗапрос.URL = КодироватьСтроку(НовыйURL, СпособКодированияСтроки.URLВКодировкеURL);
НовыйHTTPЗапрос = Новый HTTPЗапрос(СобратьАдресРесурса(РазобратьURL(НовыйURL), Неопределено));
ПереопределитьМетод(ПодготовленныйЗапрос, Ответ);
Если Ответ.КодСостояния <> КодыСостоянияHTTP.ВременноеПеренаправление_307
И Ответ.КодСостояния <> КодыСостоянияHTTP.ПостоянноеПеренаправление_308 Тогда
УдалитьЗаголовки(ПодготовленныйЗапрос.Заголовки, "content-length,content-type,transfer-encoding");
НовыйHTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки;
Иначе
ИсходныйПоток = ПодготовленныйЗапрос.HTTPЗапрос.ПолучитьТелоКакПоток();
ИсходныйПоток.КопироватьВ(НовыйHTTPЗапрос.ПолучитьТелоКакПоток());
КонецЕсли;
ПодготовленныйЗапрос.HTTPЗапрос = НовыйHTTPЗапрос;
УдалитьЗаголовки(ПодготовленныйЗапрос.Заголовки, "cookies");
ПодготовленныйЗапрос.Cookies = ОбъединитьCookies(Сессия.Cookies, ПодготовленныйЗапрос.Cookies);
ПодготовитьCookies(ПодготовленныйЗапрос);
// INFO: по хорошему аутентификацию нужно привести к новых параметрам, но пока будем игнорировать.
Ответ = ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, НастройкиПодключения);
Перенаправление = Перенаправление + 1;
КонецЦикла;
ВызватьИсключение("СлишкомМногоПеренаправлений");
КонецФункции
Функция СформироватьНовыйURLПриПеренаправлении(Ответ)
НовыйURL = ЗначениеЗаголовка("location", Ответ.Заголовки);
НовыйURL = РаскодироватьСтроку(НовыйURL, СпособКодированияСтроки.URLВКодировкеURL);
// Редирект без схемы
Если СтрНачинаетсяС(НовыйURL, "//") Тогда
СтруктураURL = РазобратьURL(Ответ.URL);
НовыйURL = СтруктураURL.Схема + ":" + НовыйURL;
КонецЕсли;
СтруктураURL = РазобратьURL(НовыйURL);
Если Не ЗначениеЗаполнено(СтруктураURL.Сервер) Тогда
СтруктураURLОтвета = РазобратьURL(Ответ.URL);
БазовыйURL = СтрШаблон("%1://%2", СтруктураURLОтвета.Схема, СтруктураURLОтвета.Сервер);
Если ЗначениеЗаполнено(СтруктураURLОтвета.Порт) Тогда
БазовыйURL = БазовыйURL + Формат(СтруктураURLОтвета.Порт, "ЧРГ=; ЧГ=");
КонецЕсли;
НовыйURL = БазовыйURL + НовыйURL;
КонецЕсли;
Возврат НовыйURL;
КонецФункции
Процедура УдалитьЗаголовки(Заголовки, СписокЗаголовковСтрокой)
ЗаголовкиДляУдаления = Новый Массив;
СписокЗаголовков = СтрРазделить(СписокЗаголовковСтрокой, ",", Ложь);
Для Каждого Заголовок Из Заголовки Цикл
Если СписокЗаголовков.Найти(НРег(Заголовок.Ключ)) <> Неопределено Тогда
ЗаголовкиДляУдаления.Добавить(Заголовок.Ключ);
КонецЕсли;
КонецЦикла;
Для Каждого ЗаголовокДляУдаления Из ЗаголовкиДляУдаления Цикл
Заголовки.Удалить(ЗаголовокДляУдаления);
КонецЦикла;
КонецПроцедуры
Функция НастройкиПодключения(Метод, URL, ДополнительныеПараметры)
РазрешитьПеренаправление =
ЗначениеПоКлючу(ДополнительныеПараметры, "РазрешитьПеренаправление", ВРег(Метод) <> "HEAD");
ПроверятьSSL = ЗначениеПоКлючу(ДополнительныеПараметры, "ПроверятьSSL", Истина);
КлиентскийСертификатSSL = ЗначениеПоКлючу(ДополнительныеПараметры, "КлиентскийСертификатSSL");
Прокси = ЗначениеПоКлючу(ДополнительныеПараметры, "Прокси", ПроксиПоУмолчанию(URL));
МаксимальноеКоличествоПовторов = ЗначениеПоКлючу(ДополнительныеПараметры, "МаксимальноеКоличествоПовторов", 0);
ПовторятьДляКодовСостояний =
ЗначениеПоКлючу(ДополнительныеПараметры, "ПовторятьДляКодовСостояний", Неопределено);
КоэффициентЭкспоненциальнойЗадержки =
ЗначениеПоКлючу(ДополнительныеПараметры, "КоэффициентЭкспоненциальнойЗадержки", 1);
МаксимальноеВремяПовторов = ЗначениеПоКлючу(ДополнительныеПараметры, "МаксимальноеВремяПовторов", 600);
Настройки = Новый Структура;
Настройки.Вставить("Таймаут", Таймаут(ДополнительныеПараметры));
Настройки.Вставить("РазрешитьПеренаправление", РазрешитьПеренаправление);
Настройки.Вставить("ПроверятьSSL", ПроверятьSSL);
Настройки.Вставить("КлиентскийСертификатSSL", КлиентскийСертификатSSL);
Настройки.Вставить("Прокси", Прокси);
Настройки.Вставить("МаксимальноеКоличествоПовторов", МаксимальноеКоличествоПовторов);
Настройки.Вставить("ПовторятьДляКодовСостояний", ПовторятьДляКодовСостояний);
Настройки.Вставить("КоэффициентЭкспоненциальнойЗадержки", КоэффициентЭкспоненциальнойЗадержки);
Настройки.Вставить("МаксимальноеВремяПовторов", МаксимальноеВремяПовторов);
Возврат Настройки;
КонецФункции
Функция Таймаут(ДополнительныеПараметры)
Если ДополнительныеПараметры.Свойство("Таймаут") И ЗначениеЗаполнено(ДополнительныеПараметры.Таймаут) Тогда
Таймаут = ДополнительныеПараметры.Таймаут;
Иначе
Таймаут = СтандартныйТаймаут();
КонецЕсли;
Возврат Таймаут;
КонецФункции
Функция ПроксиПоУмолчанию(URL)
ПроксиПоУмолчанию = Новый ИнтернетПрокси;
// BSLLS:ExecuteExternalCodeInCommonModule-off
ИмяОМПолученияФайловБСП = "ПолучениеФайловИзИнтернета";
Если Метаданные.ОбщиеМодули.Найти(ИмяОМПолученияФайловБСП) <> Неопределено Тогда
СтруктураURL = РазобратьURL(URL);
Модуль = Вычислить(ИмяОМПолученияФайловБСП);
ПроксиПоУмолчанию = Модуль.ПолучитьПрокси(СтруктураURL.Схема);
КонецЕсли;
// BSLLS:ExecuteExternalCodeInCommonModule-on
Возврат ПроксиПоУмолчанию;
КонецФункции
Функция ДозаполнитьCookie(Cookies, URL)
СтруктураURL = РазобратьURL(URL);
НовыеCookies = Новый Массив;
Если ТипЗнч(Cookies) = Тип("Массив") Тогда
Для Каждого Cookie Из Cookies Цикл
НовыйCookie = КонструкторCookie(Cookie.Наименование, Cookie.Значение);
ЗаполнитьЗначенияСвойств(НовыйCookie, Cookie);
Если Не ЗначениеЗаполнено(НовыйCookie.Домен) Тогда
НовыйCookie.Домен = СтруктураURL.Сервер;
КонецЕсли;
Если Не ЗначениеЗаполнено(НовыйCookie.Путь) Тогда
НовыйCookie.Путь = "/";
КонецЕсли;
НовыеCookies.Добавить(НовыйCookie);
КонецЦикла;
Возврат НовыеCookies;
КонецЕсли;
Возврат Cookies;
КонецФункции
Процедура УдалитьCookieИзХранилища(ХранилищеCookies, Cookie)
Если ХранилищеCookies.Получить(Cookie.Домен) <> Неопределено
И ХранилищеCookies[Cookie.Домен].Получить(Cookie.Путь) <> Неопределено
И ХранилищеCookies[Cookie.Домен][Cookie.Путь].Получить(Cookie.Наименование) <> Неопределено Тогда
ХранилищеCookies[Cookie.Домен][Cookie.Путь].Удалить(Cookie.Наименование);
КонецЕсли;
КонецПроцедуры
Процедура ДобавитьCookieВХранилище(ХранилищеCookies, Cookie, Замещать = Ложь)
Если ХранилищеCookies.Получить(Cookie.Домен) = Неопределено Тогда
ХранилищеCookies[Cookie.Домен] = Новый Соответствие;
КонецЕсли;
Если ХранилищеCookies[Cookie.Домен].Получить(Cookie.Путь) = Неопределено Тогда
ХранилищеCookies[Cookie.Домен][Cookie.Путь] = Новый Соответствие;
КонецЕсли;
Если ХранилищеCookies[Cookie.Домен][Cookie.Путь].Получить(Cookie.Наименование) = Неопределено ИЛИ Замещать Тогда
ХранилищеCookies[Cookie.Домен][Cookie.Путь][Cookie.Наименование] = Cookie;
КонецЕсли;
КонецПроцедуры
Функция ДобавитьЛидирующуюТочку(Знач Домен)
Если Не СтрНачинаетсяС(Домен, ".") Тогда
Домен = "." + Домен;
КонецЕсли;
Возврат Домен;
КонецФункции
Процедура ЗаполнитьСписокОтфильтрованнымиCookies(Cookies, СтруктураURL, Список)
Для Каждого Cookie Из Cookies Цикл
Если Cookie.Значение.ТолькоБезопасноеСоединение = Истина И СтруктураURL.Схема <> "https" Тогда
Продолжить;
КонецЕсли;
// INFO: проверка срока действия игнорируется (Cookie.Значение.СрокДействия)
// INFO: проверка порта игнорируется
Список.Добавить(Cookie.Значение);
КонецЦикла;
КонецПроцедуры
Функция ОтобратьCookiesДляЗапроса(СтруктураURL, Cookies)
СерверВЗапросе = ДобавитьЛидирующуюТочку(СтруктураURL.Сервер);
Результат = Новый Массив;
Для Каждого Домен Из Cookies Цикл
Если Не СтрЗаканчиваетсяНа(СерверВЗапросе, Домен.Ключ) Тогда
Продолжить;
КонецЕсли;
Для Каждого Путь Из Домен.Значение Цикл
Если Не СтрНачинаетсяС(СтруктураURL.Путь, Путь.Ключ) Тогда
Продолжить;
КонецЕсли;
ЗаполнитьСписокОтфильтрованнымиCookies(Путь.Значение, СтруктураURL, Результат);
КонецЦикла;
КонецЦикла;
Возврат Результат;
КонецФункции
Функция ПодготовитьЗаголовокCookie(ПодготовленныйЗапрос)
СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
Cookies = Новый Массив;
Для Каждого Cookie Из ОтобратьCookiesДляЗапроса(СтруктураURL, ПодготовленныйЗапрос.Cookies) Цикл
Cookies.Добавить(СтрШаблон("%1=%2", Cookie.Наименование, Cookie.Значение));
КонецЦикла;
Возврат СтрСоединить(Cookies, "; ");
КонецФункции
Процедура ПодготовитьCookies(ПодготовленныйЗапрос)
ЗаголовокCookie = ПодготовитьЗаголовокCookie(ПодготовленныйЗапрос);
Если ЗначениеЗаполнено(ЗаголовокCookie) Тогда
ПодготовленныйЗапрос.Заголовки["Cookie"] = ЗаголовокCookie;
КонецЕсли;
КонецПроцедуры
Функция КодироватьПараметрыЗапроса(ПараметрыЗапроса)
ЧастиПараметрыЗапроса = Новый Массив;
Для Каждого Параметр Из ПараметрыЗапроса Цикл
Если ТипЗнч(Параметр.Значение) = Тип("Массив") Тогда
Значения = Параметр.Значение;
Иначе
Значения = Новый Массив;
Значения.Добавить(Параметр.Значение);
КонецЕсли;
Если Параметр.Значение = Неопределено Тогда
ЧастиПараметрыЗапроса.Добавить(Параметр.Ключ);
Иначе
Для Каждого Значение Из Значения Цикл
ЗначениеПараметра = КодироватьСтроку(Значение, СпособКодированияСтроки.КодировкаURL);
ЧастиПараметрыЗапроса.Добавить(СтрШаблон("%1=%2", Параметр.Ключ, ЗначениеПараметра));
КонецЦикла;
КонецЕсли;
КонецЦикла;
Возврат СтрСоединить(ЧастиПараметрыЗапроса, "&");
КонецФункции
Функция ПодготовитьURL(Знач URL, ПараметрыЗапроса = Неопределено)
URL = СокрЛ(URL);
СтруктураURL = РазобратьURL(URL);
ПодготовленныйURL = СтруктураURL.Схема + "://";
Если ЗначениеЗаполнено(СтруктураURL.Аутентификация.Пользователь) Тогда
ПодготовленныйURL = ПодготовленныйURL
+ СтруктураURL.Аутентификация.Пользователь + ":"
+ СтруктураURL.Аутентификация.Пароль + "@";
КонецЕсли;
ПодготовленныйURL = ПодготовленныйURL + СтруктураURL.Сервер;
Если ЗначениеЗаполнено(СтруктураURL.Порт) Тогда
ПодготовленныйURL = ПодготовленныйURL + ":" + Формат(СтруктураURL.Порт, "ЧРГ=; ЧГ=");
КонецЕсли;
ПодготовленныйURL = ПодготовленныйURL + СобратьАдресРесурса(СтруктураURL, ПараметрыЗапроса);
Возврат ПодготовленныйURL;
КонецФункции
Функция ЗаголовкиВСтроку(Заголовки)
РазделительСтрок = Символы.ВК + Символы.ПС;
Строки = Новый Массив;
СортированныеЗаголовки = "Content-Disposition,Content-Type,Content-Location";
Для Каждого Ключ Из СтрРазделить(СортированныеЗаголовки, ",") Цикл
Значение = ЗначениеЗаголовка(Ключ, Заголовки);
Если Значение <> Ложь И ЗначениеЗаполнено(Значение) Тогда
Строки.Добавить(СтрШаблон("%1: %2", Ключ, Значение));
КонецЕсли;
КонецЦикла;
Ключи = СтрРазделить(ВРег(СортированныеЗаголовки), ",");
Для Каждого Заголовок Из Заголовки Цикл
Если Ключи.Найти(ВРег(Заголовок.Ключ)) = Неопределено Тогда
Строки.Добавить(СтрШаблон("%1: %2", Заголовок.Ключ, Заголовок.Значение));
КонецЕсли;
КонецЦикла;
Строки.Добавить(РазделительСтрок);
Возврат СтрСоединить(Строки, РазделительСтрок);
КонецФункции
Функция ЗначениеПоКлючу(Структура, Ключ, ЗначениеПоУмолчанию = Неопределено)
Если ТипЗнч(Структура) = Тип("Структура") И Структура.Свойство(Ключ) Тогда
Значение = Структура[Ключ];
ИначеЕсли ТипЗнч(Структура) = Тип("Соответствие") И Структура.Получить(Ключ) <> Неопределено Тогда
Значение = Структура.Получить(Ключ);
Иначе
Значение = ЗначениеПоУмолчанию;
КонецЕсли;
Возврат Значение;
КонецФункции
Функция СоздатьПолеФормы(ИсходныеПараметры)
Поле = Новый Структура("Имя,ИмяФайла,Данные,Тип,Заголовки");
Поле.Имя = ИсходныеПараметры.Имя;
Поле.Данные = ИсходныеПараметры.Данные;
Поле.Тип = ЗначениеПоКлючу(ИсходныеПараметры, "Тип");
Поле.Заголовки = ЗначениеПоКлючу(ИсходныеПараметры, "Заголовки", Новый Соответствие);
Поле.ИмяФайла = ЗначениеПоКлючу(ИсходныеПараметры, "ИмяФайла");
Ключ = "Content-Disposition";
Если ЗначениеЗаголовка("content-disposition", Поле.Заголовки, Ключ) = Ложь Тогда
Поле.Заголовки.Вставить("Content-Disposition", "form-data");
КонецЕсли;
Части = Новый Массив;
Части.Добавить(Поле.Заголовки[Ключ]);
Части.Добавить(СтрШаблон("name=""%1""", Поле.Имя));
Если ЗначениеЗаполнено(Поле.ИмяФайла) Тогда
Части.Добавить(СтрШаблон("filename=""%1""", Поле.ИмяФайла));
КонецЕсли;
Поле.Заголовки[Ключ] = СтрСоединить(Части, "; ");
Поле.Заголовки["Content-Type"] = Поле.Тип;
Возврат Поле;
КонецФункции
Функция ЗакодироватьФайлы(HTTPЗапрос, Файлы, Данные)
Части = Новый Массив;
Если ЗначениеЗаполнено(Данные) Тогда
Для Каждого Поле Из Данные Цикл
Части.Добавить(СоздатьПолеФормы(Новый Структура("Имя,Данные", Поле.Ключ, Поле.Значение)));
КонецЦикла;
КонецЕсли;
Если ТипЗнч(Файлы) = Тип("Массив") Тогда
Для Каждого Файл Из Файлы Цикл
Части.Добавить(СоздатьПолеФормы(Файл));
КонецЦикла;
Иначе
Части.Добавить(СоздатьПолеФормы(Файлы));
КонецЕсли;
Разделитель = СтрЗаменить(Новый УникальныйИдентификатор, "-", "");
РазделительСтрок = Символы.ВК + Символы.ПС;
ТелоЗапроса = HTTPЗапрос.ПолучитьТелоКакПоток();
ЗаписьДанных = Новый ЗаписьДанных(ТелоЗапроса, КодировкаТекста.UTF8, ПорядокБайтов.LittleEndian, "", "", Ложь);
Для Каждого Часть Из Части Цикл
ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + РазделительСтрок);
ЗаписьДанных.ЗаписатьСтроку(ЗаголовкиВСтроку(Часть.Заголовки));
Если ТипЗнч(Часть.Данные) = Тип("ДвоичныеДанные") Тогда
ЗаписьДанных.Записать(Часть.Данные);
Иначе
ЗаписьДанных.ЗаписатьСтроку(Часть.Данные);
КонецЕсли;
ЗаписьДанных.ЗаписатьСтроку(РазделительСтрок);
КонецЦикла;
ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + "--" + РазделительСтрок);
ЗаписьДанных.Закрыть();
Возврат СтрШаблон("multipart/form-data; boundary=%1", Разделитель);
КонецФункции
Процедура ПодготовитьТелоЗапроса(ПодготовленныйЗапрос, Данные, Файлы, Json, ПараметрыЗаписиJSON)
СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
HTTPЗапрос = Новый HTTPЗапрос;
HTTPЗапрос.АдресРесурса = СобратьАдресРесурса(СтруктураURL, ПодготовленныйЗапрос.ПараметрыЗапроса);
Если ЗначениеЗаполнено(Файлы) Тогда
ContentType = ЗакодироватьФайлы(HTTPЗапрос, Файлы, Данные);
ИначеЕсли ЗначениеЗаполнено(Данные) Тогда
ContentType = "application/x-www-form-urlencoded";
Если ТипЗнч(Данные) = Тип("ДвоичныеДанные") Тогда
HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(Данные);
Иначе
Если ТипЗнч(Данные) = Тип("Строка") Тогда
Тело = Данные;
Иначе
Тело = КодироватьПараметрыЗапроса(Данные);
КонецЕсли;
HTTPЗапрос.УстановитьТелоИзСтроки(Тело, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
КонецЕсли;
ИначеЕсли Json <> Неопределено Тогда
ContentType = "application/json";
СтрокаJson = ОбъектВJson(Json, ПодготовленныйЗапрос.ПараметрыПреобразованияJSON, ПараметрыЗаписиJSON);
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJson, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Иначе
ContentType = Неопределено;
КонецЕсли;
ЗначениеЗаголовка = ЗначениеЗаголовка("content-type", ПодготовленныйЗапрос.Заголовки);
Если ЗначениеЗаголовка = Ложь И ЗначениеЗаполнено(ContentType) Тогда
ПодготовленныйЗапрос.Заголовки.Вставить("Content-Type", ContentType);
КонецЕсли;
HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки;
УпаковатьЗапрос(HTTPЗапрос);
ПодготовленныйЗапрос.Вставить("HTTPЗапрос", HTTPЗапрос);
КонецПроцедуры
Процедура ПодготовитьАутентификацию(ПодготовленныйЗапрос)
ПодготовленныйЗапрос.Вставить("СобытияНаОтвет", Новый Массив);
Если Не ЗначениеЗаполнено(ПодготовленныйЗапрос.Аутентификация) Тогда
СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
Если ЗначениеЗаполнено(СтруктураURL.Аутентификация) Тогда
ПодготовленныйЗапрос.Аутентификация = СтруктураURL.Аутентификация;
КонецЕсли;
КонецЕсли;
Если ЗначениеЗаполнено(ПодготовленныйЗапрос.Аутентификация) Тогда
Если ПодготовленныйЗапрос.Аутентификация.Свойство("Тип") Тогда
ТипАутентификации = НРег(ПодготовленныйЗапрос.Аутентификация.Тип);
Если ТипАутентификации = "digest" Тогда
ПодготовленныйЗапрос.СобытияНаОтвет.Добавить("ОбработкаОтветаСКодом401");
КонецЕсли;
Если ТипАутентификации = "aws4-hmac-sha256" Тогда
ПодготовитьАутентификациюAWS4(ПодготовленныйЗапрос);
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Функция ОбъединитьCookies(ГлавныйИсточник, ДополнительныйИсточник)
Cookies = Новый Соответствие;
Для Каждого Cookie Из ПреобразоватьХранилищеCookiesВМассивCookies(ГлавныйИсточник) Цикл
ДобавитьCookieВХранилище(Cookies, Cookie, Ложь);
КонецЦикла;
Для Каждого Cookie Из ПреобразоватьХранилищеCookiesВМассивCookies(ДополнительныйИсточник) Цикл
ДобавитьCookieВХранилище(Cookies, Cookie, Ложь);
КонецЦикла;
Возврат Cookies;
КонецФункции
Функция ПреобразоватьХранилищеCookiesВМассивCookies(ХранилищеCookies)
Cookies = Новый Массив;
Если ТипЗнч(ХранилищеCookies) = Тип("Массив") Тогда
Для Каждого Cookie Из ХранилищеCookies Цикл
НоваяCookie = КонструкторCookie();
ЗаполнитьЗначенияСвойств(НоваяCookie, Cookie);
Cookies.Добавить(НоваяCookie);
КонецЦикла;
Возврат Cookies;
КонецЕсли;
Для Каждого Домен Из ХранилищеCookies Цикл
Для Каждого Путь Из Домен.Значение Цикл
Для Каждого Наименование Из Путь.Значение Цикл
Cookies.Добавить(Наименование.Значение);
КонецЦикла;
КонецЦикла;
КонецЦикла;
Возврат Cookies;
КонецФункции
Функция ОбъединитьПараметрыАутентификации(ГлавныйИсточник, ДополнительныйИсточник)
ПараметрыАутентификации = Новый Структура;
Если ТипЗнч(ГлавныйИсточник) = Тип("Структура") Тогда
Для Каждого Параметр Из ГлавныйИсточник Цикл
ПараметрыАутентификации.Вставить(Параметр.Ключ, Параметр.Значение);
КонецЦикла;
КонецЕсли;
Если ТипЗнч(ДополнительныйИсточник) = Тип("Структура") Тогда
Для Каждого Параметр Из ДополнительныйИсточник Цикл
Если Не ПараметрыАутентификации.Свойство(Параметр) Тогда
ПараметрыАутентификации.Вставить(Параметр.Ключ, Параметр.Значение);
КонецЕсли;
КонецЦикла;
КонецЕсли;
Возврат ПараметрыАутентификации;
КонецФункции
Функция ОбъединитьЗаголовки(ГлавныйИсточник, ДополнительныйИсточник)
Заголовки = Новый Соответствие;
Для Каждого Заголовок Из ГлавныйИсточник Цикл
Заголовки.Вставить(Заголовок.Ключ, Заголовок.Значение);
КонецЦикла;
Для Каждого Заголовок Из ДополнительныйИсточник Цикл
Если Заголовки.Получить(Заголовок.Ключ) = Неопределено Тогда
Заголовки.Вставить(Заголовок.Ключ, Заголовок.Значение);
КонецЕсли;
КонецЦикла;
Возврат Заголовки;
КонецФункции
Функция ОбъединитьПараметрыЗапроса(ГлавныйИсточник, ДополнительныйИсточник)
ПараметрыЗапроса = Новый Соответствие;
Если ТипЗнч(ГлавныйИсточник) = Тип("Структура") ИЛИ ТипЗнч(ГлавныйИсточник) = Тип("Соответствие") Тогда
Для Каждого Параметр Из ГлавныйИсточник Цикл
ПараметрыЗапроса.Вставить(Параметр.Ключ, Параметр.Значение);
КонецЦикла;
КонецЕсли;
Если ТипЗнч(ДополнительныйИсточник) = Тип("Структура") ИЛИ ТипЗнч(ДополнительныйИсточник) = Тип("Соответствие") Тогда
Для Каждого Параметр Из ДополнительныйИсточник Цикл
Если ПараметрыЗапроса.Получить(Параметр) = Неопределено Тогда
ПараметрыЗапроса.Вставить(Параметр.Ключ, Параметр.Значение);
КонецЕсли;
КонецЦикла;
КонецЕсли;
Возврат ПараметрыЗапроса;
КонецФункции
Функция ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки)
СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
Соединение = Соединение(СтруктураURL, ПодготовленныйЗапрос.Аутентификация, Настройки, Сессия);
Ответ = Соединение.ВызватьHTTPМетод(ПодготовленныйЗапрос.Метод, ПодготовленныйЗапрос.HTTPЗапрос);
Для Каждого Обработчик Из ПодготовленныйЗапрос.СобытияНаОтвет Цикл
Если Обработчик = "ОбработкаОтветаСКодом401" Тогда
ОбработкаОтветаСКодом401(Сессия, ПодготовленныйЗапрос, Настройки, Ответ);
КонецЕсли;
КонецЦикла;
Возврат Ответ;
КонецФункции
Функция РассчитатьДлительностьПриостановки(Повтор, КоэффициентЭкспоненциальнойЗадержки, ЗаголовокRetryAfter, Остаток)
Если ЗаголовокRetryAfter <> Ложь Тогда
Длительность = ЧислоИзСтроки(ЗаголовокRetryAfter);
Если Длительность = 0 Тогда
Дата = ДатаИзСтрокиRFC7231(ЗаголовокRetryAfter);
Если ЗначениеЗаполнено(Дата) Тогда
Длительность = Дата - ТекущаяУниверсальнаяДата();
КонецЕсли;
КонецЕсли;
Иначе
Длительность = КоэффициентЭкспоненциальнойЗадержки * Pow(2, Повтор - 1);
КонецЕсли;
Длительность = Мин(Длительность, Остаток);
Если Длительность < 0 Тогда
Длительность = 0;
КонецЕсли;
Возврат Длительность;
КонецФункции
Функция НеобходимоПовторитьЗапрос(Ответ, Настройки, ОшибкаВыполненияЗапроса)
Если Настройки.МаксимальноеКоличествоПовторов < 1 Тогда
Возврат Ложь;
КонецЕсли;
Если ОшибкаВыполненияЗапроса <> Неопределено Тогда
Возврат Истина;
КонецЕсли;
ПовторПриЛюбомКодеСостоянияБольшеИлиРавным500 = Настройки.ПовторятьДляКодовСостояний = Неопределено
И Ответ.КодСостояния >= КодыСостоянияHTTP().ВнутренняяОшибкаСервера_500;
КодСостоянияСоответствуетКодуСостоянияПовтора = ТипЗнч(Настройки.ПовторятьДляКодовСостояний) = Тип("Массив")
И Настройки.ПовторятьДляКодовСостояний.Найти(Ответ.КодСостояния) <> Неопределено;
Если ПовторПриЛюбомКодеСостоянияБольшеИлиРавным500 ИЛИ КодСостоянияСоответствуетКодуСостоянияПовтора Тогда
Возврат Истина;
КонецЕсли;
ЗаголовокRetryAfter = ЗначениеЗаголовка("retry-after", Ответ.Заголовки);
Возврат ЗаголовокRetryAfter <> Ложь
И ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(Ответ.КодСостояния);
КонецФункции
Функция ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, Настройки)
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
МиллисекундВСекунде = 1000;
Повтор = 0;
Длительность = 0;
Пока Истина Цикл
Попытка
Ответ = ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки);
Исключение
ОшибкаВыполненияЗапроса = ИнформацияОбОшибке();
КонецПопытки;
Повтор = Повтор + 1;
Длительность = (ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало) / МиллисекундВСекунде;
Если Не НеобходимоПовторитьЗапрос(Ответ, Настройки, ОшибкаВыполненияЗапроса) Тогда
Прервать;
КонецЕсли;
Если Повтор > Настройки.МаксимальноеКоличествоПовторов
ИЛИ Длительность > Настройки.МаксимальноеВремяПовторов Тогда
Прервать;
КонецЕсли;
Если ОшибкаВыполненияЗапроса <> Неопределено
ИЛИ НЕ ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(Ответ.КодСостояния) Тогда
ЗаголовокRetryAfter = Ложь;
Иначе
ЗаголовокRetryAfter = ЗначениеЗаголовка("retry-after", Ответ.Заголовки);
КонецЕсли;
ДлительностьПриостановки = РассчитатьДлительностьПриостановки(
Повтор,
Настройки.КоэффициентЭкспоненциальнойЗадержки,
ЗаголовокRetryAfter,
Настройки.МаксимальноеВремяПовторов - Длительность);
Приостановить(ДлительностьПриостановки);
КонецЦикла;
Если ОшибкаВыполненияЗапроса <> Неопределено Тогда
ВызватьИсключение(ПодробноеПредставлениеОшибки(ОшибкаВыполненияЗапроса));
КонецЕсли;
ЗаголовокContentType = ЗначениеЗаголовка("content-type", Ответ.Заголовки);
Если ЗаголовокContentType = Ложь Тогда
ЗаголовокContentType = "";
КонецЕсли;
ПодготовленныйОтвет = Новый Структура;
ПодготовленныйОтвет.Вставить("ВремяВыполнения", ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало);
ПодготовленныйОтвет.Вставить("Cookies", ИзвлечьCookies(Ответ.Заголовки, ПодготовленныйЗапрос.URL));
ПодготовленныйОтвет.Вставить("Заголовки", Ответ.Заголовки);
ПодготовленныйОтвет.Вставить("ЭтоПостоянныйРедирект", ЭтоПостоянныйРедирект(Ответ.КодСостояния, Ответ.Заголовки));
ПодготовленныйОтвет.Вставить("ЭтоРедирект", ЭтоРедирект(Ответ.КодСостояния, Ответ.Заголовки));
ПодготовленныйОтвет.Вставить("Кодировка", КодировкаИзЗаголовка(ЗаголовокContentType));
ПодготовленныйОтвет.Вставить("Тело", Ответ.ПолучитьТелоКакДвоичныеДанные());
ПодготовленныйОтвет.Вставить("КодСостояния", Ответ.КодСостояния);
ПодготовленныйОтвет.Вставить("URL", ПодготовленныйЗапрос.URL);
Сессия.Cookies = ОбъединитьCookies(Сессия.Cookies, ПодготовленныйОтвет.Cookies);
Возврат ПодготовленныйОтвет;
КонецФункции
Процедура ПереопределитьМетод(ПодготовленныйЗапрос, Ответ)
КодыСостоянияHTTP = КодыСостоянияHTTP();
Метод = ПодготовленныйЗапрос.Метод;
// http://tools.ietf.org/html/rfc7231#section-6.4.4
Если Ответ.КодСостояния = КодыСостоянияHTTP.СмотретьДругое_303 И Метод <> "HEAD" Тогда
Метод = "GET";
КонецЕсли;
// Поведение браузеров
Если Ответ.КодСостояния = КодыСостоянияHTTP.ПеремещеноВременно_302 И Метод <> "HEAD" Тогда
Метод = "GET";
КонецЕсли;
ПодготовленныйЗапрос.Метод = Метод;
КонецПроцедуры
Функция ИзвлечьCookies(Заголовки, URL)
ТекущееВремя = ТекущаяУниверсальнаяДата();
Cookies = Новый Соответствие;
Для Каждого ОчереднойЗаголовок Из Заголовки Цикл
Если НРег(ОчереднойЗаголовок.Ключ) = "set-cookie" Тогда
Для Каждого ЗаголовокCookie Из РазбитьНаОтдельныеЗаголовкиCookies(ОчереднойЗаголовок.Значение) Цикл
Cookie = РаспарситьCookie(ЗаголовокCookie, URL, ТекущееВремя);
Если Cookie = Неопределено Тогда
Продолжить;
КонецЕсли;
Если Cookie.СрокДействия <= ТекущееВремя Тогда
УдалитьCookieИзХранилища(Cookies, Cookie);
Иначе
ДобавитьCookieВХранилище(Cookies, Cookie);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
Возврат Cookies;
КонецФункции
Функция РазбитьНаОтдельныеЗаголовкиCookies(Знач Заголовок)
Заголовки = Новый Массив;
Если Не ЗначениеЗаполнено(Заголовок) Тогда
Возврат Заголовки;
КонецЕсли;
ЗапчастиЗаголовков = СтрРазделить(Заголовок, ",", Ложь);
ОтдельныйЗаголовок = ЗапчастиЗаголовков[0];
Для Индекс = 1 По ЗапчастиЗаголовков.ВГраница() Цикл
ТочкаСЗапятой = СтрНайти(ЗапчастиЗаголовков[Индекс], ";");
Равно = СтрНайти(ЗапчастиЗаголовков[Индекс], "=");
Если ТочкаСЗапятой И Равно И Равно < ТочкаСЗапятой Тогда
Заголовки.Добавить(ОтдельныйЗаголовок);
ОтдельныйЗаголовок = ЗапчастиЗаголовков[Индекс];
Иначе
ОтдельныйЗаголовок = ОтдельныйЗаголовок + ЗапчастиЗаголовков[Индекс];
КонецЕсли;
КонецЦикла;
Заголовки.Добавить(ОтдельныйЗаголовок);
Возврат Заголовки;
КонецФункции
Функция КонструкторCookie(Наименование = "", Значение = Неопределено)
НовыйCookie = Новый Структура;
НовыйCookie.Вставить("Наименование", Наименование);
НовыйCookie.Вставить("Значение", Значение);
НовыйCookie.Вставить("Домен", "");
НовыйCookie.Вставить("Путь", "");
НовыйCookie.Вставить("Порт");
НовыйCookie.Вставить("СрокДействия", '39990101');
НовыйCookie.Вставить("ТолькоБезопасноеСоединение");
Возврат НовыйCookie;
КонецФункции
Функция СоздатьCookieИЗаполнитьОсновныеПараметры(Параметр)
Части = СтрРазделить(Параметр, "=", Ложь);
Наименование = Части[0];
Если Части.Количество() > 1 Тогда
Значение = Части[1];
КонецЕсли;
Возврат КонструкторCookie(Наименование, Значение);
КонецФункции
Функция РаспарситьCookie(Заголовок, URL, ТекущееВремя)
Cookie = Неопределено;
Индекс = 0;
Для Каждого Параметр Из СтрРазделить(Заголовок, ";", Ложь) Цикл
Индекс = Индекс + 1;
Параметр = СокрЛП(Параметр);
Если Индекс = 1 Тогда
Cookie = СоздатьCookieИЗаполнитьОсновныеПараметры(Параметр);
Продолжить;
КонецЕсли;
Части = СтрРазделить(Параметр, "=", Ложь);
Ключ = НРег(Части[0]);
Если Части.Количество() > 1 Тогда
Значение = Части[1];
КонецЕсли;
Если Ключ = "domain" Тогда
Cookie.Домен = Значение;
ИначеЕсли Ключ = "path" Тогда
Cookie.Путь = Значение;
ИначеЕсли Ключ = "secure" Тогда
Cookie.ТолькоБезопасноеСоединение = Истина;
ИначеЕсли Ключ = "max-age" Тогда
СрокДействияMaxAge = ТекущееВремя + ЧислоИзСтроки(Значение);
ИначеЕсли Ключ = "expires" Тогда
Cookie.СрокДействия = ДатаИзСтрокиRFC7231(Значение);
Иначе
Продолжить;
КонецЕсли;
КонецЦикла;
Если ЗначениеЗаполнено(Cookie) И ЗначениеЗаполнено(СрокДействияMaxAge) Тогда
Cookie.СрокДействия = СрокДействияMaxAge;
КонецЕсли;
ДозаполнитьCookieНеявнымиЗначениями(Cookie, URL);
Возврат Cookie;
КонецФункции
Процедура ДозаполнитьCookieНеявнымиЗначениями(Cookie, URL)
Если Cookie = Неопределено Тогда
Возврат;
КонецЕсли;
СтруктураURL = РазобратьURL(URL);
Если Не ЗначениеЗаполнено(Cookie.Домен) Тогда
Cookie.Домен = СтруктураURL.Сервер;
КонецЕсли;
Если Не ЗначениеЗаполнено(Cookie.Порт) И ЗначениеЗаполнено(СтруктураURL.Порт) Тогда
Cookie.Порт = СтруктураURL.Порт;
КонецЕсли;
Если Не ЗначениеЗаполнено(Cookie.Путь) Тогда
ПозицияПоследнегоСлеша = СтрНайти(СтруктураURL.Путь, "/", НаправлениеПоиска.СКонца);
Если ПозицияПоследнегоСлеша <= 1 Тогда
Cookie.Путь = "/";
Иначе
Cookie.Путь = Лев(СтруктураURL.Путь, ПозицияПоследнегоСлеша - 1);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Функция ЗначениеЗаголовка(Заголовок, ВсеЗаголовки, Ключ = Неопределено)
Для Каждого ОчереднойЗаголовок Из ВсеЗаголовки Цикл
Если НРег(ОчереднойЗаголовок.Ключ) = НРег(Заголовок) Тогда
Ключ = ОчереднойЗаголовок.Ключ;
Возврат ОчереднойЗаголовок.Значение;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
Функция ЭтоПостоянныйРедирект(КодСостояния, Заголовки)
КодыСостоянияHTTP = КодыСостоянияHTTP();
Возврат ЕстьЗаголовокLocation(Заголовки) И
(КодСостояния = КодыСостоянияHTTP.ПеремещеноНавсегда_301
ИЛИ КодСостояния = КодыСостоянияHTTP.ПостоянноеПеренаправление_308);
КонецФункции
Функция ЭтоРедирект(КодСостояния, Заголовки)
КодыСостоянияHTTP = КодыСостоянияHTTP();
СостоянияРедиректа = Новый Массив;
СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПеремещеноНавсегда_301);
СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПеремещеноВременно_302);
СостоянияРедиректа.Добавить(КодыСостоянияHTTP.СмотретьДругое_303);
СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ВременноеПеренаправление_307);
СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПостоянноеПеренаправление_308);
Возврат ЕстьЗаголовокLocation(Заголовки) И СостоянияРедиректа.Найти(КодСостояния) <> Неопределено;
КонецФункции
Функция ЕстьЗаголовокLocation(Заголовки)
Возврат ЗначениеЗаголовка("location", Заголовки) <> Ложь;
КонецФункции
Функция КодировкаИзЗаголовка(Знач Заголовок)
Кодировка = Неопределено;
Заголовок = НРег(СокрЛП(Заголовок));
ИндексРазделителя = СтрНайти(Заголовок, ";");
Если ИндексРазделителя Тогда
ТипСодержимого = СокрЛП(Лев(Заголовок, ИндексРазделителя - 1));
КлючКодировки = "charset=";
ИндексКодировки = СтрНайти(Заголовок, КлючКодировки);
Если ИндексКодировки Тогда
ИндексРазделителя = СтрНайти(Заголовок, ";", НаправлениеПоиска.СНачала, ИндексКодировки);
НачальнаяПозиция = ИндексКодировки + СтрДлина(КлючКодировки);
Если ИндексРазделителя Тогда
ДлинаКодировки = ИндексРазделителя - НачальнаяПозиция;
Иначе
ДлинаКодировки = СтрДлина(Заголовок);
КонецЕсли;
Кодировка = Сред(Заголовок, НачальнаяПозиция, ДлинаКодировки);
Кодировка = СтрЗаменить(Кодировка, """", "");
Кодировка = СтрЗаменить(Кодировка, "'", "");
КонецЕсли;
Иначе
ТипСодержимого = Заголовок;
КонецЕсли;
Если Кодировка = Неопределено И СтрНайти(ТипСодержимого, "text") Тогда
Кодировка = "iso-8859-1";
КонецЕсли;
Возврат Кодировка;
КонецФункции
Функция СобратьАдресРесурса(СтруктураURL, ПараметрыЗапроса)
АдресРесурса = СтруктураURL.Путь;
ОбъединенныеПараметрыЗапроса = ОбъединитьПараметрыЗапроса(ПараметрыЗапроса, СтруктураURL.ПараметрыЗапроса);
Если ЗначениеЗаполнено(ОбъединенныеПараметрыЗапроса) Тогда
АдресРесурса = АдресРесурса + "?" + КодироватьПараметрыЗапроса(ОбъединенныеПараметрыЗапроса);
КонецЕсли;
Если ЗначениеЗаполнено(СтруктураURL.Фрагмент) Тогда
АдресРесурса = АдресРесурса + "#" + СтруктураURL.Фрагмент;
КонецЕсли;
Возврат АдресРесурса;
КонецФункции
Функция ЗащищенноеСоединение(ДополнительныеПараметры)
Если ДополнительныеПараметры.ПроверятьSSL = Ложь Тогда
СертификатыУЦ = Неопределено;
ИначеЕсли ТипЗнч(ДополнительныеПараметры.ПроверятьSSL) = Тип("СертификатыУдостоверяющихЦентровФайл") Тогда
СертификатыУЦ = ДополнительныеПараметры.ПроверятьSSL;
Иначе
СертификатыУЦ = Новый СертификатыУдостоверяющихЦентровОС;
КонецЕсли;
КлиентскийСертификат = Неопределено;
Если ДополнительныеПараметры.КлиентскийСертификатSSL = Тип("СертификатКлиентаФайл")
ИЛИ ДополнительныеПараметры.КлиентскийСертификатSSL = Тип("СертификатКлиентаWindows") Тогда
КлиентскийСертификат = ДополнительныеПараметры.КлиентскийСертификатSSL;
КонецЕсли;
Возврат Новый ЗащищенноеСоединениеOpenSSL(КлиентскийСертификат, СертификатыУЦ);
КонецФункции
Функция Соединение(ПараметрыСоединения, Аутентификация, ДополнительныеПараметры, Сессия)
Если Не ЗначениеЗаполнено(ПараметрыСоединения.Порт) Тогда
Если ПараметрыСоединения.Схема = "https" Тогда
ПараметрыСоединения.Порт = 443;
Иначе
ПараметрыСоединения.Порт = 80;
КонецЕсли;
КонецЕсли;
ЗащищенноеСоединение = Неопределено;
Если ПараметрыСоединения.Схема = "https" Тогда
ЗащищенноеСоединение = ЗащищенноеСоединение(ДополнительныеПараметры);
КонецЕсли;
Пользователь = "";
Пароль = "";
Если ЗначениеЗаполнено(Аутентификация) Тогда
Если Аутентификация.Свойство("Пользователь") И Аутентификация.Свойство("Пароль") Тогда
Пользователь = Аутентификация.Пользователь;
Пароль = Аутентификация.Пароль;
КонецЕсли;
КонецЕсли;
ИспользоватьАутентификациюОС = Аутентификация.Свойство("ИспользоватьАутентификациюОС")
И Аутентификация.ИспользоватьАутентификациюОС = Истина;
ПараметрыДляРасчетаИдентификатора = Новый Массив;
ПараметрыДляРасчетаИдентификатора.Добавить(ПараметрыСоединения.Сервер);
ПараметрыДляРасчетаИдентификатора.Добавить(ПараметрыСоединения.Порт);
ПараметрыДляРасчетаИдентификатора.Добавить(Пользователь);
ПараметрыДляРасчетаИдентификатора.Добавить(Пароль);
ПараметрыДляРасчетаИдентификатора.Добавить(ДополнительныеПараметры.Таймаут);
ПараметрыДляРасчетаИдентификатора.Добавить(ИспользоватьАутентификациюОС);
ПараметрыДляРасчетаИдентификатора.Добавить(ЗащищенноеСоединение);
ПараметрыДляРасчетаИдентификатора.Добавить(ДополнительныеПараметры.Прокси);
Если Не Сессия.Свойство("СлужебныеДанные") ИЛИ ТипЗнч(Сессия.СлужебныеДанные) <> Тип("Структура") Тогда
Сессия.Вставить("СлужебныеДанные", Новый Структура);
КонецЕсли;
Если Не Сессия.СлужебныеДанные.Свойство("ПулСоединений") Тогда
Сессия.СлужебныеДанные.Вставить("ПулСоединений", Новый Соответствие);
КонецЕсли;
ПулСоединений = Сессия.СлужебныеДанные.ПулСоединений;
ИдентификаторСоединения = ИдентификаторСоединения(ПараметрыДляРасчетаИдентификатора);
Если ПулСоединений.Получить(ИдентификаторСоединения) = Неопределено Тогда
НовоеСоединение = Новый HTTPСоединение(
ПараметрыСоединения.Сервер,
ПараметрыСоединения.Порт,
Пользователь, Пароль,
ДополнительныеПараметры.Прокси,
ДополнительныеПараметры.Таймаут,
ЗащищенноеСоединение,
ИспользоватьАутентификациюОС);
ПулСоединений.Вставить(ИдентификаторСоединения, НовоеСоединение);
КонецЕсли;
Возврат ПулСоединений[ИдентификаторСоединения];
КонецФункции
Функция ИдентификаторСоединения(ПараметрыСоединения)
ПараметрыДляРасчетаИдентификатора = Новый Массив;
Для Каждого Элемент Из ПараметрыСоединения Цикл
ТипЭлемента = ТипЗнч(Элемент);
Если ТипЭлемента = Тип("ИнтернетПрокси") Тогда
ПараметрыДляРасчетаИдентификатора.Добавить(СтрСоединить(Элемент.НеИспользоватьПроксиДляАдресов, ""));
ПараметрыДляРасчетаИдентификатора.Добавить(XMLСтрока(Элемент.НеИспользоватьПроксиДляЛокальныхАдресов));
ПараметрыДляРасчетаИдентификатора.Добавить(Элемент.Пользователь);
ПараметрыДляРасчетаИдентификатора.Добавить(Элемент.Пароль);
ИначеЕсли ТипЭлемента = Тип("ЗащищенноеСоединениеOpenSSL") Тогда
// Для упрощения будет считать, что сертификаты в рамках сессии не меняются
Если Элемент.СертификатКлиента = Неопределено Тогда
ПараметрыДляРасчетаИдентификатора.Добавить("");
Иначе
ПараметрыДляРасчетаИдентификатора.Добавить(Строка(ТипЗнч(Элемент.СертификатКлиента)));
КонецЕсли;
Если Элемент.СертификатыУдостоверяющихЦентров = Неопределено Тогда
ПараметрыДляРасчетаИдентификатора.Добавить("");
Иначе
ПараметрыДляРасчетаИдентификатора.Добавить(Строка(ТипЗнч(Элемент.СертификатыУдостоверяющихЦентров)));
КонецЕсли;
Иначе
ПараметрыДляРасчетаИдентификатора.Добавить(XMLСтрока(Элемент));
КонецЕсли;
КонецЦикла;
Возврат ХешированиеДанных(ХешФункция.MD5, СтрСоединить(ПараметрыДляРасчетаИдентификатора, ""));
КонецФункции
Функция ВыбратьЗначение(ОсновноеЗначение, ДополнительныеЗначения, Ключ, ЗначениеПоУмолчанию)
Если ОсновноеЗначение <> Неопределено Тогда
Возврат ОсновноеЗначение;
КонецЕсли;
Значение = ЗначениеПоКлючу(ДополнительныеЗначения, Ключ);
Если Значение <> Неопределено Тогда
Возврат Значение;
КонецЕсли;
Возврат ЗначениеПоУмолчанию;
КонецФункции
Функция ЗаполнитьПараметрыЗапроса(Путь)
ПараметрыЗапроса = Новый Соответствие;
Запрос = "";
РазбитьСтрокуПоРазделителю(Запрос, Путь, "?", Истина);
Для Каждого СтрокаКлючРавноПараметр Из СтрРазделить(Запрос, "&", Ложь) Цикл
СтрокаКлючРавноПараметр = РаскодироватьСтроку(
СтрокаКлючРавноПараметр, СпособКодированияСтроки.URLВКодировкеURL);
ПозицияРавно = СтрНайти(СтрокаКлючРавноПараметр, "=");
Если ПозицияРавно = 0 Тогда
Ключ = СтрокаКлючРавноПараметр;
Значение = Неопределено;
Иначе
Ключ = Лев(СтрокаКлючРавноПараметр, ПозицияРавно - 1);
Значение = Сред(СтрокаКлючРавноПараметр, ПозицияРавно + 1);
КонецЕсли;
Если ПараметрыЗапроса.Получить(Ключ) <> Неопределено Тогда
Если ТипЗнч(ПараметрыЗапроса[Ключ]) = Тип("Массив") Тогда
ПараметрыЗапроса[Ключ].Добавить(Значение);
Иначе
Значения = Новый Массив;
Значения.Добавить(ПараметрыЗапроса[Ключ]);
Значения.Добавить(Значение);
ПараметрыЗапроса[Ключ] = Значения;
КонецЕсли;
Иначе
ПараметрыЗапроса.Вставить(Ключ, Значение);
КонецЕсли;
КонецЦикла;
Возврат ПараметрыЗапроса;
КонецФункции
Процедура РазбитьСтрокуПоРазделителю(ИзвлекаемаяЧасть, ОстальнаяЧасть, Разделитель, Инверсия = Ложь)
Индекс = СтрНайти(ОстальнаяЧасть, Разделитель);
Если Индекс Тогда
ИзвлекаемаяЧасть = Лев(ОстальнаяЧасть, Индекс - 1);
ОстальнаяЧасть = Сред(ОстальнаяЧасть, Индекс + СтрДлина(Разделитель));
Если Инверсия Тогда
ДляОбмена = ИзвлекаемаяЧасть;
ИзвлекаемаяЧасть = ОстальнаяЧасть;
ОстальнаяЧасть = ДляОбмена;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Функция РазделитьПоПервомуНайденномуРазделителю(Строка, Разделители)
МинимальныйИндекс = СтрДлина(Строка);
ПервыйРазделитель = "";
Для Каждого Разделитель Из Разделители Цикл
Индекс = СтрНайти(Строка, Разделитель);
Если Индекс = 0 Тогда
Продолжить;
КонецЕсли;
Если Индекс < МинимальныйИндекс Тогда
МинимальныйИндекс = Индекс;
ПервыйРазделитель = Разделитель;
КонецЕсли;
КонецЦикла;
Результат = Новый Массив;
Если ЗначениеЗаполнено(ПервыйРазделитель) Тогда
Результат.Добавить(Лев(Строка, МинимальныйИндекс - 1));
Результат.Добавить(Сред(Строка, МинимальныйИндекс + СтрДлина(ПервыйРазделитель)));
Результат.Добавить(ПервыйРазделитель);
Иначе
Результат.Добавить(Строка);
Результат.Добавить("");
Результат.Добавить(Неопределено);
КонецЕсли;
Возврат Результат;
КонецФункции
Функция ДополнитьПараметрыПреобразованияJSON(ПараметрыПреобразования)
ПараметрыПреобразованияJSON = ПараметрыПреобразованияJSONПоУмолчанию();
Если ЗначениеЗаполнено(ПараметрыПреобразования) Тогда
Для Каждого Параметр Из ПараметрыПреобразования Цикл
Если ПараметрыПреобразованияJSON.Свойство(Параметр.Ключ) Тогда
ПараметрыПреобразованияJSON.Вставить(Параметр.Ключ, Параметр.Значение);
КонецЕсли;
КонецЦикла;
КонецЕсли;
Возврат ПараметрыПреобразованияJSON;
КонецФункции
Функция ДополнитьПараметрыЗаписиJSON(ПараметрыЗаписи)
ПараметрыЗаписиJSON = ПараметрыЗаписиJSONПоУмолчанию();
Если ЗначениеЗаполнено(ПараметрыЗаписи) Тогда
Для Каждого Параметр Из ПараметрыЗаписи Цикл
Если ПараметрыЗаписиJSON.Свойство(Параметр.Ключ) Тогда
ПараметрыЗаписиJSON.Вставить(Параметр.Ключ, Параметр.Значение);
КонецЕсли;
КонецЦикла;
КонецЕсли;
Возврат ПараметрыЗаписиJSON;
КонецФункции
#Область АутентификацияAWS4
Функция КлючПодписиAWS4(СекретныйКлюч, Дата, Регион, Сервис)
КлючДата = ПодписатьСообщениеHMAC("AWS4" + СекретныйКлюч, Дата);
КлючРегион = ПодписатьСообщениеHMAC(КлючДата, Регион);
КлючСервис = ПодписатьСообщениеHMAC(КлючРегион, Сервис);
Возврат ПодписатьСообщениеHMAC(КлючСервис, "aws4_request");
КонецФункции
Функция ПодписатьСообщениеHMAC(Знач Ключ, Знач Сообщение, Знач Алгоритм = Неопределено)
Если Алгоритм = Неопределено Тогда
Алгоритм = ХешФункция.SHA256;
КонецЕсли;
Если ТипЗнч(Ключ) = Тип("Строка") Тогда
Ключ = ПолучитьДвоичныеДанныеИзСтроки(Ключ, КодировкаТекста.UTF8, Ложь);
КонецЕсли;
Если ТипЗнч(Сообщение) = Тип("Строка") Тогда
Сообщение = ПолучитьДвоичныеДанныеИзСтроки(Сообщение, КодировкаТекста.UTF8, Ложь);
КонецЕсли;
Возврат HMAC(Ключ, Сообщение, Алгоритм);
КонецФункции
Процедура ПодготовитьАутентификациюAWS4(ПодготовленныйЗапрос)
ЗначениеЗаголовка = ЗначениеЗаголовка("x-amz-date", ПодготовленныйЗапрос.Заголовки);
Если ЗначениеЗаголовка <> Ложь Тогда
ТекущееВремя = Дата(СтрЗаменить(СтрЗаменить(ЗначениеЗаголовка, "T", ""), "Z", ""));
Иначе
ТекущееВремя = ТекущаяУниверсальнаяДата();
КонецЕсли;
ПодготовленныйЗапрос.Заголовки["x-amz-date"] = Формат(ТекущееВремя, "ДФ=yyyyMMddTHHmmssZ");
ОбластьДействияДата = Формат(ТекущееВремя, "ДФ=yyyyMMdd");
ПодготовленныйЗапрос.Заголовки["x-amz-content-sha256"] =
ХешированиеДанных(ХешФункция.SHA256, ПодготовленныйЗапрос.HTTPЗапрос.ПолучитьТелоКакПоток());
СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
КаноническиеЗаголовки = КаноническиеЗаголовкиAWS4(ПодготовленныйЗапрос.Заголовки, СтруктураURL);
КаноническийПуть = СтруктураURL.Путь;
КаноническиеПараметрыЗапроса = КаноническиеПараметрыЗапросаAWS4(СтруктураURL.ПараметрыЗапроса);
ЧастиЗапроса = Новый Массив;
ЧастиЗапроса.Добавить(ПодготовленныйЗапрос.Метод);
ЧастиЗапроса.Добавить(КаноническийПуть);
ЧастиЗапроса.Добавить(КаноническиеПараметрыЗапроса);
ЧастиЗапроса.Добавить(КаноническиеЗаголовки.КаноническиеЗаголовки);
ЧастиЗапроса.Добавить(КаноническиеЗаголовки.ПодписываемыеЗаголовки);
ЧастиЗапроса.Добавить(ПодготовленныйЗапрос.Заголовки["x-amz-content-sha256"]);
КаноническийЗапрос = СтрСоединить(ЧастиЗапроса, Символы.ПС);
ЧастиОбластиДействия = Новый Массив;
ЧастиОбластиДействия.Добавить(ОбластьДействияДата);
ЧастиОбластиДействия.Добавить(ПодготовленныйЗапрос.Аутентификация.Регион);
ЧастиОбластиДействия.Добавить(ПодготовленныйЗапрос.Аутентификация.Сервис);
ЧастиОбластиДействия.Добавить("aws4_request");
ОбластьДействия = СтрСоединить(ЧастиОбластиДействия, "/");
ЧастиСтрокиДляПодписи = Новый Массив;
ЧастиСтрокиДляПодписи.Добавить(ПодготовленныйЗапрос.Аутентификация.Тип);
ЧастиСтрокиДляПодписи.Добавить(ПодготовленныйЗапрос.Заголовки["x-amz-date"]);
ЧастиСтрокиДляПодписи.Добавить(ОбластьДействия);
ЧастиСтрокиДляПодписи.Добавить(ХешированиеДанных(ХешФункция.SHA256, КаноническийЗапрос));
СтрокаДляПодписи = СтрСоединить(ЧастиСтрокиДляПодписи, Символы.ПС);
Ключ = КлючПодписиAWS4(
ПодготовленныйЗапрос.Аутентификация.СекретныйКлюч,
ОбластьДействияДата,
ПодготовленныйЗапрос.Аутентификация.Регион,
ПодготовленныйЗапрос.Аутентификация.Сервис);
Подпись = НРег(ПолучитьHexСтрокуИзДвоичныхДанных(ПодписатьСообщениеHMAC(Ключ, СтрокаДляПодписи)));
ПодготовленныйЗапрос.Заголовки["Authorization"] = СтрШаблон(
"%1 Credential=%2/%3, SignedHeaders=%4, Signature=%5",
ПодготовленныйЗапрос.Аутентификация.Тип,
ПодготовленныйЗапрос.Аутентификация.ИдентификаторКлючаДоступа,
ОбластьДействия,
КаноническиеЗаголовки.ПодписываемыеЗаголовки,
Подпись);
ПодготовленныйЗапрос.HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки;
КонецПроцедуры
Функция ЭтоСтандартныйПорт(СтруктураURL)
СтандартныйПортHTTP = 80;
СтандартныйПортHTTPS = 443;
Возврат (СтруктураURL.Схема = "http" И СтруктураURL.Порт = СтандартныйПортHTTP)
ИЛИ (СтруктураURL.Схема = "https" И СтруктураURL.Порт = СтандартныйПортHTTPS);
КонецФункции
Функция СформироватьЗначениеЗаголовкаHost(СтруктураURL)
Host = СтруктураURL.Сервер;
Если ЗначениеЗаполнено(СтруктураURL.Порт) И НЕ ЭтоСтандартныйПорт(СтруктураURL) Тогда
Host = Host + ":" + Формат(СтруктураURL.Порт, "ЧРГ=; ЧГ=");
КонецЕсли;
Возврат Host;
КонецФункции
Функция КаноническиеЗаголовкиAWS4(Заголовки, СтруктураURL)
Список = Новый СписокЗначений;
ЗаголовокHostЕстьВЗапросе = Ложь;
ЗаголовкиПоУмолчанию = ЗаголовкиПоУмолчаниюAWS4();
Для Каждого ОчереднойЗаголовок Из Заголовки Цикл
Заголовок = НРег(ОчереднойЗаголовок.Ключ);
Если ЗаголовкиПоУмолчанию.Исключения.Найти(Заголовок) <> Неопределено Тогда
Продолжить;
КонецЕсли;
ЗаголовокHostЕстьВЗапросе = Макс(ЗаголовокHostЕстьВЗапросе, Заголовок = "host");
Если ЗаголовкиПоУмолчанию.Равно.Найти(Заголовок) <> Неопределено Тогда
Список.Добавить(Заголовок, СокрЛП(ОчереднойЗаголовок.Значение));
Иначе
Для Каждого Префикс Из ЗаголовкиПоУмолчанию.НачинаетсяС Цикл
Если СтрНачинаетсяС(Заголовок, Префикс) Тогда
Список.Добавить(Заголовок, СокрЛП(ОчереднойЗаголовок.Значение));
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
Если Не ЗаголовокHostЕстьВЗапросе Тогда
Список.Добавить("host", СформироватьЗначениеЗаголовкаHost(СтруктураURL));
КонецЕсли;
Список.СортироватьПоЗначению(НаправлениеСортировки.Возр);
КаноническиеЗаголовки = Новый Массив;
ПодписываемыеЗаголовки = Новый Массив;
Для Каждого ЭлементСписка Из Список Цикл
КаноническиеЗаголовки.Добавить(ЭлементСписка.Значение + ":" + ЭлементСписка.Представление);
ПодписываемыеЗаголовки.Добавить(ЭлементСписка.Значение);
КонецЦикла;
КаноническиеЗаголовки.Добавить("");
КаноническиеЗаголовки = СтрСоединить(КаноническиеЗаголовки, Символы.ПС);
ПодписываемыеЗаголовки = СтрСоединить(ПодписываемыеЗаголовки, ";");
Возврат Новый Структура(
"КаноническиеЗаголовки, ПодписываемыеЗаголовки",
КаноническиеЗаголовки, ПодписываемыеЗаголовки);
КонецФункции
Функция КаноническиеПараметрыЗапросаAWS4(ПараметрыЗапроса)
Список = Новый СписокЗначений;
Для Каждого ОчереднойПараметрЗапроса Из ПараметрыЗапроса Цикл
Список.Добавить(ОчереднойПараметрЗапроса.Ключ, СокрЛП(ОчереднойПараметрЗапроса.Значение));
КонецЦикла;
Список.СортироватьПоЗначению(НаправлениеСортировки.Возр);
КаноническиеПараметры = Новый Массив;
Для Каждого ЭлементСписка Из Список Цикл
ЗначениеПараметра = КодироватьСтроку(ЭлементСписка.Представление, СпособКодированияСтроки.КодировкаURL);
КаноническиеПараметры.Добавить(ЭлементСписка.Значение + "=" + ЗначениеПараметра);
КонецЦикла;
Возврат СтрСоединить(КаноническиеПараметры, "&");
КонецФункции
Функция ЗаголовкиПоУмолчаниюAWS4()
Заголовки = Новый Структура;
Заголовки.Вставить("Равно", СтрРазделить("host,content-type,date", ","));
Заголовки.Вставить("НачинаетсяС", СтрРазделить("x-amz-", ","));
Заголовки.Вставить("Исключения", СтрРазделить("x-amz-client-context", ","));
Возврат Заголовки;
КонецФункции
#КонецОбласти
#Область КодированиеДекодированиеДанных
#Область СлужебныеСтруктурыZip
// Описание структур см. здесь https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
Функция ZipРазмерLFH()
Возврат 34;
КонецФункции
Функция ZipРазмерDD()
Возврат 16;
КонецФункции
Функция ZipРазмерCDH()
Возврат 50;
КонецФункции
Функция ZipРазмерEOCD()
Возврат 22;
КонецФункции
Функция ZipLFH()
// Local file header
Буфер = Новый БуферДвоичныхДанных(ZipРазмерLFH());
Буфер.ЗаписатьЦелое32(0, 67324752); // signature 0x04034b50
Буфер.ЗаписатьЦелое16(4, 20); // version
Буфер.ЗаписатьЦелое16(6, 10); // bit flags
Буфер.ЗаписатьЦелое16(8, 8); // compression method
Буфер.ЗаписатьЦелое16(10, 0); // time
Буфер.ЗаписатьЦелое16(12, 0); // date
Буфер.ЗаписатьЦелое32(14, 0); // crc-32
Буфер.ЗаписатьЦелое32(18, 0); // compressed size
Буфер.ЗаписатьЦелое32(22, 0); // uncompressed size
Буфер.ЗаписатьЦелое16(26, 4); // filename legth - "data"
Буфер.ЗаписатьЦелое16(28, 0); // extra field length
Буфер.Записать(30, ПолучитьБуферДвоичныхДанныхИзСтроки("data", "ascii", Ложь));
Возврат Буфер;
КонецФункции
Функция ZipDD(CRC32, РазмерСжатыхДанных, РазмерНесжатыхДанных)
// Data descriptor
Буфер = Новый БуферДвоичныхДанных(ZipРазмерDD());
Буфер.ЗаписатьЦелое32(0, 134695760);
Буфер.ЗаписатьЦелое32(4, CRC32);
Буфер.ЗаписатьЦелое32(8, РазмерСжатыхДанных);
Буфер.ЗаписатьЦелое32(12, РазмерНесжатыхДанных);
Возврат Буфер;
КонецФункции
Функция ZipCDH(CRC32, РазмерСжатыхДанных, РазмерНесжатыхДанных)
// Central directory header
Буфер = Новый БуферДвоичныхДанных(ZipРазмерCDH());
Буфер.ЗаписатьЦелое32(0, 33639248); // signature 0x02014b50
Буфер.ЗаписатьЦелое16(4, 798); // version made by
Буфер.ЗаписатьЦелое16(6, 20); // version needed to extract
Буфер.ЗаписатьЦелое16(8, 10); // bit flags
Буфер.ЗаписатьЦелое16(10, 8); // compression method
Буфер.ЗаписатьЦелое16(12, 0); // time
Буфер.ЗаписатьЦелое16(14, 0); // date
Буфер.ЗаписатьЦелое32(16, CRC32); // crc-32
Буфер.ЗаписатьЦелое32(20, РазмерСжатыхДанных); // compressed size
Буфер.ЗаписатьЦелое32(24, РазмерНесжатыхДанных); // uncompressed size
Буфер.ЗаписатьЦелое16(28, 4); // file name length
Буфер.ЗаписатьЦелое16(30, 0); // extra field length
Буфер.ЗаписатьЦелое16(32, 0); // file comment length
Буфер.ЗаписатьЦелое16(34, 0); // disk number start
Буфер.ЗаписатьЦелое16(36, 0); // internal file attributes
Буфер.ЗаписатьЦелое32(38, 2176057344); // external file attributes
Буфер.ЗаписатьЦелое32(42, 0); // relative offset of local header
Буфер.Записать(46, ПолучитьБуферДвоичныхДанныхИзСтроки("data", "ascii", Ложь));
Возврат Буфер;
КонецФункции
Функция ZipEOCD(РазмерСжатыхДанных)
// End of central directory
РазмерCDH = 50;
Буфер = Новый БуферДвоичныхДанных(ZipРазмерEOCD());
Буфер.ЗаписатьЦелое32(0, 101010256); // signature 0x06054b50
Буфер.ЗаписатьЦелое16(4, 0); // number of this disk
Буфер.ЗаписатьЦелое16(6, 0); // number of the disk with the start of the central directory
Буфер.ЗаписатьЦелое16(8, 1); // total number of entries in the central directory on this disk
Буфер.ЗаписатьЦелое16(10, 1); // total number of entries in the central directory
Буфер.ЗаписатьЦелое32(12, РазмерCDH); // size of the central directory
// offset of start of central directory with respect to the starting disk number
Буфер.ЗаписатьЦелое32(16, ZipРазмерLFH() + РазмерСжатыхДанных + ZipРазмерDD());
Буфер.ЗаписатьЦелое16(20, 0); // the starting disk number
Возврат Буфер;
КонецФункции
#КонецОбласти
#Область СлужебныеСтруктурыGZip
// Описание структур см. здесь https://www.ietf.org/rfc/rfc1952.txt
Функция GZipРазмерHeader()
Возврат 10;
КонецФункции
Функция GZipРазмерFooter()
Возврат 8;
КонецФункции
Функция GZipHeader()
Буфер = Новый БуферДвоичныхДанных(GZipРазмерHeader());
Буфер[0] = 31; // ID1 0x1f
Буфер[1] = 139; // ID2 0x8b
Буфер[2] = 8; // compression method (08 for DEFLATE)
Буфер[3] = 0; // header flags
Буфер.ЗаписатьЦелое32(4, 0); // timestamp
Буфер[8] = 0; // compression flags
Буфер[9] = 255; // operating system ID
Возврат Буфер;
КонецФункции
Функция GZipFooter(CRC32, РазмерИсходныхДанных)
Буфер = Новый БуферДвоичныхДанных(GZipРазмерFooter());
Буфер.ЗаписатьЦелое32(0, CRC32);
Буфер.ЗаписатьЦелое32(4, РазмерИсходныхДанных);
Возврат Буфер;
КонецФункции
#КонецОбласти
Функция ПрочитатьZip(СжатыеДанные, ТекстОшибки = Неопределено)
#Если МобильноеПриложениеСервер Тогда
ВызватьИсключение(НСтр("ru = 'Работа с Zip-файлами в мобильной платформе не поддерживается'"));
#Иначе
Каталог = ПолучитьИмяВременногоФайла();
ЧтениеZip = Новый ЧтениеZipФайла(СжатыеДанные);
ИмяФайла = ЧтениеZip.Элементы[0].Имя;
Попытка
ЧтениеZip.Извлечь(ЧтениеZip.Элементы[0], Каталог, РежимВосстановленияПутейФайловZIP.НеВосстанавливать);
Исключение
// Игнорируем проверку целостности архива, просто читаем результат
ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
ЧтениеZip.Закрыть();
Результат = Новый ДвоичныеДанные(Каталог + ПолучитьРазделительПути() + ИмяФайла);
УдалитьФайлы(Каталог);
Возврат Результат;
#КонецЕсли
КонецФункции
Функция ЗаписатьZip(Данные)
#Если МобильноеПриложениеСервер Тогда
ВызватьИсключение(НСтр("ru = 'Работа с Zip-файлами в мобильной платформе не поддерживается'"));
#Иначе
ВременныйФайл = ПолучитьИмяВременногоФайла(".bin");
Данные.Записать(ВременныйФайл);
ПотокZip = Новый ПотокВПамяти;
ЗаписьZip = Новый ЗаписьZipФайла(ПотокZip);
ЗаписьZip.Добавить(ВременныйФайл);
ЗаписьZip.Записать();
УдалитьФайлы(ВременныйФайл);
Возврат ПотокZip.ЗакрытьИПолучитьДвоичныеДанные();
#КонецЕсли
КонецФункции
#КонецОбласти
#Область ОбработчикиСобытий
Процедура ОбработкаОтветаСКодом401(Сессия, ПодготовленныйЗапрос, Настройки, Ответ)
Если ЭтоРедирект(Ответ.КодСостояния, Ответ.Заголовки) Тогда
Возврат;
КонецЕсли;
КодыСостоянияHTTP = КодыСостоянияHTTP();
Если Ответ.КодСостояния < КодыСостоянияHTTP.НеверныйЗапрос_400
ИЛИ Ответ.КодСостояния >= КодыСостоянияHTTP.ВнутренняяОшибкаСервера_500 Тогда
Возврат;
КонецЕсли;
Значение = ЗначениеЗаголовка("www-authenticate", Ответ.Заголовки);
Если Значение <> Ложь И СтрНайти(НРег(Значение), "digest") Тогда
Позиция = СтрНайти(НРег(Значение), "digest");
Значение = Сред(Значение, Позиция + СтрДлина("digest") + 1);
Значение = СтрЗаменить(Значение, """", "");
Значение = СтрЗаменить(Значение, Символы.ПС, "");
ПараметрыDigest = Новый Структура("algorithm,realm,nonce,qop,opaque");
Для Каждого Часть Из РазбитьСтрокуПоСтроке(Значение, ", ") Цикл
КлючЗначение = СтрРазделить(Часть, "=");
ПараметрыDigest.Вставить(КлючЗначение[0], КлючЗначение[1]);
КонецЦикла;
Сессия.СлужебныеДанные.ПараметрыDigest = ПараметрыDigest;
ПодготовленныйЗапрос.Заголовки.Вставить("Authorization", ПодготовитьЗаголовокDigest(Сессия, ПодготовленныйЗапрос));
ПодготовленныйЗапрос.HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки;
Ответ = ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки);
КонецЕсли;
КонецПроцедуры
Функция ОпределитьХешФункцию(Знач Алгоритм)
Алгоритм = ВРег(Алгоритм);
Если Не ЗначениеЗаполнено(Алгоритм) ИЛИ Алгоритм = "MD5" ИЛИ Алгоритм = "MD5-SESS" Тогда
Возврат ХешФункция.MD5;
ИначеЕсли Алгоритм = "SHA" Тогда
Возврат ХешФункция.SHA1;
ИначеЕсли Алгоритм = "SHA-256" Тогда
Возврат ХешФункция.SHA256;
Иначе
Возврат Неопределено;
КонецЕсли;
КонецФункции
Функция ПодготовитьЗаголовокDigest(Сессия, ПодготовленныйЗапрос)
ПараметрыDigest = Сессия.СлужебныеДанные.ПараметрыDigest;
Алгоритм = ОпределитьХешФункцию(ПараметрыDigest.algorithm);
АлгоритмСтрокой = ВРег(ПараметрыDigest.algorithm);
Если Алгоритм = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL);
Путь = СтруктураURL.Путь;
Если ЗначениеЗаполнено(СтруктураURL.ПараметрыЗапроса) Тогда
Путь = Путь + "?" + КодироватьПараметрыЗапроса(СтруктураURL.ПараметрыЗапроса);
КонецЕсли;
A1 = СтрШаблон("%1:%2:%3",
ПодготовленныйЗапрос.Аутентификация.Пользователь,
ПараметрыDigest.realm,
ПодготовленныйЗапрос.Аутентификация.Пароль);
A2 = СтрШаблон("%1:%2", ПодготовленныйЗапрос.Метод, Путь);
HA1 = ХешированиеДанных(Алгоритм, A1);
HA2 = ХешированиеДанных(Алгоритм, A2);
Если Не ПараметрыDigest.Свойство("last_nonce") Тогда
ПараметрыDigest.Вставить("last_nonce");
КонецЕсли;
Если ПараметрыDigest.nonce = ПараметрыDigest.last_nonce Тогда
ПараметрыDigest.nonce_count = ПараметрыDigest.nonce_count + 1;
Иначе
ПараметрыDigest.Вставить("nonce_count", 1);
КонецЕсли;
ЗначениеNC = Формат(ПараметрыDigest.nonce_count, "ЧЦ=8; ЧВН=; ЧГ=");
ЗначениеNonce = Лев(СтрЗаменить(НРег(Новый УникальныйИдентификатор), "-", ""), 16);
Если АлгоритмСтрокой = "MD5-SESS" Тогда
HA1 = ХешированиеДанных(Алгоритм, СтрШаблон("%1:%2:%3", HA1, ПараметрыDigest.nonce, ЗначениеNonce));
КонецЕсли;
Если Не ЗначениеЗаполнено(ПараметрыDigest.qop) Тогда
ЗначениеResponse = ХешированиеДанных(Алгоритм, СтрШаблон("%1:%2:%3", HA1, ПараметрыDigest.nonce, HA2));
ИначеЕсли ПараметрыDigest.qop = "auth"
ИЛИ СтрРазделить(ПараметрыDigest.qop, ",", Ложь).Найти("auth") <> Неопределено Тогда
ЗначениеNonceBit = СтрШаблон("%1:%2:%3:%4:%5", ПараметрыDigest.nonce, ЗначениеNC, ЗначениеNonce, "auth", HA2);
ЗначениеResponse = ХешированиеДанных(Алгоритм, СтрШаблон("%1:%2", HA1, ЗначениеNonceBit));
Иначе
// INFO: auth-int не реализовано
Возврат Неопределено;
КонецЕсли;
ПараметрыDigest.last_nonce = ПараметрыDigest.nonce;
База = СтрШаблон("username=""%1"", realm=""%2"", nonce=""%3"", uri=""%4"", response=""%5""",
ПодготовленныйЗапрос.Аутентификация.Пользователь,
ПараметрыDigest.realm,
ПараметрыDigest.nonce,
Путь,
ЗначениеResponse);
Строки = Новый Массив;
Строки.Добавить(База);
Если ЗначениеЗаполнено(ПараметрыDigest.opaque) Тогда
Строки.Добавить(СтрШаблон(", opaque=""%1""", ПараметрыDigest.opaque));
КонецЕсли;
Если ЗначениеЗаполнено(ПараметрыDigest.algorithm) Тогда
Строки.Добавить(СтрШаблон(", algorithm=""%1""", ПараметрыDigest.algorithm));
КонецЕсли;
Если ЗначениеЗаполнено(ПараметрыDigest.qop) Тогда
Строки.Добавить(СтрШаблон(", qop=""auth"", nc=%1, cnonce=""%2""", ЗначениеNC, ЗначениеNonce));
КонецЕсли;
Возврат СтрШаблон("Digest %1", СтрСоединить(Строки, ""));
КонецФункции
Функция ХешированиеДанных(Знач Алгоритм, Знач Данные)
Если ТипЗнч(Данные) = Тип("Строка") Тогда
Данные = ПолучитьДвоичныеДанныеИзСтроки(Данные, КодировкаТекста.UTF8, Ложь);
КонецЕсли;
Хеширование = Новый ХешированиеДанных(Алгоритм);
Хеширование.Добавить(Данные);
Возврат НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма));
КонецФункции
Функция РазбитьСтрокуПоСтроке(Знач Строка, Разделитель)
Результат = Новый Массив;
Пока Истина Цикл
Позиция = СтрНайти(Строка, Разделитель);
Если Позиция = 0 И ЗначениеЗаполнено(Строка) Тогда
Результат.Добавить(Строка);
Прервать;
КонецЕсли;
ПерваяЧасть = Лев(Строка, Позиция - СтрДлина(Разделитель) + 1);
Результат.Добавить(ПерваяЧасть);
Строка = Сред(Строка, Позиция + СтрДлина(Разделитель));
КонецЦикла;
Возврат Результат;
КонецФункции
#КонецОбласти
Функция РаспаковатьОтвет(Ответ)
Заголовок = ЗначениеЗаголовка("content-encoding", Ответ.Заголовки);
Если Заголовок <> Ложь Тогда
Если НРег(Заголовок) = "gzip" Тогда
Возврат ПрочитатьGZip(Ответ.Тело);
КонецЕсли;
КонецЕсли;
Возврат Ответ.Тело;
КонецФункции
Процедура УпаковатьЗапрос(Запрос)
Заголовок = ЗначениеЗаголовка("content-encoding", Запрос.Заголовки);
Если Заголовок <> Ложь Тогда
Если НРег(Заголовок) = "gzip" Тогда
Запрос.УстановитьТелоИзДвоичныхДанных(ЗаписатьGZip(Запрос.ПолучитьТелоКакДвоичныеДанные()));
КонецЕсли;
КонецЕсли;
КонецПроцедуры
#Область ПараметрыПоУмолчанию
Функция ЗаголовкиПоУмолчанию()
Заголовки = Новый Соответствие;
#Если МобильноеПриложениеСервер Тогда
Заголовки.Вставить("Accept-Encoding", "identity");
#Иначе
Заголовки.Вставить("Accept-Encoding", "gzip");
#КонецЕсли
Заголовки.Вставить("Accept", "*/*");
Заголовки.Вставить("Connection", "keep-alive");
Возврат Заголовки;
КонецФункции
Функция МаксимальноеКоличествоПеренаправлений()
Возврат 30;
КонецФункции
Функция СтандартныйТаймаут()
Возврат 30;
КонецФункции
Функция ПараметрыПреобразованияJSONПоУмолчанию()
ПараметрыПреобразованияПоУмолчанию = Новый Структура;
ПараметрыПреобразованияПоУмолчанию.Вставить("ПрочитатьВСоответствие", Истина);
ПараметрыПреобразованияПоУмолчанию.Вставить("ФорматДатыJSON", ФорматДатыJSON.ISO);
ПараметрыПреобразованияПоУмолчанию.Вставить("ИменаСвойствСоЗначениямиДата", Неопределено);
Возврат ПараметрыПреобразованияПоУмолчанию;
КонецФункции
Функция ПараметрыЗаписиJSONПоУмолчанию()
ПараметрыЗаписиJSONПоУмолчанию = Новый Структура;
ПараметрыЗаписиJSONПоУмолчанию.Вставить("ПереносСтрок", ПереносСтрокJSON.Авто);
ПараметрыЗаписиJSONПоУмолчанию.Вставить("СимволыОтступа", " ");
ПараметрыЗаписиJSONПоУмолчанию.Вставить("ИспользоватьДвойныеКавычки", Истина);
ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранированиеСимволов", ЭкранированиеСимволовJSON.Нет);
ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранироватьУгловыеСкобки", Ложь);
ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранироватьРазделителиСтрок", Истина);
ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранироватьАмперсанд", Ложь);
ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранироватьОдинарныеКавычки", Ложь);
ПараметрыЗаписиJSONПоУмолчанию.Вставить("ЭкранироватьСлеш", Ложь);
Возврат ПараметрыЗаписиJSONПоУмолчанию;
КонецФункции
#КонецОбласти
Процедура ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, ПараметрыЗапроса, Данные, Json)
Если ДополнительныеПараметры = Неопределено Тогда
ДополнительныеПараметры = Новый Структура();
КонецЕсли;
Если Не ДополнительныеПараметры.Свойство("ПараметрыЗапроса") Тогда
ДополнительныеПараметры.Вставить("ПараметрыЗапроса", ПараметрыЗапроса);
КонецЕсли;
Если Не ДополнительныеПараметры.Свойство("Данные") Тогда
ДополнительныеПараметры.Вставить("Данные", Данные);
КонецЕсли;
Если Не ДополнительныеПараметры.Свойство("Json") Тогда
ДополнительныеПараметры.Вставить("Json", Json);
КонецЕсли;
КонецПроцедуры
// Когда-нибудь в платформе сделают паузу и это можно будет выкинуть
//
Процедура Приостановить(ДлительностьОстановкиВСекундах)
Если ДлительностьОстановкиВСекундах < 1 Тогда
Возврат;
КонецЕсли;
ТекущаяДата = ТекущаяУниверсальнаяДата();
ЖдатьДо = ТекущаяДата + ДлительностьОстановкиВСекундах;
// BSLLS:UsingHardcodeNetworkAddress-off
ЛокальныйХост = "127.0.0.0";
КакойНибудьСвободныйПорт = 56476;
// BSLLS:UsingHardcodeNetworkAddress-on
Пока ТекущаяДата < ЖдатьДо Цикл
Таймаут = ЖдатьДо - ТекущаяДата;
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
Попытка
Соединение = Новый HTTPСоединение(
ЛокальныйХост, КакойНибудьСвободныйПорт, Неопределено, Неопределено, Неопределено, Таймаут);
Соединение.Получить(Новый HTTPЗапрос("/does_not_matter"));
Исключение
РеальныйТаймаут = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало;
КонецПопытки;
МинимальныйТаймаутВМиллисекундах = 1000;
Если РеальныйТаймаут < МинимальныйТаймаутВМиллисекундах Тогда
ВызватьИсключение(НСтр("ru = 'Процедура Приостановить не работает должным образом'"));
КонецЕсли;
ТекущаяДата = ТекущаяУниверсальнаяДата();
КонецЦикла;
КонецПроцедуры
Функция ТекущаяСессия(Сессия)
Если Сессия = Неопределено Тогда
Сессия = СоздатьСессию();
КонецЕсли;
Возврат Сессия;
КонецФункции
#КонецОбласти
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment