diff --git "a/src/ru/CommonModules/\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200HTTP/Ext/Module.bsl" "b/src/ru/CommonModules/\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200HTTP/Ext/Module.bsl" index 073940f..10914f5 100644 --- "a/src/ru/CommonModules/\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200HTTP/Ext/Module.bsl" +++ "b/src/ru/CommonModules/\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200HTTP/Ext/Module.bsl" @@ -161,6 +161,34 @@ КонецФункции +// Отправляет данные на указанный адрес для обработки с использованием указанного HTTP-метода. +// +// Параметры: +// Метод - Строка - имя HTTP-метода для запроса. +// URL - Строка - URL ресурса, к которому будет отправлен запрос. +// ДополнительныеПараметры - См. НовыеПараметры +// Сессия - См. СоздатьСессию +// +// Возвращаемое значение: +// Структура - ответ на выполненный запрос: +// * ВремяВыполнения - Число - время выполнения запроса в миллисекундах. +// * Cookies - Соответствие - cookies полученные с сервера. +// * Заголовки - Соответствие - HTTP заголовки ответа. +// * ЭтоПостоянныйРедирект - Булево - признак постоянного редиректа. +// * ЭтоРедирект - Булево - признак редиректа. +// * Кодировка - Строка - кодировка текста ответа. +// * Тело - ДвоичныеДанные - тело ответа. +// * КодСостояния - Число - код состояния ответа. +// * URL - Строка - итоговый URL, по которому был выполнен запрос. +// +Функция ВызватьМетод(Метод, URL, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт + + ТекущаяСессия = ТекущаяСессия(Сессия); + ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Неопределено); + Возврат ВызватьHTTPМетод(ТекущаяСессия, Метод, URL, ДополнительныеПараметры); + +КонецФункции + #КонецОбласти #Область УпрощенныеМетодыДляРаботыСЗапросамиВФорматеJSON @@ -259,6 +287,8 @@ #КонецОбласти +#Область Конструкторы + // Конструктор дополнительных параметров // // Возвращаемое значение: @@ -359,34 +389,6 @@ КонецФункции -// Отправляет данные на указанный адрес для обработки с использованием указанного HTTP-метода. -// -// Параметры: -// Метод - Строка - имя HTTP-метода для запроса. -// URL - Строка - URL ресурса, к которому будет отправлен запрос. -// ДополнительныеПараметры - См. НовыеПараметры -// Сессия - См. СоздатьСессию -// -// Возвращаемое значение: -// Структура - ответ на выполненный запрос: -// * ВремяВыполнения - Число - время выполнения запроса в миллисекундах. -// * Cookies - Соответствие - cookies полученные с сервера. -// * Заголовки - Соответствие - HTTP заголовки ответа. -// * ЭтоПостоянныйРедирект - Булево - признак постоянного редиректа. -// * ЭтоРедирект - Булево - признак редиректа. -// * Кодировка - Строка - кодировка текста ответа. -// * Тело - ДвоичныеДанные - тело ответа. -// * КодСостояния - Число - код состояния ответа. -// * URL - Строка - итоговый URL, по которому был выполнен запрос. -// -Функция ВызватьМетод(Метод, URL, ДополнительныеПараметры = Неопределено, Сессия = Неопределено) Экспорт - - ТекущаяСессия = ТекущаяСессия(Сессия); - ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, Неопределено, Неопределено, Неопределено); - Возврат ВызватьHTTPМетод(ТекущаяСессия, Метод, URL, ДополнительныеПараметры); - -КонецФункции - // Создает объект для хранения параметров сессии. // // Возвращаемое значение: @@ -439,6 +441,8 @@ #КонецОбласти +#КонецОбласти + #Область ФорматыОтветов // Возвращает ответ сервера в виде десериализованного значения JSON. @@ -998,315 +1002,260 @@ #Область СлужебныеПроцедурыИФункции -Функция ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(КодСостояния) +#Область РаботаСHTTPЗапросами - Коды = КодыСостоянияHTTP(); - Возврат КодСостояния = Коды.ПолезнаяНагрузкаСлишкомВелика_413 - ИЛИ КодСостояния = Коды.СлишкомМногоЗапросов_429 - ИЛИ КодСостояния = Коды.СервисНедоступен_503; +Процедура ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, ПараметрыЗапроса, Данные, Json) -КонецФункции + Если ДополнительныеПараметры = Неопределено Тогда + ДополнительныеПараметры = Новый Структура(); + КонецЕсли; + Если Не ДополнительныеПараметры.Свойство("ПараметрыЗапроса") Тогда + ДополнительныеПараметры.Вставить("ПараметрыЗапроса", ПараметрыЗапроса); + КонецЕсли; + Если Не ДополнительныеПараметры.Свойство("Данные") Тогда + ДополнительныеПараметры.Вставить("Данные", Данные); + КонецЕсли; + Если Не ДополнительныеПараметры.Свойство("Json") Тогда + ДополнительныеПараметры.Вставить("Json", Json); + КонецЕсли; -Функция ЧислоИзСтроки(Знач Строка) Экспорт +КонецПроцедуры - ОписаниеТипа = Новый ОписаниеТипов("Число"); - Возврат ОписаниеТипа.ПривестиЗначение(Строка); +Функция ВызватьHTTPМетод(Сессия, Метод, URL, ДополнительныеПараметры) -КонецФункции + ПодготовленныйЗапрос = ПодготовитьЗапрос(Сессия, Метод, URL, ДополнительныеПараметры); -Функция ДатаИзСтроки(Знач Строка) Экспорт + НастройкиПодключения = НастройкиПодключения(Метод, URL, ДополнительныеПараметры); - КвалификаторДаты = Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя); - ОписаниеТипа = Новый ОписаниеТипов("Дата", Неопределено, Неопределено, КвалификаторДаты); - Возврат ОписаниеТипа.ПривестиЗначение(Строка); + Ответ = ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, НастройкиПодключения); + + Если НастройкиПодключения.РазрешитьПеренаправление И Ответ.ЭтоРедирект Тогда + // INFO: по хорошему аутентификацию нужно привести к новых параметрам, но пока будем игнорировать. + Ответ = ПеренаправитьЗапрос(Сессия, НастройкиПодключения, ПодготовленныйЗапрос, Ответ); + КонецЕсли; + + Возврат Ответ; КонецФункции -Функция ДатаИзСтрокиRFC7231(Знач Строка) Экспорт - - Разделители = ",-:/\."; - Для Индекс = 1 По СтрДлина(Разделители) Цикл - Разделитель = Сред(Разделители, Индекс, 1); - Строка = СтрЗаменить(Строка, Разделитель, " "); +Функция ПеренаправитьЗапрос(Сессия, НастройкиПодключения, ПодготовленныйЗапрос, ПеренаправленныйОтвет) + + Перенаправление = 0; + + Пока ПеренаправленныйОтвет.ЭтоРедирект Цикл + + ПодготовитьЗапросДляРедиректа(Сессия, ПодготовленныйЗапрос, ПеренаправленныйОтвет); + + ПеренаправленныйОтвет = ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, НастройкиПодключения); + + Перенаправление = Перенаправление + 1; + + Если Перенаправление > Сессия.МаксимальноеКоличествоПеренаправлений Тогда + ВызватьИсключение("СлишкомМногоПеренаправлений"); + КонецЕсли; + КонецЦикла; - Строка = СтрЗаменить(Строка, " ", " "); - СоставляющиеДаты = СтрРазделить(Строка, " "); - МесяцСтр = СоставляющиеДаты[2]; + + Возврат ПеренаправленныйОтвет; + +КонецФункции - Месяцы = СтрРазделить("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", ","); - Месяц = Месяцы.Найти(МесяцСтр); - Если Месяц = Неопределено Тогда - Возврат '00010101'; +Процедура ПодготовитьЗапросДляРедиректа(Сессия, ПодготовленныйЗапрос, ПеренаправленныйОтвет) + + КодыСостоянияHTTP = КодыСостоянияHTTP(); + + НовыйURL = СформироватьНовыйURLПриПеренаправлении(ПеренаправленныйОтвет); + + ПодготовленныйЗапрос.URL = КодироватьСтроку(НовыйURL, СпособКодированияСтроки.URLВКодировкеURL); + НовыйHTTPЗапрос = Новый HTTPЗапрос(СобратьАдресРесурса(РазобратьURL(НовыйURL), Неопределено)); + ПереопределитьМетод(ПодготовленныйЗапрос, ПеренаправленныйОтвет); + + Если ПеренаправленныйОтвет.КодСостояния <> КодыСостоянияHTTP.ВременноеПеренаправление_307 + И ПеренаправленныйОтвет.КодСостояния <> КодыСостоянияHTTP.ПостоянноеПеренаправление_308 Тогда + УдалитьЗаголовки(ПодготовленныйЗапрос.Заголовки, "content-length,content-type,transfer-encoding"); + НовыйHTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки; + Иначе + ИсходныйПоток = ПодготовленныйЗапрос.HTTPЗапрос.ПолучитьТелоКакПоток(); + ИсходныйПоток.КопироватьВ(НовыйHTTPЗапрос.ПолучитьТелоКакПоток()); КонецЕсли; + + ПодготовленныйЗапрос.HTTPЗапрос = НовыйHTTPЗапрос; + УдалитьЗаголовки(ПодготовленныйЗапрос.Заголовки, "cookies"); - Дата = СоставляющиеДаты[3] + Формат(Месяц + 1, "ЧЦ=2; ЧВН=;") + СоставляющиеДаты[1]; - Время = СоставляющиеДаты[4] + СоставляющиеДаты[5] + СоставляющиеДаты[6]; - - Возврат ДатаИзСтроки(Дата + Время); + ПодготовленныйЗапрос.Cookies = ОбъединитьCookies(Сессия.Cookies, ПодготовленныйЗапрос.Cookies); + ПодготовитьCookies(ПодготовленныйЗапрос); + +КонецПроцедуры -КонецФункции +Процедура ПодготовитьАутентификацию(ПодготовленныйЗапрос) -Функция ВызватьHTTPМетод(Сессия, Метод, URL, ДополнительныеПараметры) + ПодготовленныйЗапрос.Вставить("СобытияНаОтвет", Новый Массив); + Если Не ЗначениеЗаполнено(ПодготовленныйЗапрос.Аутентификация) Тогда + СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL); + Если ЗначениеЗаполнено(СтруктураURL.Аутентификация) Тогда + ПодготовленныйЗапрос.Аутентификация = СтруктураURL.Аутентификация; + КонецЕсли; + КонецЕсли; - КодыСостоянияHTTP = КодыСостоянияHTTP(); + Если ЗначениеЗаполнено(ПодготовленныйЗапрос.Аутентификация) Тогда + Если ПодготовленныйЗапрос.Аутентификация.Свойство("Тип") Тогда + ТипАутентификации = НРег(ПодготовленныйЗапрос.Аутентификация.Тип); + Если ТипАутентификации = "digest" Тогда + ПодготовленныйЗапрос.СобытияНаОтвет.Добавить("ОбработкаОтветаСКодом401"); + КонецЕсли; + Если ТипАутентификации = "aws4-hmac-sha256" Тогда + ПодготовитьАутентификациюAWS4(ПодготовленныйЗапрос); + КонецЕсли; + КонецЕсли; + КонецЕсли; - ПодготовленныйЗапрос = ПодготовитьЗапрос(Сессия, Метод, URL, ДополнительныеПараметры); +КонецПроцедуры - НастройкиПодключения = НастройкиПодключения(Метод, URL, ДополнительныеПараметры); +Процедура ПодготовитьТелоЗапроса(ПодготовленныйЗапрос, Данные, Файлы, Json, ПараметрыЗаписиJSON) - Ответ = ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, НастройкиПодключения); + СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL); - Перенаправление = 0; - Пока Перенаправление < Сессия.МаксимальноеКоличествоПеренаправлений Цикл - Если Не НастройкиПодключения.РазрешитьПеренаправление ИЛИ Не Ответ.ЭтоРедирект Тогда - Возврат Ответ; + 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); + КонецЕсли; - НовыйURL = СформироватьНовыйURLПриПеренаправлении(Ответ); + HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки; - ПодготовленныйЗапрос.URL = КодироватьСтроку(НовыйURL, СпособКодированияСтроки.URLВКодировкеURL); - НовыйHTTPЗапрос = Новый HTTPЗапрос(СобратьАдресРесурса(РазобратьURL(НовыйURL), Неопределено)); - ПереопределитьМетод(ПодготовленныйЗапрос, Ответ); + УпаковатьЗапрос(HTTPЗапрос); - Если Ответ.КодСостояния <> КодыСостоянияHTTP.ВременноеПеренаправление_307 - И Ответ.КодСостояния <> КодыСостоянияHTTP.ПостоянноеПеренаправление_308 Тогда - УдалитьЗаголовки(ПодготовленныйЗапрос.Заголовки, "content-length,content-type,transfer-encoding"); - НовыйHTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки; - Иначе - ИсходныйПоток = ПодготовленныйЗапрос.HTTPЗапрос.ПолучитьТелоКакПоток(); - ИсходныйПоток.КопироватьВ(НовыйHTTPЗапрос.ПолучитьТелоКакПоток()); - КонецЕсли; - ПодготовленныйЗапрос.HTTPЗапрос = НовыйHTTPЗапрос; - УдалитьЗаголовки(ПодготовленныйЗапрос.Заголовки, "cookies"); + ПодготовленныйЗапрос.Вставить("HTTPЗапрос", HTTPЗапрос); - ПодготовленныйЗапрос.Cookies = ОбъединитьCookies(Сессия.Cookies, ПодготовленныйЗапрос.Cookies); - ПодготовитьCookies(ПодготовленныйЗапрос); +КонецПроцедуры - // INFO: по хорошему аутентификацию нужно привести к новых параметрам, но пока будем игнорировать. +Функция ЗакодироватьФайлы(HTTPЗапрос, Файлы, Данные) - Ответ = ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, НастройкиПодключения); + Части = Новый Массив; + Если ЗначениеЗаполнено(Данные) Тогда + Для Каждого Поле Из Данные Цикл + Части.Добавить(СоздатьПолеФормы(Новый Структура("Имя,Данные", Поле.Ключ, Поле.Значение))); + КонецЦикла; + КонецЕсли; + Если ТипЗнч(Файлы) = Тип("Массив") Тогда + Для Каждого Файл Из Файлы Цикл + Части.Добавить(СоздатьПолеФормы(Файл)); + КонецЦикла; + Иначе + Части.Добавить(СоздатьПолеФормы(Файлы)); + КонецЕсли; - Перенаправление = Перенаправление + 1; + Разделитель = СтрЗаменить(Новый УникальныйИдентификатор, "-", ""); + РазделительСтрок = Символы.ВК + Символы.ПС; + + ТелоЗапроса = HTTPЗапрос.ПолучитьТелоКакПоток(); + ЗаписьДанных = Новый ЗаписьДанных(ТелоЗапроса, КодировкаТекста.UTF8, ПорядокБайтов.LittleEndian, "", "", Ложь); + Для Каждого Часть Из Части Цикл + ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + РазделительСтрок); + ЗаписьДанных.ЗаписатьСтроку(ЗаголовкиВСтроку(Часть.Заголовки)); + Если ТипЗнч(Часть.Данные) = Тип("ДвоичныеДанные") Тогда + ЗаписьДанных.Записать(Часть.Данные); + Иначе + ЗаписьДанных.ЗаписатьСтроку(Часть.Данные); + КонецЕсли; + ЗаписьДанных.ЗаписатьСтроку(РазделительСтрок); КонецЦикла; + ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + "--" + РазделительСтрок); + ЗаписьДанных.Закрыть(); - ВызватьИсключение("СлишкомМногоПеренаправлений"); + Возврат СтрШаблон("multipart/form-data; boundary=%1", Разделитель); КонецФункции -Функция СформироватьНовыйURLПриПеренаправлении(Ответ) +Функция СоздатьПолеФормы(ИсходныеПараметры) - НовыйURL = ЗначениеЗаголовка("location", Ответ.Заголовки); - НовыйURL = РаскодироватьСтроку(НовыйURL, СпособКодированияСтроки.URLВКодировкеURL); + Поле = Новый Структура("Имя,ИмяФайла,Данные,Тип,Заголовки"); + Поле.Имя = ИсходныеПараметры.Имя; + Поле.Данные = ИсходныеПараметры.Данные; - // Редирект без схемы - Если СтрНачинаетсяС(НовыйURL, "//") Тогда - СтруктураURL = РазобратьURL(Ответ.URL); - НовыйURL = СтруктураURL.Схема + ":" + НовыйURL; + Поле.Тип = ЗначениеПоКлючу(ИсходныеПараметры, "Тип"); + Поле.Заголовки = ЗначениеПоКлючу(ИсходныеПараметры, "Заголовки", Новый Соответствие); + Поле.ИмяФайла = ЗначениеПоКлючу(ИсходныеПараметры, "ИмяФайла"); + + Ключ = "Content-Disposition"; + Если ЗначениеЗаголовка("content-disposition", Поле.Заголовки, Ключ) = Ложь Тогда + Поле.Заголовки.Вставить("Content-Disposition", "form-data"); КонецЕсли; - СтруктураURL = РазобратьURL(НовыйURL); - Если Не ЗначениеЗаполнено(СтруктураURL.Сервер) Тогда - СтруктураURLОтвета = РазобратьURL(Ответ.URL); - БазовыйURL = СтрШаблон("%1://%2", СтруктураURLОтвета.Схема, СтруктураURLОтвета.Сервер); - Если ЗначениеЗаполнено(СтруктураURLОтвета.Порт) Тогда - БазовыйURL = БазовыйURL + ":" + Формат(СтруктураURLОтвета.Порт, "ЧРГ=; ЧГ="); - КонецЕсли; - НовыйURL = БазовыйURL + НовыйURL; + Части = Новый Массив; + Части.Добавить(Поле.Заголовки[Ключ]); + Части.Добавить(СтрШаблон("name=""%1""", Поле.Имя)); + Если ЗначениеЗаполнено(Поле.ИмяФайла) Тогда + Части.Добавить(СтрШаблон("filename=""%1""", Поле.ИмяФайла)); КонецЕсли; - Возврат Новый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; - КонецЕсли; + Поле.Заголовки[Ключ] = СтрСоединить(Части, "; "); + Поле.Заголовки["Content-Type"] = Поле.Тип; - Возврат 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, Список) + Запрос = ""; + РазбитьСтрокуПоРазделителю(Запрос, Путь, "?", Истина); + Для Каждого СтрокаКлючРавноПараметр Из СтрРазделить(Запрос, "&", Ложь) Цикл + СтрокаКлючРавноПараметр = РаскодироватьСтроку( + СтрокаКлючРавноПараметр, СпособКодированияСтроки.URLВКодировкеURL); - Для Каждого Cookie Из Cookies Цикл - Если Cookie.Значение.ТолькоБезопасноеСоединение = Истина И СтруктураURL.Схема <> "https" Тогда - Продолжить; + ПозицияРавно = СтрНайти(СтрокаКлючРавноПараметр, "="); + Если ПозицияРавно = 0 Тогда + Ключ = СтрокаКлючРавноПараметр; + Значение = Неопределено; + Иначе + Ключ = Лев(СтрокаКлючРавноПараметр, ПозицияРавно - 1); + Значение = Сред(СтрокаКлючРавноПараметр, ПозицияРавно + 1); КонецЕсли; - // 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; - КонецЕсли; - -КонецПроцедуры - Функция КодироватьПараметрыЗапроса(ПараметрыЗапроса) ЧастиПараметрыЗапроса = Новый Массив; @@ -1332,630 +1281,528 @@ КонецФункции -Функция ПодготовитьURL(Знач URL, ПараметрыЗапроса = Неопределено) +Процедура ПереопределитьМетод(ПодготовленныйЗапрос, Ответ) - URL = СокрЛ(URL); + КодыСостоянияHTTP = КодыСостоянияHTTP(); - СтруктураURL = РазобратьURL(URL); + Метод = ПодготовленныйЗапрос.Метод; - ПодготовленныйURL = СтруктураURL.Схема + "://"; - Если ЗначениеЗаполнено(СтруктураURL.Аутентификация.Пользователь) Тогда - ПодготовленныйURL = ПодготовленныйURL - + СтруктураURL.Аутентификация.Пользователь + ":" - + СтруктураURL.Аутентификация.Пароль + "@"; + // http://tools.ietf.org/html/rfc7231#section-6.4.4 + Если Ответ.КодСостояния = КодыСостоянияHTTP.СмотретьДругое_303 И Метод <> "HEAD" Тогда + Метод = "GET"; КонецЕсли; - ПодготовленныйURL = ПодготовленныйURL + СтруктураURL.Сервер; - Если ЗначениеЗаполнено(СтруктураURL.Порт) Тогда - ПодготовленныйURL = ПодготовленныйURL + ":" + Формат(СтруктураURL.Порт, "ЧРГ=; ЧГ="); + + // Поведение браузеров + Если Ответ.КодСостояния = КодыСостоянияHTTP.ПеремещеноВременно_302 И Метод <> "HEAD" Тогда + Метод = "GET"; КонецЕсли; - ПодготовленныйURL = ПодготовленныйURL + СобратьАдресРесурса(СтруктураURL, ПараметрыЗапроса); + ПодготовленныйЗапрос.Метод = Метод; - Возврат ПодготовленныйURL; +КонецПроцедуры -КонецФункции +Функция ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, Настройки) -Функция ЗаголовкиВСтроку(Заголовки) + Начало = ТекущаяУниверсальнаяДатаВМиллисекундах(); + МиллисекундВСекунде = 1000; - РазделительСтрок = Символы.ВК + Символы.ПС; - Строки = Новый Массив; + Повтор = 0; + Длительность = 0; + Пока Истина Цикл + Попытка + Ответ = ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки); + Исключение + ОшибкаВыполненияЗапроса = ИнформацияОбОшибке(); + КонецПопытки; - СортированныеЗаголовки = "Content-Disposition,Content-Type,Content-Location"; - Для Каждого Ключ Из СтрРазделить(СортированныеЗаголовки, ",") Цикл - Значение = ЗначениеЗаголовка(Ключ, Заголовки); - Если Значение <> Ложь И ЗначениеЗаполнено(Значение) Тогда - Строки.Добавить(СтрШаблон("%1: %2", Ключ, Значение)); + Повтор = Повтор + 1; + Длительность = (ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало) / МиллисекундВСекунде; + + Если Не НеобходимоПовторитьЗапрос(Ответ, Настройки, ОшибкаВыполненияЗапроса) Тогда + Прервать; КонецЕсли; - КонецЦикла; - Ключи = СтрРазделить(ВРег(СортированныеЗаголовки), ","); - Для Каждого Заголовок Из Заголовки Цикл - Если Ключи.Найти(ВРег(Заголовок.Ключ)) = Неопределено Тогда - Строки.Добавить(СтрШаблон("%1: %2", Заголовок.Ключ, Заголовок.Значение)); + Если Повтор > Настройки.МаксимальноеКоличествоПовторов + ИЛИ Длительность > Настройки.МаксимальноеВремяПовторов Тогда + Прервать; КонецЕсли; - КонецЦикла; - Строки.Добавить(РазделительСтрок); - - Возврат СтрСоединить(Строки, РазделительСтрок); - -КонецФункции -Функция ЗначениеПоКлючу(Структура, Ключ, ЗначениеПоУмолчанию = Неопределено) + Если ОшибкаВыполненияЗапроса <> Неопределено + ИЛИ НЕ ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(Ответ.КодСостояния) Тогда + ЗаголовокRetryAfter = Ложь; + Иначе + ЗаголовокRetryAfter = ЗначениеЗаголовка("retry-after", Ответ.Заголовки); + КонецЕсли; + ДлительностьПриостановки = РассчитатьДлительностьПриостановки( + Повтор, + Настройки.КоэффициентЭкспоненциальнойЗадержки, + ЗаголовокRetryAfter, + Настройки.МаксимальноеВремяПовторов - Длительность); + Приостановить(ДлительностьПриостановки); + КонецЦикла; - Если ТипЗнч(Структура) = Тип("Структура") И Структура.Свойство(Ключ) Тогда - Значение = Структура[Ключ]; - ИначеЕсли ТипЗнч(Структура) = Тип("Соответствие") И Структура.Получить(Ключ) <> Неопределено Тогда - Значение = Структура.Получить(Ключ); - Иначе - Значение = ЗначениеПоУмолчанию; + Если ОшибкаВыполненияЗапроса <> Неопределено Тогда + ВызватьИсключение(ПодробноеПредставлениеОшибки(ОшибкаВыполненияЗапроса)); КонецЕсли; - Возврат Значение; + ЗаголовокContentType = ЗначениеЗаголовка("content-type", Ответ.Заголовки); + Если ЗаголовокContentType = Ложь Тогда + ЗаголовокContentType = ""; + КонецЕсли; -КонецФункции + ПодготовленныйОтвет = Новый Структура; + ПодготовленныйОтвет.Вставить("ВремяВыполнения", ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало); + ПодготовленныйОтвет.Вставить("Cookies", ИзвлечьCookies(Ответ.Заголовки, ПодготовленныйЗапрос.URL)); + ПодготовленныйОтвет.Вставить("Заголовки", Ответ.Заголовки); + ПодготовленныйОтвет.Вставить("ЭтоПостоянныйРедирект", ЭтоПостоянныйРедирект(Ответ.КодСостояния, Ответ.Заголовки)); + ПодготовленныйОтвет.Вставить("ЭтоРедирект", ЭтоРедирект(Ответ.КодСостояния, Ответ.Заголовки)); + ПодготовленныйОтвет.Вставить("Кодировка", КодировкаИзЗаголовка(ЗаголовокContentType)); + ПодготовленныйОтвет.Вставить("Тело", Ответ.ПолучитьТелоКакДвоичныеДанные()); + ПодготовленныйОтвет.Вставить("КодСостояния", Ответ.КодСостояния); + ПодготовленныйОтвет.Вставить("URL", ПодготовленныйЗапрос.URL); -Функция СоздатьПолеФормы(ИсходныеПараметры) + Сессия.Cookies = ОбъединитьCookies(Сессия.Cookies, ПодготовленныйОтвет.Cookies); - Поле = Новый Структура("Имя,ИмяФайла,Данные,Тип,Заголовки"); - Поле.Имя = ИсходныеПараметры.Имя; - Поле.Данные = ИсходныеПараметры.Данные; + Возврат ПодготовленныйОтвет; - Поле.Тип = ЗначениеПоКлючу(ИсходныеПараметры, "Тип"); - Поле.Заголовки = ЗначениеПоКлючу(ИсходныеПараметры, "Заголовки", Новый Соответствие); - Поле.ИмяФайла = ЗначениеПоКлючу(ИсходныеПараметры, "ИмяФайла"); +КонецФункции - Ключ = "Content-Disposition"; - Если ЗначениеЗаголовка("content-disposition", Поле.Заголовки, Ключ) = Ложь Тогда - Поле.Заголовки.Вставить("Content-Disposition", "form-data"); - КонецЕсли; +Функция ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки) - Части = Новый Массив; - Части.Добавить(Поле.Заголовки[Ключ]); - Части.Добавить(СтрШаблон("name=""%1""", Поле.Имя)); - Если ЗначениеЗаполнено(Поле.ИмяФайла) Тогда - Части.Добавить(СтрШаблон("filename=""%1""", Поле.ИмяФайла)); - КонецЕсли; + СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL); + Соединение = Соединение(СтруктураURL, ПодготовленныйЗапрос.Аутентификация, Настройки, Сессия); + Ответ = Соединение.ВызватьHTTPМетод(ПодготовленныйЗапрос.Метод, ПодготовленныйЗапрос.HTTPЗапрос); - Поле.Заголовки[Ключ] = СтрСоединить(Части, "; "); - Поле.Заголовки["Content-Type"] = Поле.Тип; + Для Каждого Обработчик Из ПодготовленныйЗапрос.СобытияНаОтвет Цикл + Если Обработчик = "ОбработкаОтветаСКодом401" Тогда + ОбработкаОтветаСКодом401(Сессия, ПодготовленныйЗапрос, Настройки, Ответ); + КонецЕсли; + КонецЦикла; - Возврат Поле; + Возврат Ответ; КонецФункции -Функция ЗакодироватьФайлы(HTTPЗапрос, Файлы, Данные) +Функция НеобходимоПовторитьЗапрос(Ответ, Настройки, ОшибкаВыполненияЗапроса) - Части = Новый Массив; - Если ЗначениеЗаполнено(Данные) Тогда - Для Каждого Поле Из Данные Цикл - Части.Добавить(СоздатьПолеФормы(Новый Структура("Имя,Данные", Поле.Ключ, Поле.Значение))); - КонецЦикла; - КонецЕсли; - Если ТипЗнч(Файлы) = Тип("Массив") Тогда - Для Каждого Файл Из Файлы Цикл - Части.Добавить(СоздатьПолеФормы(Файл)); - КонецЦикла; + Если Настройки.МаксимальноеКоличествоПовторов < 1 Тогда + ПовторитьЗапрос = Ложь; + ИначеЕсли ОшибкаВыполненияЗапроса <> Неопределено ИЛИ ПовторятьПриКодеСостояния(Ответ.КодСостояния, Настройки) Тогда + ПовторитьЗапрос = Истина; Иначе - Части.Добавить(СоздатьПолеФормы(Файлы)); + ЗаголовокRetryAfter = ЗначениеЗаголовка("retry-after", Ответ.Заголовки); + ПовторитьЗапрос = ЗаголовокRetryAfter <> Ложь + И ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(Ответ.КодСостояния); КонецЕсли; - Разделитель = СтрЗаменить(Новый УникальныйИдентификатор, "-", ""); - РазделительСтрок = Символы.ВК + Символы.ПС; + Возврат ПовторитьЗапрос; - ТелоЗапроса = HTTPЗапрос.ПолучитьТелоКакПоток(); - ЗаписьДанных = Новый ЗаписьДанных(ТелоЗапроса, КодировкаТекста.UTF8, ПорядокБайтов.LittleEndian, "", "", Ложь); - Для Каждого Часть Из Части Цикл - ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + РазделительСтрок); - ЗаписьДанных.ЗаписатьСтроку(ЗаголовкиВСтроку(Часть.Заголовки)); - Если ТипЗнч(Часть.Данные) = Тип("ДвоичныеДанные") Тогда - ЗаписьДанных.Записать(Часть.Данные); - Иначе - ЗаписьДанных.ЗаписатьСтроку(Часть.Данные); - КонецЕсли; - ЗаписьДанных.ЗаписатьСтроку(РазделительСтрок); - КонецЦикла; - ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + "--" + РазделительСтрок); - ЗаписьДанных.Закрыть(); +КонецФункции - Возврат СтрШаблон("multipart/form-data; boundary=%1", Разделитель); +Функция ПовторятьПриКодеСостояния(КодСостояния, Настройки) + + ПовторПриЛюбомКодеСостоянияБольшеИлиРавным500 = Настройки.ПовторятьДляКодовСостояний = Неопределено + И КодСостояния >= КодыСостоянияHTTP().ВнутренняяОшибкаСервера_500; + КодСостоянияСоответствуетКодуСостоянияПовтора = ТипЗнч(Настройки.ПовторятьДляКодовСостояний) = Тип("Массив") + И Настройки.ПовторятьДляКодовСостояний.Найти(КодСостояния) <> Неопределено; + Возврат ПовторПриЛюбомКодеСостоянияБольшеИлиРавным500 ИЛИ КодСостоянияСоответствуетКодуСостоянияПовтора; КонецФункции -Процедура ПодготовитьТелоЗапроса(ПодготовленныйЗапрос, Данные, Файлы, Json, ПараметрыЗаписиJSON) +Функция ЭтоПостоянныйРедирект(КодСостояния, Заголовки) - СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL); + КодыСостоянияHTTP = КодыСостоянияHTTP(); - 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); - КонецЕсли; + Возврат ЕстьЗаголовокLocation(Заголовки) + И (КодСостояния = КодыСостоянияHTTP.ПеремещеноНавсегда_301 + ИЛИ КодСостояния = КодыСостоянияHTTP.ПостоянноеПеренаправление_308); - HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки; +КонецФункции - УпаковатьЗапрос(HTTPЗапрос); +Функция ЭтоРедирект(КодСостояния, Заголовки) - ПодготовленныйЗапрос.Вставить("HTTPЗапрос", HTTPЗапрос); + КодыСостоянияHTTP = КодыСостоянияHTTP(); -КонецПроцедуры + СостоянияРедиректа = Новый Массив; + СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПеремещеноНавсегда_301); + СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПеремещеноВременно_302); + СостоянияРедиректа.Добавить(КодыСостоянияHTTP.СмотретьДругое_303); + СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ВременноеПеренаправление_307); + СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПостоянноеПеренаправление_308); -Процедура ПодготовитьАутентификацию(ПодготовленныйЗапрос) + Возврат ЕстьЗаголовокLocation(Заголовки) И СостоянияРедиректа.Найти(КодСостояния) <> Неопределено; - ПодготовленныйЗапрос.Вставить("СобытияНаОтвет", Новый Массив); - Если Не ЗначениеЗаполнено(ПодготовленныйЗапрос.Аутентификация) Тогда - СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL); - Если ЗначениеЗаполнено(СтруктураURL.Аутентификация) Тогда - ПодготовленныйЗапрос.Аутентификация = СтруктураURL.Аутентификация; - КонецЕсли; - КонецЕсли; +КонецФункции - Если ЗначениеЗаполнено(ПодготовленныйЗапрос.Аутентификация) Тогда - Если ПодготовленныйЗапрос.Аутентификация.Свойство("Тип") Тогда - ТипАутентификации = НРег(ПодготовленныйЗапрос.Аутентификация.Тип); - Если ТипАутентификации = "digest" Тогда - ПодготовленныйЗапрос.СобытияНаОтвет.Добавить("ОбработкаОтветаСКодом401"); - КонецЕсли; - Если ТипАутентификации = "aws4-hmac-sha256" Тогда - ПодготовитьАутентификациюAWS4(ПодготовленныйЗапрос); - КонецЕсли; +Процедура УпаковатьЗапрос(Запрос) + + Заголовок = ЗначениеЗаголовка("content-encoding", Запрос.Заголовки); + Если Заголовок <> Ложь Тогда + Если НРег(Заголовок) = "gzip" Тогда + Запрос.УстановитьТелоИзДвоичныхДанных(ЗаписатьGZip(Запрос.ПолучитьТелоКакДвоичныеДанные())); КонецЕсли; КонецЕсли; КонецПроцедуры -Функция ОбъединитьCookies(ГлавныйИсточник, ДополнительныйИсточник) +Функция РаспаковатьОтвет(Ответ) - Cookies = Новый Соответствие; - Для Каждого Cookie Из ПреобразоватьХранилищеCookiesВМассивCookies(ГлавныйИсточник) Цикл - ДобавитьCookieВХранилище(Cookies, Cookie, Ложь); - КонецЦикла; - Для Каждого Cookie Из ПреобразоватьХранилищеCookiesВМассивCookies(ДополнительныйИсточник) Цикл - ДобавитьCookieВХранилище(Cookies, Cookie, Ложь); - КонецЦикла; + Заголовок = ЗначениеЗаголовка("content-encoding", Ответ.Заголовки); + Если Заголовок <> Ложь Тогда + Если НРег(Заголовок) = "gzip" Тогда + Возврат ПрочитатьGZip(Ответ.Тело); + КонецЕсли; + КонецЕсли; - Возврат Cookies; + Возврат Ответ.Тело; КонецФункции -Функция ПреобразоватьХранилищеCookiesВМассивCookies(ХранилищеCookies) +#КонецОбласти - Cookies = Новый Массив; - Если ТипЗнч(ХранилищеCookies) = Тип("Массив") Тогда - Для Каждого Cookie Из ХранилищеCookies Цикл - НоваяCookie = КонструкторCookie(); - ЗаполнитьЗначенияСвойств(НоваяCookie, Cookie); - Cookies.Добавить(НоваяCookie); - КонецЦикла; +#Область ОбработчикиСобытий - Возврат Cookies; - КонецЕсли; +Процедура ОбработкаОтветаСКодом401(Сессия, ПодготовленныйЗапрос, Настройки, Ответ) - Для Каждого Домен Из ХранилищеCookies Цикл - Для Каждого Путь Из Домен.Значение Цикл - Для Каждого Наименование Из Путь.Значение Цикл - Cookies.Добавить(Наименование.Значение); - КонецЦикла; - КонецЦикла; - КонецЦикла; + Если ЭтоРедирект(Ответ.КодСостояния, Ответ.Заголовки) Тогда + Возврат; + КонецЕсли; - Возврат Cookies; + КодыСостоянияHTTP = КодыСостоянияHTTP(); + Если Ответ.КодСостояния < КодыСостоянияHTTP.НеверныйЗапрос_400 + ИЛИ Ответ.КодСостояния >= КодыСостоянияHTTP.ВнутренняяОшибкаСервера_500 Тогда + Возврат; + КонецЕсли; -КонецФункции + Значение = ЗначениеЗаголовка("www-authenticate", Ответ.Заголовки); + Если Значение <> Ложь И СтрНайти(НРег(Значение), "digest") Тогда + Позиция = СтрНайти(НРег(Значение), "digest"); + Значение = Сред(Значение, Позиция + СтрДлина("digest") + 1); + Значение = СтрЗаменить(Значение, """", ""); + Значение = СтрЗаменить(Значение, Символы.ПС, ""); -Функция ОбъединитьПараметрыАутентификации(ГлавныйИсточник, ДополнительныйИсточник) - - ПараметрыАутентификации = Новый Структура; - Если ТипЗнч(ГлавныйИсточник) = Тип("Структура") Тогда - Для Каждого Параметр Из ГлавныйИсточник Цикл - ПараметрыАутентификации.Вставить(Параметр.Ключ, Параметр.Значение); - КонецЦикла; - КонецЕсли; - Если ТипЗнч(ДополнительныйИсточник) = Тип("Структура") Тогда - Для Каждого Параметр Из ДополнительныйИсточник Цикл - Если Не ПараметрыАутентификации.Свойство(Параметр) Тогда - ПараметрыАутентификации.Вставить(Параметр.Ключ, Параметр.Значение); - КонецЕсли; + ПараметрыDigest = Новый Структура("algorithm,realm,nonce,qop,opaque"); + Для Каждого Часть Из РазбитьСтрокуПоСтроке(Значение, ", ") Цикл + КлючЗначение = СтрРазделить(Часть, "="); + ПараметрыDigest.Вставить(КлючЗначение[0], КлючЗначение[1]); КонецЦикла; - КонецЕсли; - Возврат ПараметрыАутентификации; + Сессия.СлужебныеДанные.ПараметрыDigest = ПараметрыDigest; -КонецФункции + ПодготовленныйЗапрос.Заголовки.Вставить("Authorization", ПодготовитьЗаголовокDigest(Сессия, ПодготовленныйЗапрос)); + ПодготовленныйЗапрос.HTTPЗапрос.Заголовки = ПодготовленныйЗапрос.Заголовки; -Функция ОбъединитьЗаголовки(ГлавныйИсточник, ДополнительныйИсточник) + Ответ = ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки); + КонецЕсли; - Заголовки = Новый Соответствие; - Для Каждого Заголовок Из ГлавныйИсточник Цикл - Заголовки.Вставить(Заголовок.Ключ, Заголовок.Значение); - КонецЦикла; - Для Каждого Заголовок Из ДополнительныйИсточник Цикл - Если Заголовки.Получить(Заголовок.Ключ) = Неопределено Тогда - Заголовки.Вставить(Заголовок.Ключ, Заголовок.Значение); - КонецЕсли; - КонецЦикла; +КонецПроцедуры - Возврат Заголовки; +#КонецОбласти -КонецФункции +#Область URL -Функция ОбъединитьПараметрыЗапроса(ГлавныйИсточник, ДополнительныйИсточник) +Функция ПодготовитьURL(Знач URL, ПараметрыЗапроса = Неопределено) - ПараметрыЗапроса = Новый Соответствие; - Если ТипЗнч(ГлавныйИсточник) = Тип("Структура") ИЛИ ТипЗнч(ГлавныйИсточник) = Тип("Соответствие") Тогда - Для Каждого Параметр Из ГлавныйИсточник Цикл - ПараметрыЗапроса.Вставить(Параметр.Ключ, Параметр.Значение); - КонецЦикла; + URL = СокрЛ(URL); + + СтруктураURL = РазобратьURL(URL); + + ПодготовленныйURL = СтруктураURL.Схема + "://"; + Если ЗначениеЗаполнено(СтруктураURL.Аутентификация.Пользователь) Тогда + ПодготовленныйURL = ПодготовленныйURL + + СтруктураURL.Аутентификация.Пользователь + ":" + + СтруктураURL.Аутентификация.Пароль + "@"; КонецЕсли; - Если ТипЗнч(ДополнительныйИсточник) = Тип("Структура") ИЛИ ТипЗнч(ДополнительныйИсточник) = Тип("Соответствие") Тогда - Для Каждого Параметр Из ДополнительныйИсточник Цикл - Если ПараметрыЗапроса.Получить(Параметр) = Неопределено Тогда - ПараметрыЗапроса.Вставить(Параметр.Ключ, Параметр.Значение); - КонецЕсли; - КонецЦикла; + ПодготовленныйURL = ПодготовленныйURL + СтруктураURL.Сервер; + Если ЗначениеЗаполнено(СтруктураURL.Порт) Тогда + ПодготовленныйURL = ПодготовленныйURL + ":" + Формат(СтруктураURL.Порт, "ЧРГ=; ЧГ="); КонецЕсли; - Возврат ПараметрыЗапроса; + ПодготовленныйURL = ПодготовленныйURL + СобратьАдресРесурса(СтруктураURL, ПараметрыЗапроса); + + Возврат ПодготовленныйURL; КонецФункции -Функция ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки) +Функция СобратьАдресРесурса(СтруктураURL, ПараметрыЗапроса) - СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL); - Соединение = Соединение(СтруктураURL, ПодготовленныйЗапрос.Аутентификация, Настройки, Сессия); - Ответ = Соединение.ВызватьHTTPМетод(ПодготовленныйЗапрос.Метод, ПодготовленныйЗапрос.HTTPЗапрос); + АдресРесурса = СтруктураURL.Путь; - Для Каждого Обработчик Из ПодготовленныйЗапрос.СобытияНаОтвет Цикл - Если Обработчик = "ОбработкаОтветаСКодом401" Тогда - ОбработкаОтветаСКодом401(Сессия, ПодготовленныйЗапрос, Настройки, Ответ); - КонецЕсли; - КонецЦикла; + ОбъединенныеПараметрыЗапроса = ОбъединитьПараметрыЗапроса(ПараметрыЗапроса, СтруктураURL.ПараметрыЗапроса); + Если ЗначениеЗаполнено(ОбъединенныеПараметрыЗапроса) Тогда + АдресРесурса = АдресРесурса + "?" + КодироватьПараметрыЗапроса(ОбъединенныеПараметрыЗапроса); + КонецЕсли; + Если ЗначениеЗаполнено(СтруктураURL.Фрагмент) Тогда + АдресРесурса = АдресРесурса + "#" + СтруктураURL.Фрагмент; + КонецЕсли; - Возврат Ответ; + Возврат АдресРесурса; КонецФункции -Функция РассчитатьДлительностьПриостановки(Повтор, КоэффициентЭкспоненциальнойЗадержки, ЗаголовокRetryAfter, Остаток) +Функция СформироватьНовыйURLПриПеренаправлении(Ответ) - Если ЗаголовокRetryAfter <> Ложь Тогда - Длительность = ЧислоИзСтроки(ЗаголовокRetryAfter); + НовыйURL = ЗначениеЗаголовка("location", Ответ.Заголовки); + НовыйURL = РаскодироватьСтроку(НовыйURL, СпособКодированияСтроки.URLВКодировкеURL); - Если Длительность = 0 Тогда - Дата = ДатаИзСтрокиRFC7231(ЗаголовокRetryAfter); - Если ЗначениеЗаполнено(Дата) Тогда - Длительность = Дата - ТекущаяУниверсальнаяДата(); - КонецЕсли; - КонецЕсли; - Иначе - Длительность = КоэффициентЭкспоненциальнойЗадержки * Pow(2, Повтор - 1); + // Редирект без схемы + Если СтрНачинаетсяС(НовыйURL, "//") Тогда + СтруктураURL = РазобратьURL(Ответ.URL); + НовыйURL = СтруктураURL.Схема + ":" + НовыйURL; КонецЕсли; - Длительность = Мин(Длительность, Остаток); - - Если Длительность < 0 Тогда - Длительность = 0; + СтруктураURL = РазобратьURL(НовыйURL); + Если Не ЗначениеЗаполнено(СтруктураURL.Сервер) Тогда + СтруктураURLОтвета = РазобратьURL(Ответ.URL); + БазовыйURL = СтрШаблон("%1://%2", СтруктураURLОтвета.Схема, СтруктураURLОтвета.Сервер); + Если ЗначениеЗаполнено(СтруктураURLОтвета.Порт) Тогда + БазовыйURL = БазовыйURL + ":" + Формат(СтруктураURLОтвета.Порт, "ЧРГ=; ЧГ="); + КонецЕсли; + НовыйURL = БазовыйURL + НовыйURL; КонецЕсли; - Возврат Длительность; + Возврат НовыйURL; КонецФункции -Функция НеобходимоПовторитьЗапрос(Ответ, Настройки, ОшибкаВыполненияЗапроса) +Функция ЭтоСтандартныйПорт(СтруктураURL) - Если Настройки.МаксимальноеКоличествоПовторов < 1 Тогда - ПовторитьЗапрос = Ложь; - ИначеЕсли ОшибкаВыполненияЗапроса <> Неопределено ИЛИ ПовторятьПриКодеСостояния(Ответ.КодСостояния, Настройки) Тогда - ПовторитьЗапрос = Истина; - Иначе - ЗаголовокRetryAfter = ЗначениеЗаголовка("retry-after", Ответ.Заголовки); - ПовторитьЗапрос = ЗаголовокRetryAfter <> Ложь - И ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(Ответ.КодСостояния); - КонецЕсли; + СтандартныйПортHTTP = 80; + СтандартныйПортHTTPS = 443; - Возврат ПовторитьЗапрос; + Возврат (СтруктураURL.Схема = "http" И СтруктураURL.Порт = СтандартныйПортHTTP) + ИЛИ (СтруктураURL.Схема = "https" И СтруктураURL.Порт = СтандартныйПортHTTPS); КонецФункции -Функция ПовторятьПриКодеСостояния(КодСостояния, Настройки) - - ПовторПриЛюбомКодеСостоянияБольшеИлиРавным500 = Настройки.ПовторятьДляКодовСостояний = Неопределено - И КодСостояния >= КодыСостоянияHTTP().ВнутренняяОшибкаСервера_500; - КодСостоянияСоответствуетКодуСостоянияПовтора = ТипЗнч(Настройки.ПовторятьДляКодовСостояний) = Тип("Массив") - И Настройки.ПовторятьДляКодовСостояний.Найти(КодСостояния) <> Неопределено; - Возврат ПовторПриЛюбомКодеСостоянияБольшеИлиРавным500 ИЛИ КодСостоянияСоответствуетКодуСостоянияПовтора; +#КонецОбласти -КонецФункции +#Область РаботаССоединением -Функция ОтправитьЗапрос(Сессия, ПодготовленныйЗапрос, Настройки) +Функция НастройкиПодключения(Метод, URL, ДополнительныеПараметры) - Начало = ТекущаяУниверсальнаяДатаВМиллисекундах(); - МиллисекундВСекунде = 1000; + РазрешитьПеренаправление = + ЗначениеПоКлючу(ДополнительныеПараметры, "РазрешитьПеренаправление", ВРег(Метод) <> "HEAD"); + ПроверятьSSL = ЗначениеПоКлючу(ДополнительныеПараметры, "ПроверятьSSL", Истина); + КлиентскийСертификатSSL = ЗначениеПоКлючу(ДополнительныеПараметры, "КлиентскийСертификатSSL"); + Прокси = ЗначениеПоКлючу(ДополнительныеПараметры, "Прокси", ПроксиПоУмолчанию(URL)); + МаксимальноеКоличествоПовторов = ЗначениеПоКлючу(ДополнительныеПараметры, "МаксимальноеКоличествоПовторов", 0); + ПовторятьДляКодовСостояний = + ЗначениеПоКлючу(ДополнительныеПараметры, "ПовторятьДляКодовСостояний", Неопределено); + КоэффициентЭкспоненциальнойЗадержки = + ЗначениеПоКлючу(ДополнительныеПараметры, "КоэффициентЭкспоненциальнойЗадержки", 1); + МаксимальноеВремяПовторов = ЗначениеПоКлючу(ДополнительныеПараметры, "МаксимальноеВремяПовторов", 600); - Повтор = 0; - Длительность = 0; - Пока Истина Цикл - Попытка - Ответ = ОтправитьHTTPЗапрос(Сессия, ПодготовленныйЗапрос, Настройки); - Исключение - ОшибкаВыполненияЗапроса = ИнформацияОбОшибке(); - КонецПопытки; + Настройки = Новый Структура; + Настройки.Вставить("Таймаут", Таймаут(ДополнительныеПараметры)); + Настройки.Вставить("РазрешитьПеренаправление", РазрешитьПеренаправление); + Настройки.Вставить("ПроверятьSSL", ПроверятьSSL); + Настройки.Вставить("КлиентскийСертификатSSL", КлиентскийСертификатSSL); + Настройки.Вставить("Прокси", Прокси); + Настройки.Вставить("МаксимальноеКоличествоПовторов", МаксимальноеКоличествоПовторов); + Настройки.Вставить("ПовторятьДляКодовСостояний", ПовторятьДляКодовСостояний); + Настройки.Вставить("КоэффициентЭкспоненциальнойЗадержки", КоэффициентЭкспоненциальнойЗадержки); + Настройки.Вставить("МаксимальноеВремяПовторов", МаксимальноеВремяПовторов); - Повтор = Повтор + 1; - Длительность = (ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало) / МиллисекундВСекунде; + Возврат Настройки; - Если Не НеобходимоПовторитьЗапрос(Ответ, Настройки, ОшибкаВыполненияЗапроса) Тогда - Прервать; - КонецЕсли; +КонецФункции - Если Повтор > Настройки.МаксимальноеКоличествоПовторов - ИЛИ Длительность > Настройки.МаксимальноеВремяПовторов Тогда - Прервать; - КонецЕсли; +Функция Соединение(ПараметрыСоединения, Аутентификация, ДополнительныеПараметры, Сессия) - Если ОшибкаВыполненияЗапроса <> Неопределено - ИЛИ НЕ ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(Ответ.КодСостояния) Тогда - ЗаголовокRetryAfter = Ложь; + Если Не ЗначениеЗаполнено(ПараметрыСоединения.Порт) Тогда + Если ПараметрыСоединения.Схема = "https" Тогда + ПараметрыСоединения.Порт = 443; Иначе - ЗаголовокRetryAfter = ЗначениеЗаголовка("retry-after", Ответ.Заголовки); + ПараметрыСоединения.Порт = 80; КонецЕсли; - ДлительностьПриостановки = РассчитатьДлительностьПриостановки( - Повтор, - Настройки.КоэффициентЭкспоненциальнойЗадержки, - ЗаголовокRetryAfter, - Настройки.МаксимальноеВремяПовторов - Длительность); - Приостановить(ДлительностьПриостановки); - КонецЦикла; - - Если ОшибкаВыполненияЗапроса <> Неопределено Тогда - ВызватьИсключение(ПодробноеПредставлениеОшибки(ОшибкаВыполненияЗапроса)); КонецЕсли; - ЗаголовокContentType = ЗначениеЗаголовка("content-type", Ответ.Заголовки); - Если ЗаголовокContentType = Ложь Тогда - ЗаголовокContentType = ""; + ЗащищенноеСоединение = Неопределено; + Если ПараметрыСоединения.Схема = "https" Тогда + ЗащищенноеСоединение = ОбъектЗащищенногоСоединения(ДополнительныеПараметры); КонецЕсли; - ПодготовленныйОтвет = Новый Структура; - ПодготовленныйОтвет.Вставить("ВремяВыполнения", ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало); - ПодготовленныйОтвет.Вставить("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"; + Если ПулСоединений.Получить(ИдентификаторСоединения) = Неопределено Тогда + НовоеСоединение = Новый HTTPСоединение( + ПараметрыСоединения.Сервер, + ПараметрыСоединения.Порт, + Пользователь, Пароль, + ДополнительныеПараметры.Прокси, + ДополнительныеПараметры.Таймаут, + ЗащищенноеСоединение, + ИспользоватьАутентификациюОС); + ПулСоединений.Вставить(ИдентификаторСоединения, НовоеСоединение); КонецЕсли; - ПодготовленныйЗапрос.Метод = Метод; - -КонецПроцедуры - -Функция ИзвлечьCookies(Заголовки, URL) - - ТекущееВремя = ТекущаяУниверсальнаяДата(); - Cookies = Новый Соответствие; - Для Каждого ОчереднойЗаголовок Из Заголовки Цикл - Если НРег(ОчереднойЗаголовок.Ключ) = "set-cookie" Тогда - Для Каждого ЗаголовокCookie Из РазбитьНаОтдельныеЗаголовкиCookies(ОчереднойЗаголовок.Значение) Цикл - Cookie = РаспарситьCookie(ЗаголовокCookie, URL, ТекущееВремя); - Если Cookie = Неопределено Тогда - Продолжить; - КонецЕсли; - Если Cookie.СрокДействия <= ТекущееВремя Тогда - УдалитьCookieИзХранилища(Cookies, Cookie); - Иначе - ДобавитьCookieВХранилище(Cookies, Cookie); - КонецЕсли; - КонецЦикла; - КонецЕсли; - КонецЦикла; - - Возврат Cookies; + Возврат ПулСоединений[ИдентификаторСоединения]; КонецФункции -Функция РазбитьНаОтдельныеЗаголовкиCookies(Знач Заголовок) - - Заголовки = Новый Массив; - - Если Не ЗначениеЗаполнено(Заголовок) Тогда - Возврат Заголовки; - КонецЕсли; +Функция ИдентификаторСоединения(ПараметрыСоединения) - ЗапчастиЗаголовков = СтрРазделить(Заголовок, ",", Ложь); + ПараметрыДляРасчетаИдентификатора = Новый Массив; - ОтдельныйЗаголовок = ЗапчастиЗаголовков[0]; - Для Индекс = 1 По ЗапчастиЗаголовков.ВГраница() Цикл - ТочкаСЗапятой = СтрНайти(ЗапчастиЗаголовков[Индекс], ";"); - Равно = СтрНайти(ЗапчастиЗаголовков[Индекс], "="); - Если ТочкаСЗапятой И Равно И Равно < ТочкаСЗапятой Тогда - Заголовки.Добавить(ОтдельныйЗаголовок); - ОтдельныйЗаголовок = ЗапчастиЗаголовков[Индекс]; + Для Каждого Элемент Из ПараметрыСоединения Цикл + ТипЭлемента = ТипЗнч(Элемент); + Если ТипЭлемента = Тип("ИнтернетПрокси") Тогда + ПараметрыДляРасчетаИдентификатора.Добавить(СтрСоединить(Элемент.НеИспользоватьПроксиДляАдресов, "")); + ПараметрыДляРасчетаИдентификатора.Добавить(XMLСтрока(Элемент.НеИспользоватьПроксиДляЛокальныхАдресов)); + ПараметрыДляРасчетаИдентификатора.Добавить(Элемент.Пользователь); + ПараметрыДляРасчетаИдентификатора.Добавить(Элемент.Пароль); + ИначеЕсли ТипЭлемента = Тип("ЗащищенноеСоединениеOpenSSL") Тогда + // Для упрощения будет считать, что сертификаты в рамках сессии не меняются + Если Элемент.СертификатКлиента = Неопределено Тогда + ПараметрыДляРасчетаИдентификатора.Добавить(""); + Иначе + ПараметрыДляРасчетаИдентификатора.Добавить(Строка(ТипЗнч(Элемент.СертификатКлиента))); + КонецЕсли; + Если Элемент.СертификатыУдостоверяющихЦентров = Неопределено Тогда + ПараметрыДляРасчетаИдентификатора.Добавить(""); + Иначе + ПараметрыДляРасчетаИдентификатора.Добавить(Строка(ТипЗнч(Элемент.СертификатыУдостоверяющихЦентров))); + КонецЕсли; Иначе - ОтдельныйЗаголовок = ОтдельныйЗаголовок + ЗапчастиЗаголовков[Индекс]; + ПараметрыДляРасчетаИдентификатора.Добавить(XMLСтрока(Элемент)); КонецЕсли; КонецЦикла; - Заголовки.Добавить(ОтдельныйЗаголовок); - Возврат Заголовки; + Возврат ХешированиеДанных(ХешФункция.MD5, СтрСоединить(ПараметрыДляРасчетаИдентификатора, "")); КонецФункции -Функция КонструкторCookie(Наименование = "", Значение = Неопределено) +Функция ОбъектЗащищенногоСоединения(ДополнительныеПараметры) - НовыйCookie = Новый Структура; - НовыйCookie.Вставить("Наименование", Наименование); - НовыйCookie.Вставить("Значение", Значение); - НовыйCookie.Вставить("Домен", ""); - НовыйCookie.Вставить("Путь", ""); - НовыйCookie.Вставить("Порт"); - НовыйCookie.Вставить("СрокДействия", '39990101'); - НовыйCookie.Вставить("ТолькоБезопасноеСоединение"); + Если ДополнительныеПараметры.ПроверятьSSL = Ложь Тогда + СертификатыУЦ = Неопределено; + ИначеЕсли ТипЗнч(ДополнительныеПараметры.ПроверятьSSL) = Тип("СертификатыУдостоверяющихЦентровФайл") Тогда + СертификатыУЦ = ДополнительныеПараметры.ПроверятьSSL; + Иначе + СертификатыУЦ = Новый СертификатыУдостоверяющихЦентровОС; + КонецЕсли; + КлиентскийСертификат = Неопределено; + Если ТипЗнч(ДополнительныеПараметры.КлиентскийСертификатSSL) = Тип("СертификатКлиентаФайл") + ИЛИ ТипЗнч(ДополнительныеПараметры.КлиентскийСертификатSSL) = Тип("СертификатКлиентаWindows") Тогда + КлиентскийСертификат = ДополнительныеПараметры.КлиентскийСертификатSSL; + КонецЕсли; - Возврат НовыйCookie; + Возврат Новый ЗащищенноеСоединениеOpenSSL(КлиентскийСертификат, СертификатыУЦ); КонецФункции -Функция СоздатьCookieИЗаполнитьОсновныеПараметры(Параметр) +Функция Таймаут(ДополнительныеПараметры) - Части = СтрРазделить(Параметр, "=", Ложь); - Наименование = Части[0]; - Если Части.Количество() > 1 Тогда - Значение = Части[1]; + Если ДополнительныеПараметры.Свойство("Таймаут") И ЗначениеЗаполнено(ДополнительныеПараметры.Таймаут) Тогда + Таймаут = ДополнительныеПараметры.Таймаут; + Иначе + Таймаут = СтандартныйТаймаут(); КонецЕсли; - Возврат КонструкторCookie(Наименование, Значение); + Возврат Таймаут; КонецФункции -Функция РаспарситьCookie(Заголовок, URL, ТекущееВремя) +Функция ПроксиПоУмолчанию(URL) - Cookie = Неопределено; - Индекс = 0; + ПроксиПоУмолчанию = Новый ИнтернетПрокси; + // BSLLS:ExecuteExternalCodeInCommonModule-off + ИмяОМПолученияФайловБСП = "ПолучениеФайловИзИнтернета"; + Если Метаданные.ОбщиеМодули.Найти(ИмяОМПолученияФайловБСП) <> Неопределено Тогда + СтруктураURL = РазобратьURL(URL); + Модуль = Вычислить(ИмяОМПолученияФайловБСП); + ПроксиПоУмолчанию = Модуль.ПолучитьПрокси(СтруктураURL.Схема); + КонецЕсли; + // BSLLS:ExecuteExternalCodeInCommonModule-on - Для Каждого Параметр Из СтрРазделить(Заголовок, ";", Ложь) Цикл - Индекс = Индекс + 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); - КонецЕсли; - КонецЕсли; +#Область Заголовки -КонецПроцедуры +Функция ЗаголовкиВСтроку(Заголовки) -Функция ЗначениеЗаголовка(Заголовок, ВсеЗаголовки, Ключ = Неопределено) + РазделительСтрок = Символы.ВК + Символы.ПС; + Строки = Новый Массив; - Для Каждого ОчереднойЗаголовок Из ВсеЗаголовки Цикл - Если НРег(ОчереднойЗаголовок.Ключ) = НРег(Заголовок) Тогда - Ключ = ОчереднойЗаголовок.Ключ; - Возврат ОчереднойЗаголовок.Значение; + СортированныеЗаголовки = "Content-Disposition,Content-Type,Content-Location"; + Для Каждого Ключ Из СтрРазделить(СортированныеЗаголовки, ",") Цикл + Значение = ЗначениеЗаголовка(Ключ, Заголовки); + Если Значение <> Ложь И ЗначениеЗаполнено(Значение) Тогда + Строки.Добавить(СтрШаблон("%1: %2", Ключ, Значение)); КонецЕсли; КонецЦикла; - Возврат Ложь; - -КонецФункции - -Функция ЭтоПостоянныйРедирект(КодСостояния, Заголовки) - - КодыСостоянияHTTP = КодыСостоянияHTTP(); + Ключи = СтрРазделить(ВРег(СортированныеЗаголовки), ","); + Для Каждого Заголовок Из Заголовки Цикл + Если Ключи.Найти(ВРег(Заголовок.Ключ)) = Неопределено Тогда + Строки.Добавить(СтрШаблон("%1: %2", Заголовок.Ключ, Заголовок.Значение)); + КонецЕсли; + КонецЦикла; + Строки.Добавить(РазделительСтрок); - Возврат ЕстьЗаголовокLocation(Заголовки) - И (КодСостояния = КодыСостоянияHTTP.ПеремещеноНавсегда_301 - ИЛИ КодСостояния = КодыСостоянияHTTP.ПостоянноеПеренаправление_308); + Возврат СтрСоединить(Строки, РазделительСтрок); КонецФункции -Функция ЭтоРедирект(КодСостояния, Заголовки) - - КодыСостоянияHTTP = КодыСостоянияHTTP(); - - СостоянияРедиректа = Новый Массив; - СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПеремещеноНавсегда_301); - СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПеремещеноВременно_302); - СостоянияРедиректа.Добавить(КодыСостоянияHTTP.СмотретьДругое_303); - СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ВременноеПеренаправление_307); - СостоянияРедиректа.Добавить(КодыСостоянияHTTP.ПостоянноеПеренаправление_308); +Процедура УдалитьЗаголовки(Заголовки, СписокЗаголовковСтрокой) - Возврат ЕстьЗаголовокLocation(Заголовки) И СостоянияРедиректа.Найти(КодСостояния) <> Неопределено; + ЗаголовкиДляУдаления = Новый Массив; + СписокЗаголовков = СтрРазделить(СписокЗаголовковСтрокой, ",", Ложь); + Для Каждого Заголовок Из Заголовки Цикл + Если СписокЗаголовков.Найти(НРег(Заголовок.Ключ)) <> Неопределено Тогда + ЗаголовкиДляУдаления.Добавить(Заголовок.Ключ); + КонецЕсли; + КонецЦикла; + Для Каждого ЗаголовокДляУдаления Из ЗаголовкиДляУдаления Цикл + Заголовки.Удалить(ЗаголовокДляУдаления); + КонецЦикла; -КонецФункции +КонецПроцедуры Функция ЕстьЗаголовокLocation(Заголовки) @@ -1997,235 +1844,408 @@ КонецФункции -Функция СобратьАдресРесурса(СтруктураURL, ПараметрыЗапроса) - - АдресРесурса = СтруктураURL.Путь; +Функция ЗначениеЗаголовка(Заголовок, ВсеЗаголовки, Ключ = Неопределено) - ОбъединенныеПараметрыЗапроса = ОбъединитьПараметрыЗапроса(ПараметрыЗапроса, СтруктураURL.ПараметрыЗапроса); - Если ЗначениеЗаполнено(ОбъединенныеПараметрыЗапроса) Тогда - АдресРесурса = АдресРесурса + "?" + КодироватьПараметрыЗапроса(ОбъединенныеПараметрыЗапроса); - КонецЕсли; - Если ЗначениеЗаполнено(СтруктураURL.Фрагмент) Тогда - АдресРесурса = АдресРесурса + "#" + СтруктураURL.Фрагмент; - КонецЕсли; + Для Каждого ОчереднойЗаголовок Из ВсеЗаголовки Цикл + Если НРег(ОчереднойЗаголовок.Ключ) = НРег(Заголовок) Тогда + Ключ = ОчереднойЗаголовок.Ключ; + Возврат ОчереднойЗаголовок.Значение; + КонецЕсли; + КонецЦикла; - Возврат АдресРесурса; + Возврат Ложь; КонецФункции -Функция ОбъектЗащищенногоСоединения(ДополнительныеПараметры) +Функция СформироватьЗначениеЗаголовкаHost(СтруктураURL) - Если ДополнительныеПараметры.ПроверятьSSL = Ложь Тогда - СертификатыУЦ = Неопределено; - ИначеЕсли ТипЗнч(ДополнительныеПараметры.ПроверятьSSL) = Тип("СертификатыУдостоверяющихЦентровФайл") Тогда - СертификатыУЦ = ДополнительныеПараметры.ПроверятьSSL; - Иначе - СертификатыУЦ = Новый СертификатыУдостоверяющихЦентровОС; - КонецЕсли; - КлиентскийСертификат = Неопределено; - Если ТипЗнч(ДополнительныеПараметры.КлиентскийСертификатSSL) = Тип("СертификатКлиентаФайл") - ИЛИ ТипЗнч(ДополнительныеПараметры.КлиентскийСертификатSSL) = Тип("СертификатКлиентаWindows") Тогда - КлиентскийСертификат = ДополнительныеПараметры.КлиентскийСертификатSSL; + Host = СтруктураURL.Сервер; + Если ЗначениеЗаполнено(СтруктураURL.Порт) И НЕ ЭтоСтандартныйПорт(СтруктураURL) Тогда + Host = Host + ":" + Формат(СтруктураURL.Порт, "ЧРГ=; ЧГ="); КонецЕсли; - Возврат Новый ЗащищенноеСоединениеOpenSSL(КлиентскийСертификат, СертификатыУЦ); + Возврат Host; КонецФункции -Функция Соединение(ПараметрыСоединения, Аутентификация, ДополнительныеПараметры, Сессия) +Функция ПодготовитьЗаголовокDigest(Сессия, ПодготовленныйЗапрос) - Если Не ЗначениеЗаполнено(ПараметрыСоединения.Порт) Тогда - Если ПараметрыСоединения.Схема = "https" Тогда - ПараметрыСоединения.Порт = 443; - Иначе - ПараметрыСоединения.Порт = 80; - КонецЕсли; - КонецЕсли; + ПараметрыDigest = Сессия.СлужебныеДанные.ПараметрыDigest; - ЗащищенноеСоединение = Неопределено; - Если ПараметрыСоединения.Схема = "https" Тогда - ЗащищенноеСоединение = ОбъектЗащищенногоСоединения(ДополнительныеПараметры); + Алгоритм = ОпределитьХешФункцию(Параметры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); - Если ПулСоединений.Получить(ИдентификаторСоединения) = Неопределено Тогда - НовоеСоединение = Новый HTTPСоединение( - ПараметрыСоединения.Сервер, - ПараметрыСоединения.Порт, - Пользователь, Пароль, - ДополнительныеПараметры.Прокси, - ДополнительныеПараметры.Таймаут, - ЗащищенноеСоединение, - ИспользоватьАутентификациюОС); - ПулСоединений.Вставить(ИдентификаторСоединения, НовоеСоединение); + Если АлгоритмСтрокой = "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); + Строки = Новый Массив; + Строки.Добавить(База); - Для Каждого Элемент Из ПараметрыСоединения Цикл - ТипЭлемента = ТипЗнч(Элемент); - Если ТипЭлемента = Тип("ИнтернетПрокси") Тогда - ПараметрыДляРасчетаИдентификатора.Добавить(СтрСоединить(Элемент.НеИспользоватьПроксиДляАдресов, "")); - ПараметрыДляРасчетаИдентификатора.Добавить(XMLСтрока(Элемент.НеИспользоватьПроксиДляЛокальныхАдресов)); - ПараметрыДляРасчетаИдентификатора.Добавить(Элемент.Пользователь); - ПараметрыДляРасчетаИдентификатора.Добавить(Элемент.Пароль); - ИначеЕсли ТипЭлемента = Тип("ЗащищенноеСоединениеOpenSSL") Тогда - // Для упрощения будет считать, что сертификаты в рамках сессии не меняются - Если Элемент.СертификатКлиента = Неопределено Тогда - ПараметрыДляРасчетаИдентификатора.Добавить(""); - Иначе - ПараметрыДляРасчетаИдентификатора.Добавить(Строка(ТипЗнч(Элемент.СертификатКлиента))); - КонецЕсли; - Если Элемент.СертификатыУдостоверяющихЦентров = Неопределено Тогда - ПараметрыДляРасчетаИдентификатора.Добавить(""); - Иначе - ПараметрыДляРасчетаИдентификатора.Добавить(Строка(ТипЗнч(Элемент.СертификатыУдостоверяющихЦентров))); - КонецЕсли; - Иначе - ПараметрыДляРасчетаИдентификатора.Добавить(XMLСтрока(Элемент)); - КонецЕсли; - КонецЦикла; + Если ЗначениеЗаполнено(ПараметрыDigest.opaque) Тогда + Строки.Добавить(СтрШаблон(", opaque=""%1""", ПараметрыDigest.opaque)); + КонецЕсли; + Если ЗначениеЗаполнено(ПараметрыDigest.algorithm) Тогда + Строки.Добавить(СтрШаблон(", algorithm=""%1""", ПараметрыDigest.algorithm)); + КонецЕсли; + Если ЗначениеЗаполнено(ПараметрыDigest.qop) Тогда + Строки.Добавить(СтрШаблон(", qop=""auth"", nc=%1, cnonce=""%2""", ЗначениеNC, ЗначениеNonce)); + КонецЕсли; - Возврат ХешированиеДанных(ХешФункция.MD5, СтрСоединить(ПараметрыДляРасчетаИдентификатора, "")); + Возврат СтрШаблон("Digest %1", СтрСоединить(Строки, "")); КонецФункции -Функция ВыбратьЗначение(ОсновноеЗначение, ДополнительныеЗначения, Ключ, ЗначениеПоУмолчанию) +#КонецОбласти - Если ОсновноеЗначение <> Неопределено Тогда - Возврат ОсновноеЗначение; - КонецЕсли; +#Область Cookies - Значение = ЗначениеПоКлючу(ДополнительныеЗначения, Ключ); - Если Значение <> Неопределено Тогда - Возврат Значение; +Процедура ПодготовитьCookies(ПодготовленныйЗапрос) + + ЗаголовокCookie = ПодготовитьЗаголовокCookie(ПодготовленныйЗапрос); + Если ЗначениеЗаполнено(ЗаголовокCookie) Тогда + ПодготовленныйЗапрос.Заголовки["Cookie"] = ЗаголовокCookie; КонецЕсли; - Возврат ЗначениеПоУмолчанию; +КонецПроцедуры -КонецФункции +Функция ПодготовитьЗаголовокCookie(ПодготовленныйЗапрос) -Функция ЗаполнитьПараметрыЗапроса(Путь) + СтруктураURL = РазобратьURL(ПодготовленныйЗапрос.URL); - ПараметрыЗапроса = Новый Соответствие; + Cookies = Новый Массив; + Для Каждого Cookie Из ОтобратьCookiesДляЗапроса(СтруктураURL, ПодготовленныйЗапрос.Cookies) Цикл + Cookies.Добавить(СтрШаблон("%1=%2", Cookie.Наименование, Cookie.Значение)); + КонецЦикла; - Запрос = ""; - РазбитьСтрокуПоРазделителю(Запрос, Путь, "?", Истина); - Для Каждого СтрокаКлючРавноПараметр Из СтрРазделить(Запрос, "&", Ложь) Цикл - СтрокаКлючРавноПараметр = РаскодироватьСтроку( - СтрокаКлючРавноПараметр, СпособКодированияСтроки.URLВКодировкеURL); + Возврат СтрСоединить(Cookies, "; "); - ПозицияРавно = СтрНайти(СтрокаКлючРавноПараметр, "="); - Если ПозицияРавно = 0 Тогда - Ключ = СтрокаКлючРавноПараметр; - Значение = Неопределено; - Иначе - Ключ = Лев(СтрокаКлючРавноПараметр, ПозицияРавно - 1); - Значение = Сред(СтрокаКлючРавноПараметр, ПозицияРавно + 1); - КонецЕсли; +КонецФункции - Если ПараметрыЗапроса.Получить(Ключ) <> Неопределено Тогда - Если ТипЗнч(ПараметрыЗапроса[Ключ]) = Тип("Массив") Тогда - ПараметрыЗапроса[Ключ].Добавить(Значение); - Иначе - Значения = Новый Массив; - Значения.Добавить(ПараметрыЗапроса[Ключ]); - Значения.Добавить(Значение); - ПараметрыЗапроса[Ключ] = Значения; - КонецЕсли; - Иначе - ПараметрыЗапроса.Вставить(Ключ, Значение); - КонецЕсли; +Функция ОбъединитьCookies(ГлавныйИсточник, ДополнительныйИсточник) + Cookies = Новый Соответствие; + Для Каждого Cookie Из ПреобразоватьХранилищеCookiesВМассивCookies(ГлавныйИсточник) Цикл + ДобавитьCookieВХранилище(Cookies, Cookie, Ложь); + КонецЦикла; + Для Каждого Cookie Из ПреобразоватьХранилищеCookiesВМассивCookies(ДополнительныйИсточник) Цикл + ДобавитьCookieВХранилище(Cookies, Cookie, Ложь); КонецЦикла; - Возврат ПараметрыЗапроса; + Возврат Cookies; КонецФункции -Процедура РазбитьСтрокуПоРазделителю(ИзвлекаемаяЧасть, ОстальнаяЧасть, Разделитель, Инверсия = Ложь) +Функция ПреобразоватьХранилищеCookiesВМассивCookies(ХранилищеCookies) - Индекс = СтрНайти(ОстальнаяЧасть, Разделитель); - Если Индекс Тогда - ИзвлекаемаяЧасть = Лев(ОстальнаяЧасть, Индекс - 1); - ОстальнаяЧасть = Сред(ОстальнаяЧасть, Индекс + СтрДлина(Разделитель)); - Если Инверсия Тогда - ДляОбмена = ИзвлекаемаяЧасть; - ИзвлекаемаяЧасть = ОстальнаяЧасть; - ОстальнаяЧасть = ДляОбмена; - КонецЕсли; + Cookies = Новый Массив; + Если ТипЗнч(ХранилищеCookies) = Тип("Массив") Тогда + Для Каждого Cookie Из ХранилищеCookies Цикл + НоваяCookie = КонструкторCookie(); + ЗаполнитьЗначенияСвойств(НоваяCookie, Cookie); + Cookies.Добавить(НоваяCookie); + КонецЦикла; + + Возврат Cookies; КонецЕсли; -КонецПроцедуры + Для Каждого Домен Из ХранилищеCookies Цикл + Для Каждого Путь Из Домен.Значение Цикл + Для Каждого Наименование Из Путь.Значение Цикл + Cookies.Добавить(Наименование.Значение); + КонецЦикла; + КонецЦикла; + КонецЦикла; -Функция РазделитьПоПервомуНайденномуРазделителю(Строка, Разделители) + Возврат Cookies; - МинимальныйИндекс = СтрДлина(Строка); - ПервыйРазделитель = ""; +КонецФункции - Для Каждого Разделитель Из Разделители Цикл - Индекс = СтрНайти(Строка, Разделитель); - Если Индекс = 0 Тогда +Функция ОтобратьCookiesДляЗапроса(СтруктураURL, Cookies) + + СерверВЗапросе = ДобавитьЛидирующуюТочку(СтруктураURL.Сервер); + + Результат = Новый Массив; + Для Каждого Домен Из Cookies Цикл + Если Не СтрЗаканчиваетсяНа(СерверВЗапросе, Домен.Ключ) Тогда Продолжить; КонецЕсли; - Если Индекс < МинимальныйИндекс Тогда - МинимальныйИндекс = Индекс; - ПервыйРазделитель = Разделитель; + Для Каждого Путь Из Домен.Значение Цикл + Если Не СтрНачинаетсяС(СтруктураURL.Путь, Путь.Ключ) Тогда + Продолжить; + КонецЕсли; + ЗаполнитьСписокОтфильтрованнымиCookies(Путь.Значение, СтруктураURL, Результат); + КонецЦикла; + КонецЦикла; + + Возврат Результат; + +КонецФункции + +Процедура ЗаполнитьСписокОтфильтрованнымиCookies(Cookies, СтруктураURL, Список) + + Для Каждого Cookie Из Cookies Цикл + Если Cookie.Значение.ТолькоБезопасноеСоединение = Истина И СтруктураURL.Схема <> "https" Тогда + Продолжить; КонецЕсли; + // INFO: проверка срока действия игнорируется (Cookie.Значение.СрокДействия) + // INFO: проверка порта игнорируется + + Список.Добавить(Cookie.Значение); КонецЦикла; - Результат = Новый Массив; - Если ЗначениеЗаполнено(ПервыйРазделитель) Тогда - Результат.Добавить(Лев(Строка, МинимальныйИндекс - 1)); - Результат.Добавить(Сред(Строка, МинимальныйИндекс + СтрДлина(ПервыйРазделитель))); - Результат.Добавить(ПервыйРазделитель); - Иначе - Результат.Добавить(Строка); - Результат.Добавить(""); - Результат.Добавить(Неопределено); +КонецПроцедуры + +Функция ДозаполнитьCookie(Cookies, URL) + + СтруктураURL = РазобратьURL(URL); + НовыеCookies = Новый Массив; + Если ТипЗнч(Cookies) = Тип("Массив") Тогда + Для Каждого Cookie Из Cookies Цикл + НовыйCookie = КонструкторCookie(Cookie.Наименование, Cookie.Значение); + ЗаполнитьЗначенияСвойств(НовыйCookie, Cookie); + + Если Не ЗначениеЗаполнено(НовыйCookie.Домен) Тогда + НовыйCookie.Домен = СтруктураURL.Сервер; + КонецЕсли; + Если Не ЗначениеЗаполнено(НовыйCookie.Путь) Тогда + НовыйCookie.Путь = "/"; + КонецЕсли; + + НовыеCookies.Добавить(НовыйCookie); + КонецЦикла; + + Возврат НовыеCookies; КонецЕсли; - Возврат Результат; + Возврат Cookies; + +КонецФункции + +Функция ИзвлечьCookies(Заголовки, URL) + + ТекущееВремя = ТекущаяУниверсальнаяДата(); + Cookies = Новый Соответствие; + Для Каждого ОчереднойЗаголовок Из Заголовки Цикл + Если НРег(ОчереднойЗаголовок.Ключ) = "set-cookie" Тогда + Для Каждого ЗаголовокCookie Из РазбитьНаОтдельныеЗаголовкиCookies(ОчереднойЗаголовок.Значение) Цикл + Cookie = РаспарситьCookie(ЗаголовокCookie, URL, ТекущееВремя); + Если Cookie = Неопределено Тогда + Продолжить; + КонецЕсли; + Если Cookie.СрокДействия <= ТекущееВремя Тогда + УдалитьCookieИзХранилища(Cookies, Cookie); + Иначе + ДобавитьCookieВХранилище(Cookies, Cookie); + КонецЕсли; + КонецЦикла; + КонецЕсли; + КонецЦикла; + + Возврат Cookies; + +КонецФункции + +Функция РазбитьНаОтдельныеЗаголовкиCookies(Знач Заголовок) + + Заголовки = Новый Массив; + + Если Не ЗначениеЗаполнено(Заголовок) Тогда + Возврат Заголовки; + КонецЕсли; + + ЗапчастиЗаголовков = СтрРазделить(Заголовок, ",", Ложь); + + ОтдельныйЗаголовок = ЗапчастиЗаголовков[0]; + Для Индекс = 1 По ЗапчастиЗаголовков.ВГраница() Цикл + ТочкаСЗапятой = СтрНайти(ЗапчастиЗаголовков[Индекс], ";"); + Равно = СтрНайти(ЗапчастиЗаголовков[Индекс], "="); + Если ТочкаСЗапятой И Равно И Равно < ТочкаСЗапятой Тогда + Заголовки.Добавить(ОтдельныйЗаголовок); + ОтдельныйЗаголовок = ЗапчастиЗаголовков[Индекс]; + Иначе + ОтдельныйЗаголовок = ОтдельныйЗаголовок + ЗапчастиЗаголовков[Индекс]; + КонецЕсли; + КонецЦикла; + Заголовки.Добавить(ОтдельныйЗаголовок); + + Возврат Заголовки; + +КонецФункции + +Процедура ДобавитьCookieВХранилище(ХранилищеCookies, Cookie, Замещать = Ложь) + + Если ХранилищеCookies.Получить(Cookie.Домен) = Неопределено Тогда + ХранилищеCookies[Cookie.Домен] = Новый Соответствие; + КонецЕсли; + Если ХранилищеCookies[Cookie.Домен].Получить(Cookie.Путь) = Неопределено Тогда + ХранилищеCookies[Cookie.Домен][Cookie.Путь] = Новый Соответствие; + КонецЕсли; + Если ХранилищеCookies[Cookie.Домен][Cookie.Путь].Получить(Cookie.Наименование) = Неопределено ИЛИ Замещать Тогда + ХранилищеCookies[Cookie.Домен][Cookie.Путь][Cookie.Наименование] = Cookie; + КонецЕсли; + +КонецПроцедуры + +Процедура УдалитьCookieИзХранилища(ХранилищеCookies, Cookie) + + Если ХранилищеCookies.Получить(Cookie.Домен) <> Неопределено + И ХранилищеCookies[Cookie.Домен].Получить(Cookie.Путь) <> Неопределено + И ХранилищеCookies[Cookie.Домен][Cookie.Путь].Получить(Cookie.Наименование) <> Неопределено Тогда + ХранилищеCookies[Cookie.Домен][Cookie.Путь].Удалить(Cookie.Наименование); + КонецЕсли; + +КонецПроцедуры + +Функция РаспарситьCookie(Заголовок, URL, ТекущееВремя) + + Cookie = Неопределено; + Индекс = 0; + + Для Каждого Параметр Из СтрРазделить(Заголовок, ";", Ложь) Цикл + Индекс = Индекс + 1; + Параметр = СокрЛП(Параметр); + + Если Индекс = 1 Тогда + Cookie = СоздатьCookieИЗаполнитьОсновныеПараметры(Параметр); + Продолжить; + КонецЕсли; + + Части = СтрРазделить(Параметр, "=", Ложь); + Ключ = НРег(Части[0]); + Если Части.Количество() > 1 Тогда + Значение = Части[1]; + КонецЕсли; + + Если Ключ = "domain" Тогда + Cookie.Домен = Значение; + ИначеЕсли Ключ = "path" Тогда + Cookie.Путь = Значение; + ИначеЕсли Ключ = "secure" Тогда + Cookie.ТолькоБезопасноеСоединение = Истина; + ИначеЕсли Ключ = "max-age" Тогда + СрокДействияMaxAge = ТекущееВремя + ЧислоИзСтроки(Значение); + ИначеЕсли Ключ = "expires" Тогда + Cookie.СрокДействия = ДатаИзСтрокиRFC7231(Значение); + Иначе + Продолжить; + КонецЕсли; + КонецЦикла; + Если ЗначениеЗаполнено(Cookie) И ЗначениеЗаполнено(СрокДействияMaxAge) Тогда + Cookie.СрокДействия = СрокДействияMaxAge; + КонецЕсли; + + ДозаполнитьCookieНеявнымиЗначениями(Cookie, URL); + + Возврат Cookie; + +КонецФункции + +Функция СоздатьCookieИЗаполнитьОсновныеПараметры(Параметр) + + Части = СтрРазделить(Параметр, "=", Ложь); + Наименование = Части[0]; + Если Части.Количество() > 1 Тогда + Значение = Части[1]; + КонецЕсли; + + Возврат КонструкторCookie(Наименование, Значение); + +КонецФункции + +Процедура ДозаполнитьCookieНеявнымиЗначениями(Cookie, URL) + + Если Cookie = Неопределено Тогда + Возврат; + КонецЕсли; + + СтруктураURL = РазобратьURL(URL); + Если Не ЗначениеЗаполнено(Cookie.Домен) Тогда + Cookie.Домен = СтруктураURL.Сервер; + КонецЕсли; + Если Не ЗначениеЗаполнено(Cookie.Порт) И ЗначениеЗаполнено(СтруктураURL.Порт) Тогда + Cookie.Порт = СтруктураURL.Порт; + КонецЕсли; + Если Не ЗначениеЗаполнено(Cookie.Путь) Тогда + ПозицияПоследнегоСлеша = СтрНайти(СтруктураURL.Путь, "/", НаправлениеПоиска.СКонца); + Если ПозицияПоследнегоСлеша <= 1 Тогда + Cookie.Путь = "/"; + Иначе + Cookie.Путь = Лев(СтруктураURL.Путь, ПозицияПоследнегоСлеша - 1); + КонецЕсли; + КонецЕсли; + +КонецПроцедуры + +Функция КонструкторCookie(Наименование = "", Значение = Неопределено) + + НовыйCookie = Новый Структура; + НовыйCookie.Вставить("Наименование", Наименование); + НовыйCookie.Вставить("Значение", Значение); + НовыйCookie.Вставить("Домен", ""); + НовыйCookie.Вставить("Путь", ""); + НовыйCookie.Вставить("Порт"); + НовыйCookie.Вставить("СрокДействия", '39990101'); + НовыйCookie.Вставить("ТолькоБезопасноеСоединение"); + + Возврат НовыйCookie; КонецФункции +#КонецОбласти + +#Область ПараметрыРаботыСJSON + Функция ДополнитьПараметрыПреобразованияJSON(ПараметрыПреобразования) ПараметрыПреобразованияJSON = ПараметрыПреобразованияJSONПоУмолчанию(); @@ -2280,6 +2300,8 @@ КонецФункции +#КонецОбласти + #Область АутентификацияAWS4 Функция КлючПодписиAWS4(СекретныйКлюч, Дата, Регион, Сервис) @@ -2372,39 +2394,18 @@ КонецПроцедуры -Функция ЭтоСтандартныйПорт(СтруктураURL) +Функция КаноническиеЗаголовкиAWS4(Заголовки, Структура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ЕстьВЗапросе = Ложь; + ЗаголовкиПоУмолчанию = ЗаголовкиПоУмолчаниюAWS4(); + Для Каждого ОчереднойЗаголовок Из Заголовки Цикл + Заголовок = НРег(ОчереднойЗаголовок.Ключ); + Если ЗаголовкиПоУмолчанию.Исключения.Найти(Заголовок) <> Неопределено Тогда + Продолжить; + КонецЕсли; + ЗаголовокHostЕстьВЗапросе = Макс(ЗаголовокHostЕстьВЗапросе, Заголовок = "host"); Если ЗаголовкиПоУмолчанию.Равно.Найти(Заголовок) <> Неопределено Тогда Список.Добавить(Заголовок, СокрЛП(ОчереднойЗаголовок.Значение)); @@ -2670,196 +2671,6 @@ #КонецОбласти -#Область ОбработчикиСобытий - -Процедура ОбработкаОтветаСКодом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(Запрос.ПолучитьТелоКакДвоичныеДанные())); - КонецЕсли; - КонецЕсли; - -КонецПроцедуры - #Область ПараметрыПоУмолчанию Функция ЗаголовкиПоУмолчанию() @@ -2923,94 +2734,35 @@ #КонецОбласти -Процедура ЗаполнитьДополнительныеДанные(ДополнительныеПараметры, ПараметрыЗапроса, Данные, Json) +#Область КодыСостояний - Если ДополнительныеПараметры = Неопределено Тогда - ДополнительныеПараметры = Новый Структура(); - КонецЕсли; - Если Не ДополнительныеПараметры.Свойство("ПараметрыЗапроса") Тогда - ДополнительныеПараметры.Вставить("ПараметрыЗапроса", ПараметрыЗапроса); - КонецЕсли; - Если Не ДополнительныеПараметры.Свойство("Данные") Тогда - ДополнительныеПараметры.Вставить("Данные", Данные); - КонецЕсли; - Если Не ДополнительныеПараметры.Свойство("Json") Тогда - ДополнительныеПараметры.Вставить("Json", Json); - КонецЕсли; +Функция ОписанияКодовСостоянийHTTP() -КонецПроцедуры + Коды = Новый Массив; + Коды.Добавить(НовыйКодHTTP(100, "Продолжай_100", "Continue")); + Коды.Добавить(НовыйКодHTTP(101, "ПереключениеПротокола_101", "Switching Protocols")); + Коды.Добавить(НовыйКодHTTP(102, "ИдетОбработка_102", "Processing")); + Коды.Добавить(НовыйКодHTTP(103, "РанняяМетаинформация_103", "Early Hints")); -Процедура Приостановить(ДлительностьОстановкиВСекундах) + Коды.Добавить(НовыйКодHTTP(200, "ОК_200", "OK")); + Коды.Добавить(НовыйКодHTTP(201, "Создано_201", "Created")); + Коды.Добавить(НовыйКодHTTP(202, "Принято_202", "Accepted")); + Коды.Добавить(НовыйКодHTTP(203, "ИнформацияНеАвторитетна_203", "Non-Authoritative Information")); + Коды.Добавить(НовыйКодHTTP(204, "НетСодержимого_204", "No Content")); + Коды.Добавить(НовыйКодHTTP(205, "СброситьСодержимое_205", "Reset Content")); + Коды.Добавить(НовыйКодHTTP(206, "ЧастичноеСодержимое_206", "Partial Content")); + Коды.Добавить(НовыйКодHTTP(207, "Многостатусный_207", "Multi-Status")); + Коды.Добавить(НовыйКодHTTP(208, "УжеСообщалось_208", "Already Reported")); + Коды.Добавить(НовыйКодHTTP(226, "ИспользованоIM_226", "IM Used")); - // Когда-нибудь в платформе сделают паузу и это можно будет выкинуть - - Если ДлительностьОстановкиВСекундах < 1 Тогда - Возврат; - КонецЕсли; - - ТекущаяДата = ТекущаяУниверсальнаяДата(); - ЖдатьДо = ТекущаяДата + ДлительностьОстановкиВСекундах; - - // BSLLS:UsingHardcodeNetworkAddress-off - ЛокальныйХост = "127.0.0.0"; - КакойНибудьСвободныйПорт = 56476; - // BSLLS:UsingHardcodeNetworkAddress-on - Пока ТекущаяДата < ЖдатьДо Цикл - Таймаут = ЖдатьДо - ТекущаяДата; - Начало = ТекущаяУниверсальнаяДатаВМиллисекундах(); - Попытка - Соединение = Новый HTTPСоединение( - ЛокальныйХост, КакойНибудьСвободныйПорт, Неопределено, Неопределено, Неопределено, Таймаут); - Соединение.Получить(Новый HTTPЗапрос("/does_not_matter")); - Исключение - РеальныйТаймаут = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало; - КонецПопытки; - МинимальныйТаймаутВМиллисекундах = 1000; - Если РеальныйТаймаут < МинимальныйТаймаутВМиллисекундах Тогда - ВызватьИсключение(НСтр("ru = 'Процедура Приостановить не работает должным образом'")); - КонецЕсли; - ТекущаяДата = ТекущаяУниверсальнаяДата(); - КонецЦикла; - -КонецПроцедуры - -Функция ТекущаяСессия(Сессия) - - Если Сессия = Неопределено Тогда - Сессия = СоздатьСессию(); - КонецЕсли; - - Возврат Сессия; - -КонецФункции - -Функция ОписанияКодовСостоянийHTTP() - - Коды = Новый Массив; - Коды.Добавить(НовыйКодHTTP(100, "Продолжай_100", "Continue")); - Коды.Добавить(НовыйКодHTTP(101, "ПереключениеПротокола_101", "Switching Protocols")); - Коды.Добавить(НовыйКодHTTP(102, "ИдетОбработка_102", "Processing")); - Коды.Добавить(НовыйКодHTTP(103, "РанняяМетаинформация_103", "Early Hints")); - - Коды.Добавить(НовыйКодHTTP(200, "ОК_200", "OK")); - Коды.Добавить(НовыйКодHTTP(201, "Создано_201", "Created")); - Коды.Добавить(НовыйКодHTTP(202, "Принято_202", "Accepted")); - Коды.Добавить(НовыйКодHTTP(203, "ИнформацияНеАвторитетна_203", "Non-Authoritative Information")); - Коды.Добавить(НовыйКодHTTP(204, "НетСодержимого_204", "No Content")); - Коды.Добавить(НовыйКодHTTP(205, "СброситьСодержимое_205", "Reset Content")); - Коды.Добавить(НовыйКодHTTP(206, "ЧастичноеСодержимое_206", "Partial Content")); - Коды.Добавить(НовыйКодHTTP(207, "Многостатусный_207", "Multi-Status")); - Коды.Добавить(НовыйКодHTTP(208, "УжеСообщалось_208", "Already Reported")); - Коды.Добавить(НовыйКодHTTP(226, "ИспользованоIM_226", "IM Used")); - - Коды.Добавить(НовыйКодHTTP(300, "МножествоВыборов_300", "Multiple Choices")); - Коды.Добавить(НовыйКодHTTP(301, "ПеремещеноНавсегда_301", "Moved Permanently")); - Коды.Добавить(НовыйКодHTTP(302, "ПеремещеноВременно_302", "Moved Temporarily")); - Коды.Добавить(НовыйКодHTTP(303, "СмотретьДругое_303", "See Other")); - Коды.Добавить(НовыйКодHTTP(304, "НеИзменялось_304", "Not Modified")); - Коды.Добавить(НовыйКодHTTP(305, "ИспользоватьПрокси_305", "Use Proxy")); - Коды.Добавить(НовыйКодHTTP(307, "ВременноеПеренаправление_307", "Temporary Redirect")); - Коды.Добавить(НовыйКодHTTP(308, "ПостоянноеПеренаправление_308", "Permanent Redirect")); + Коды.Добавить(НовыйКодHTTP(300, "МножествоВыборов_300", "Multiple Choices")); + Коды.Добавить(НовыйКодHTTP(301, "ПеремещеноНавсегда_301", "Moved Permanently")); + Коды.Добавить(НовыйКодHTTP(302, "ПеремещеноВременно_302", "Moved Temporarily")); + Коды.Добавить(НовыйКодHTTP(303, "СмотретьДругое_303", "See Other")); + Коды.Добавить(НовыйКодHTTP(304, "НеИзменялось_304", "Not Modified")); + Коды.Добавить(НовыйКодHTTP(305, "ИспользоватьПрокси_305", "Use Proxy")); + Коды.Добавить(НовыйКодHTTP(307, "ВременноеПеренаправление_307", "Temporary Redirect")); + Коды.Добавить(НовыйКодHTTP(308, "ПостоянноеПеренаправление_308", "Permanent Redirect")); Коды.Добавить(НовыйКодHTTP(400, "НеверныйЗапрос_400", "Bad Request")); Коды.Добавить(НовыйКодHTTP(401, "НеАвторизован_401", "Unauthorized")); @@ -3074,4 +2826,315 @@ КонецФункции +Функция ЭтоКодСостоянияПриКоторомНужноУчитыватьЗаголовокRetryAfter(КодСостояния) + + Коды = КодыСостоянияHTTP(); + Возврат КодСостояния = Коды.ПолезнаяНагрузкаСлишкомВелика_413 + ИЛИ КодСостояния = Коды.СлишкомМногоЗапросов_429 + ИЛИ КодСостояния = Коды.СервисНедоступен_503; + +КонецФункции + +#КонецОбласти + +#Область Прочие + +Функция ОпределитьХешФункцию(Знач Алгоритм) + + Алгоритм = ВРег(Алгоритм); + Если Не ЗначениеЗаполнено(Алгоритм) ИЛИ Алгоритм = "MD5" ИЛИ Алгоритм = "MD5-SESS" Тогда + АлгоритмХеширования = ХешФункция.MD5; + ИначеЕсли Алгоритм = "SHA" Тогда + АлгоритмХеширования = ХешФункция.SHA1; + ИначеЕсли Алгоритм = "SHA-256" Тогда + АлгоритмХеширования = ХешФункция.SHA256; + Иначе + АлгоритмХеширования = Неопределено; + КонецЕсли; + + Возврат АлгоритмХеширования; + +КонецФункции + +Функция ХешированиеДанных(Знач Алгоритм, Знач Данные) + + Если ТипЗнч(Данные) = Тип("Строка") Тогда + Данные = ПолучитьДвоичныеДанныеИзСтроки(Данные, КодировкаТекста.UTF8, Ложь); + КонецЕсли; + + Хеширование = Новый ХешированиеДанных(Алгоритм); + Хеширование.Добавить(Данные); + + Возврат НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма)); + +КонецФункции + +Процедура Приостановить(ДлительностьОстановкиВСекундах) + + // Когда-нибудь в платформе сделают паузу и это можно будет выкинуть + + Если ДлительностьОстановкиВСекундах < 1 Тогда + Возврат; + КонецЕсли; + + ТекущаяДата = ТекущаяУниверсальнаяДата(); + ЖдатьДо = ТекущаяДата + ДлительностьОстановкиВСекундах; + + // BSLLS:UsingHardcodeNetworkAddress-off + ЛокальныйХост = "127.0.0.0"; + КакойНибудьСвободныйПорт = 56476; + // BSLLS:UsingHardcodeNetworkAddress-on + Пока ТекущаяДата < ЖдатьДо Цикл + Таймаут = ЖдатьДо - ТекущаяДата; + Начало = ТекущаяУниверсальнаяДатаВМиллисекундах(); + Попытка + Соединение = Новый HTTPСоединение( + ЛокальныйХост, КакойНибудьСвободныйПорт, Неопределено, Неопределено, Неопределено, Таймаут); + Соединение.Получить(Новый HTTPЗапрос("/does_not_matter")); + Исключение + РеальныйТаймаут = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало; + КонецПопытки; + МинимальныйТаймаутВМиллисекундах = 1000; + Если РеальныйТаймаут < МинимальныйТаймаутВМиллисекундах Тогда + ВызватьИсключение(НСтр("ru = 'Процедура Приостановить не работает должным образом'")); + КонецЕсли; + ТекущаяДата = ТекущаяУниверсальнаяДата(); + КонецЦикла; + +КонецПроцедуры + +Функция РассчитатьДлительностьПриостановки(Повтор, КоэффициентЭкспоненциальнойЗадержки, ЗаголовокRetryAfter, Остаток) + + Если ЗаголовокRetryAfter <> Ложь Тогда + Длительность = ЧислоИзСтроки(ЗаголовокRetryAfter); + + Если Длительность = 0 Тогда + Дата = ДатаИзСтрокиRFC7231(ЗаголовокRetryAfter); + Если ЗначениеЗаполнено(Дата) Тогда + Длительность = Дата - ТекущаяУниверсальнаяДата(); + КонецЕсли; + КонецЕсли; + Иначе + Длительность = КоэффициентЭкспоненциальнойЗадержки * Pow(2, Повтор - 1); + КонецЕсли; + + Длительность = Мин(Длительность, Остаток); + + Если Длительность < 0 Тогда + Длительность = 0; + КонецЕсли; + + Возврат Длительность; + +КонецФункции + +#КонецОбласти + +#Область УниверсальныеСтруктурыДанных + +Функция ВыбратьЗначение(ОсновноеЗначение, ДополнительныеЗначения, Ключ, ЗначениеПоУмолчанию) + + Если ОсновноеЗначение <> Неопределено Тогда + Возврат ОсновноеЗначение; + КонецЕсли; + + Значение = ЗначениеПоКлючу(ДополнительныеЗначения, Ключ); + Если Значение <> Неопределено Тогда + Возврат Значение; + КонецЕсли; + + Возврат ЗначениеПоУмолчанию; + +КонецФункции + +Функция ЗначениеПоКлючу(Структура, Ключ, ЗначениеПоУмолчанию = Неопределено) + + Если ТипЗнч(Структура) = Тип("Структура") И Структура.Свойство(Ключ) Тогда + Значение = Структура[Ключ]; + ИначеЕсли ТипЗнч(Структура) = Тип("Соответствие") И Структура.Получить(Ключ) <> Неопределено Тогда + Значение = Структура.Получить(Ключ); + Иначе + Значение = ЗначениеПоУмолчанию; + КонецЕсли; + + Возврат Значение; + +КонецФункции + +Функция ОбъединитьПараметрыАутентификации(ГлавныйИсточник, ДополнительныйИсточник) + + ПараметрыАутентификации = Новый Структура; + Если ТипЗнч(ГлавныйИсточник) = Тип("Структура") Тогда + Для Каждого Параметр Из ГлавныйИсточник Цикл + ПараметрыАутентификации.Вставить(Параметр.Ключ, Параметр.Значение); + КонецЦикла; + КонецЕсли; + Если ТипЗнч(ДополнительныйИсточник) = Тип("Структура") Тогда + Для Каждого Параметр Из ДополнительныйИсточник Цикл + Если Не ПараметрыАутентификации.Свойство(Параметр) Тогда + ПараметрыАутентификации.Вставить(Параметр.Ключ, Параметр.Значение); + КонецЕсли; + КонецЦикла; + КонецЕсли; + + Возврат ПараметрыАутентификации; + +КонецФункции + +Функция ОбъединитьЗаголовки(ГлавныйИсточник, ДополнительныйИсточник) + + Заголовки = Новый Соответствие; + Для Каждого Заголовок Из ГлавныйИсточник Цикл + Заголовки.Вставить(Заголовок.Ключ, Заголовок.Значение); + КонецЦикла; + Для Каждого Заголовок Из ДополнительныйИсточник Цикл + Если Заголовки.Получить(Заголовок.Ключ) = Неопределено Тогда + Заголовки.Вставить(Заголовок.Ключ, Заголовок.Значение); + КонецЕсли; + КонецЦикла; + + Возврат Заголовки; + +КонецФункции + +Функция ОбъединитьПараметрыЗапроса(ГлавныйИсточник, ДополнительныйИсточник) + + ПараметрыЗапроса = Новый Соответствие; + Если ТипЗнч(ГлавныйИсточник) = Тип("Структура") ИЛИ ТипЗнч(ГлавныйИсточник) = Тип("Соответствие") Тогда + Для Каждого Параметр Из ГлавныйИсточник Цикл + ПараметрыЗапроса.Вставить(Параметр.Ключ, Параметр.Значение); + КонецЦикла; + КонецЕсли; + Если ТипЗнч(ДополнительныйИсточник) = Тип("Структура") ИЛИ ТипЗнч(ДополнительныйИсточник) = Тип("Соответствие") Тогда + Для Каждого Параметр Из ДополнительныйИсточник Цикл + Если ПараметрыЗапроса.Получить(Параметр) = Неопределено Тогда + ПараметрыЗапроса.Вставить(Параметр.Ключ, Параметр.Значение); + КонецЕсли; + КонецЦикла; + КонецЕсли; + + Возврат ПараметрыЗапроса; + +КонецФункции + +#КонецОбласти + +#Область РаботаСоСтроками + +Функция ЧислоИзСтроки(Знач Строка) Экспорт + + ОписаниеТипа = Новый ОписаниеТипов("Число"); + Возврат ОписаниеТипа.ПривестиЗначение(Строка); + +КонецФункции + +Функция ДатаИзСтроки(Знач Строка) Экспорт + + КвалификаторДаты = Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя); + ОписаниеТипа = Новый ОписаниеТипов("Дата", Неопределено, Неопределено, КвалификаторДаты); + Возврат ОписаниеТипа.ПривестиЗначение(Строка); + +КонецФункции + +Функция ДатаИзСтрокиRFC7231(Знач Строка) Экспорт + + Разделители = ",-:/\."; + Для Индекс = 1 По СтрДлина(Разделители) Цикл + Разделитель = Сред(Разделители, Индекс, 1); + Строка = СтрЗаменить(Строка, Разделитель, " "); + КонецЦикла; + Строка = СтрЗаменить(Строка, " ", " "); + СоставляющиеДаты = СтрРазделить(Строка, " "); + МесяцСтр = СоставляющиеДаты[2]; + + Месяцы = СтрРазделить("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", ","); + Месяц = Месяцы.Найти(МесяцСтр); + Если Месяц = Неопределено Тогда + Возврат '00010101'; + КонецЕсли; + + Дата = СоставляющиеДаты[3] + Формат(Месяц + 1, "ЧЦ=2; ЧВН=;") + СоставляющиеДаты[1]; + Время = СоставляющиеДаты[4] + СоставляющиеДаты[5] + СоставляющиеДаты[6]; + + Возврат ДатаИзСтроки(Дата + Время); + +КонецФункции + +Процедура РазбитьСтрокуПоРазделителю(ИзвлекаемаяЧасть, ОстальнаяЧасть, Разделитель, Инверсия = Ложь) + + Индекс = СтрНайти(ОстальнаяЧасть, Разделитель); + Если Индекс Тогда + ИзвлекаемаяЧасть = Лев(ОстальнаяЧасть, Индекс - 1); + ОстальнаяЧасть = Сред(ОстальнаяЧасть, Индекс + СтрДлина(Разделитель)); + Если Инверсия Тогда + ДляОбмена = ИзвлекаемаяЧасть; + ИзвлекаемаяЧасть = ОстальнаяЧасть; + ОстальнаяЧасть = ДляОбмена; + КонецЕсли; + КонецЕсли; + +КонецПроцедуры + +Функция РазделитьПоПервомуНайденномуРазделителю(Строка, Разделители) + + МинимальныйИндекс = СтрДлина(Строка); + ПервыйРазделитель = ""; + + Для Каждого Разделитель Из Разделители Цикл + Индекс = СтрНайти(Строка, Разделитель); + Если Индекс = 0 Тогда + Продолжить; + КонецЕсли; + Если Индекс < МинимальныйИндекс Тогда + МинимальныйИндекс = Индекс; + ПервыйРазделитель = Разделитель; + КонецЕсли; + КонецЦикла; + + Результат = Новый Массив; + Если ЗначениеЗаполнено(ПервыйРазделитель) Тогда + Результат.Добавить(Лев(Строка, МинимальныйИндекс - 1)); + Результат.Добавить(Сред(Строка, МинимальныйИндекс + СтрДлина(ПервыйРазделитель))); + Результат.Добавить(ПервыйРазделитель); + Иначе + Результат.Добавить(Строка); + Результат.Добавить(""); + Результат.Добавить(Неопределено); + КонецЕсли; + + Возврат Результат; + +КонецФункции + +Функция РазбитьСтрокуПоСтроке(Знач Строка, Разделитель) + + Результат = Новый Массив; + Пока Истина Цикл + Позиция = СтрНайти(Строка, Разделитель); + Если Позиция = 0 И ЗначениеЗаполнено(Строка) Тогда + Результат.Добавить(Строка); + Прервать; + КонецЕсли; + + ПерваяЧасть = Лев(Строка, Позиция - СтрДлина(Разделитель) + 1); + Результат.Добавить(ПерваяЧасть); + Строка = Сред(Строка, Позиция + СтрДлина(Разделитель)); + КонецЦикла; + + Возврат Результат; + +КонецФункции + +Функция ДобавитьЛидирующуюТочку(Знач Домен) + + Если Не СтрНачинаетсяС(Домен, ".") Тогда + Домен = "." + Домен; + КонецЕсли; + + Возврат Домен; + +КонецФункции + +#КонецОбласти + #КонецОбласти