diff --git a/packagedef b/packagedef
index d64c06a..fdce502 100644
--- a/packagedef
+++ b/packagedef
@@ -2,6 +2,7 @@
.Версия("1.3.0")
.Автор("Dmitry Ivanov")
.АдресАвтора("https://github.com/Stivo182")
+ .АдресРепозитория("https://github.com/Stivo182/oscript-httpbin")
.Описание("Сервис тестирования HTTP клиента")
.ВерсияСреды("1.9.2")
.ВключитьФайл("src")
@@ -18,6 +19,7 @@
.ЗависитОт("1connector", "2.3.3")
.ЗависитОт("packageinfo", "1.1.1")
.ЗависитОт("fs", "1.2.0")
+ .ЗависитОт("logos", "1.7.1")
.РазработкаЗависитОт("1testrunner")
.РазработкаЗависитОт("asserts")
.РазработкаЗависитОт("coverage")
diff --git "a/src/app/\320\236\321\201\320\275\320\276\320\262\320\275\320\276\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273.os" "b/src/app/\320\236\321\201\320\275\320\276\320\262\320\275\320\276\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273.os"
index 0fb2440..ef2538b 100644
--- "a/src/app/\320\236\321\201\320\275\320\276\320\262\320\275\320\276\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273.os"
+++ "b/src/app/\320\236\321\201\320\275\320\276\320\262\320\275\320\276\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273.os"
@@ -1,8 +1,8 @@
#Использовать "../internal"
#Использовать 1connector
-Перем Помощник; // ПомощникПодготовкиОтветов
-Перем КаталогШаблонов; // Строка
+Перем _Помощник; // ПомощникПодготовкиОтветов
+Перем _КаталогШаблонов; // Строка
#Область ТочкиМаршрута
@@ -24,7 +24,7 @@
&GET
Процедура ТочкаRobots(Ответ) Экспорт
- Ответ.ТелоТекст = Помощник.ТекстRobots;
+ Ответ.ТелоТекст = _Помощник.ТекстRobots;
КонецПроцедуры
@@ -32,7 +32,7 @@
&GET
Процедура ТочкаDeny(Ответ) Экспорт
- Ответ.ТелоТекст = Помощник.ASCII_Deny;
+ Ответ.ТелоТекст = _Помощник.ASCII_Deny;
Ответ.Заголовки.Удалить("Content-Type");
КонецПроцедуры
@@ -41,8 +41,8 @@
&GET
Процедура ТочкаIP(Запрос, Ответ) Экспорт
- Данные = Помощник.ПолучитьДанныеЗапроса("origin", Запрос);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные);
+ Данные = _Помощник.ПолучитьДанныеЗапроса("origin", Запрос);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные);
КонецПроцедуры
@@ -52,7 +52,7 @@
Данные = Новый Структура("uuid", Строка(Новый УникальныйИдентификатор()));
- Помощник.ЗаполнитьОтветJson(Ответ, Данные);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные);
КонецПроцедуры
@@ -60,7 +60,7 @@
&GET
Процедура ТочкаUuidN(Ответ, Знач Количество) Экспорт
- Количество = Помощник.ВЧисло(Количество);
+ Количество = _Помощник.ВЧисло(Количество);
Массив = Новый Массив();
Пока Количество > 0 Цикл
@@ -70,7 +70,7 @@
Данные = Новый Структура("uuid", Массив);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные);
КонецПроцедуры
@@ -78,8 +78,8 @@
&GET
Процедура ТочкаHeaders(Запрос, Ответ) Экспорт
- Данные = Помощник.ПолучитьДанныеЗапроса("headers", Запрос);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные);
+ Данные = _Помощник.ПолучитьДанныеЗапроса("headers", Запрос);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные);
КонецПроцедуры
@@ -87,12 +87,12 @@
&GET
Процедура ТочкаUserAgent(Запрос, Ответ) Экспорт
- UserAgent = Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "User-Agent");
+ UserAgent = _Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "User-Agent");
Результат = Новый Соответствие();
Результат.Вставить("user-agent", UserAgent);
- Помощник.ЗаполнитьОтветJson(Ответ, Результат);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Результат);
КонецПроцедуры
@@ -105,11 +105,11 @@
&ТочкаМаршрута("anything")
Процедура ТочкаAnything(Запрос, ДанныеФормы, Ответ) Экспорт
- Данные = Помощник.ПолучитьДанныеЗапроса("args, data, files, form, headers, json, method, origin, url",
+ Данные = _Помощник.ПолучитьДанныеЗапроса("args, data, files, form, headers, json, method, origin, url",
Запрос,
ДанныеФормы);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные);
КонецПроцедуры
@@ -150,14 +150,14 @@
&ТочкаМаршрута("redirect-to")
Процедура ТочкаRedirectTo(Запрос, ДанныеФормы, Ответ) Экспорт
- Данные = Помощник.ПолучитьДанныеЗапроса("args, form", Запрос, ДанныеФормы);
+ Данные = _Помощник.ПолучитьДанныеЗапроса("args, form", Запрос, ДанныеФормы);
Если Запрос.Метод = "GET" Тогда
URL = Данные["args"].Получить("url");
- КодСостояния = Помощник.ВЧисло(Данные["args"].Получить("status_code"));
+ КодСостояния = _Помощник.ВЧисло(Данные["args"].Получить("status_code"));
Иначе
URL = Данные["form"].Получить("url");
- КодСостояния = Помощник.ВЧисло(Данные["form"].Получить("status_code"));
+ КодСостояния = _Помощник.ВЧисло(Данные["form"].Получить("status_code"));
КонецЕсли;
Если КодСостояния >= КодыСостоянияHTTP.МножествоВыборов_300
@@ -192,8 +192,8 @@
Если Не СтрНайти(КодыСостояний, ",") Тогда
- РазделеннаяСтрока = Помощник.РазделитьСтроку(КодыСостояний, ":");
- КодСостояния = Помощник.ВЧисло(РазделеннаяСтрока.Лево);
+ РазделеннаяСтрока = _Помощник.РазделитьСтроку(КодыСостояний, ":");
+ КодСостояния = _Помощник.ВЧисло(РазделеннаяСтрока.Лево);
Если КодСостояния = 0 Тогда
Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеверныйЗапрос_400);
@@ -206,9 +206,9 @@
ВзвешенныйСписок = Новый Массив();
Для каждого Строка Из СтрРазделить(КодыСостояний, ",") Цикл
- РазделеннаяСтрока = Помощник.РазделитьСтроку(Строка, ":");
+ РазделеннаяСтрока = _Помощник.РазделитьСтроку(Строка, ":");
- КодСостояния = Помощник.ВЧисло(РазделеннаяСтрока.Лево);
+ КодСостояния = _Помощник.ВЧисло(РазделеннаяСтрока.Лево);
Если КодСостояния = 0 Тогда
Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеверныйЗапрос_400);
Ответ.ТелоТекст = "Invalid status code";
@@ -218,17 +218,17 @@
Если ПустаяСтрока(РазделеннаяСтрока.Право) Тогда
Вес = 1;
Иначе
- Вес = Помощник.ВЧисло(РазделеннаяСтрока.Право);
+ Вес = _Помощник.ВЧисло(РазделеннаяСтрока.Право);
КонецЕсли;
ВзвешенныйСписок.Добавить(Новый Структура("Значение, Вес", КодСостояния, Вес));
КонецЦикла;
- КодСостояния = Помощник.ВыбратьСлучайныйЭлементСУчетомВеса(ВзвешенныйСписок);
+ КодСостояния = _Помощник.ВыбратьСлучайныйЭлементСУчетомВеса(ВзвешенныйСписок);
КонецЕсли;
- Помощник.ЗаполнитьОтветПоСостоянию(Ответ, КодСостояния);
+ _Помощник.ЗаполнитьОтветПоСостоянию(Ответ, КодСостояния);
КонецПроцедуры
@@ -240,10 +240,10 @@
Ответ.УстановитьТипКонтента("json");
Данные = Новый Соответствие();
- Помощник.ДополнитьСоответствие(Данные, Ответ.Заголовки);
- Помощник.ДополнитьСоответствие(Данные, Запрос.ПараметрыИменные);
+ _Помощник.ДополнитьСоответствие(Данные, Ответ.Заголовки);
+ _Помощник.ДополнитьСоответствие(Данные, Запрос.ПараметрыИменные);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные);
КонецПроцедуры
@@ -257,7 +257,7 @@
КонецЦикла;
Куки.Удалить("SessionID");
- Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("cookies", Куки));
+ _Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("cookies", Куки));
КонецПроцедуры
@@ -309,14 +309,14 @@
&GET
Процедура ТочкаBasicAuth(Запрос, Ответ, ИмяПользователя, Пароль) Экспорт
- ДанныеАутентификации = Помощник.ДанныеАутентификации(Запрос);
+ ДанныеАутентификации = _Помощник.ДанныеАутентификации(Запрос);
Если ДанныеАутентификации.Тип = "basic"
И ДанныеАутентификации.ИмяПользователя = ИмяПользователя
И ДанныеАутентификации.Пароль = Пароль Тогда
- Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("authenticated, user", Истина, ИмяПользователя));
+ _Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("authenticated, user", Истина, ИмяПользователя));
Иначе
- Помощник.ЗаполнитьОтветПоСостоянию(Ответ, КодыСостоянияHTTP.НеАвторизован_401);
+ _Помощник.ЗаполнитьОтветПоСостоянию(Ответ, КодыСостоянияHTTP.НеАвторизован_401);
КонецЕсли;
КонецПроцедуры
@@ -325,12 +325,12 @@
&GET
Процедура ТочкаHiddenBasicAuth(Запрос, Ответ, ИмяПользователя, Пароль) Экспорт
- ДанныеАутентификации = Помощник.ДанныеАутентификации(Запрос);
+ ДанныеАутентификации = _Помощник.ДанныеАутентификации(Запрос);
Если ДанныеАутентификации.Тип = "basic"
И ДанныеАутентификации.ИмяПользователя = ИмяПользователя
И ДанныеАутентификации.Пароль = Пароль Тогда
- Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("authenticated, user", Истина, ИмяПользователя));
+ _Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("authenticated, user", Истина, ИмяПользователя));
Иначе
Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеНайдено_404);
КонецЕсли;
@@ -341,10 +341,10 @@
&GET
Процедура ТочкаBearer(Запрос, Ответ) Экспорт
- ДанныеАутентификации = Помощник.ДанныеАутентификации(Запрос);
+ ДанныеАутентификации = _Помощник.ДанныеАутентификации(Запрос);
Если ДанныеАутентификации.Тип = "bearer" Тогда
- Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("authenticated, token", Истина, ДанныеАутентификации.Токен));
+ _Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("authenticated, token", Истина, ДанныеАутентификации.Токен));
Иначе
Ответ.Заголовки["WWW-Authenticate"] = "Bearer";
Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеАвторизован_401);
@@ -355,7 +355,7 @@
&ТочкаМаршрута("delay/{Секунд}")
Процедура ТочкаDelay(Запрос, ДанныеФормы, Ответ, Секунд) Экспорт
- Секунд = Помощник.ВЧисло(Секунд);
+ Секунд = _Помощник.ВЧисло(Секунд);
Приостановить(Секунд * 1000);
ПередатьПолныеДанныеВОтветJson(Запрос, ДанныеФормы, Ответ);
@@ -378,11 +378,11 @@
&GET
Процедура ТочкаCache(Запрос, Ответ) Экспорт
- ЕстьЗаголовки = Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-Modified-Since") <> Неопределено
- Или Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-None-Match") <> Неопределено;
+ ЕстьЗаголовки = _Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-Modified-Since") <> Неопределено
+ Или _Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-None-Match") <> Неопределено;
Если Не ЕстьЗаголовки Тогда
- Ответ.Заголовки["Last-Modified"] = Помощник.ДатаHTTP();
+ Ответ.Заголовки["Last-Modified"] = _Помощник.ДатаВФорматеHTTP(ТекущаяУниверсальнаяДата());
Ответ.Заголовки["ETag"] = СтрЗаменить(Новый УникальныйИдентификатор(), "-", "");
ПередатьДанныеВОтветJsonGet(Запрос, Ответ);
@@ -396,7 +396,7 @@
&GET
Процедура ТочкаУстановитьCacheControl(Запрос, Ответ, Секунд) Экспорт
- Секунд = Помощник.ВЧисло(Секунд);
+ Секунд = _Помощник.ВЧисло(Секунд);
Ответ.Заголовки["Cache-Control"] = СтрШаблон("public, max-age=%1", Формат(Секунд, "ЧГ="));
@@ -408,11 +408,11 @@
&GET
Процедура ТочкаETag(Запрос, Ответ, ETag) Экспорт
- ЗаголовокIfNoneMatch = Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-None-Match");
- ЗаголовокIfMatch = Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-Match");
+ ЗаголовокIfNoneMatch = _Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-None-Match");
+ ЗаголовокIfMatch = _Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-Match");
- ЗначенияIfNoneMatch = Помощник.РаспаристьМногозначныйЗаголовок(ЗаголовокIfNoneMatch);
- ЗначенияIfMatch = Помощник.РаспаристьМногозначныйЗаголовок(ЗаголовокIfMatch);
+ ЗначенияIfNoneMatch = _Помощник.РаспаристьМногозначныйЗаголовок(ЗаголовокIfNoneMatch);
+ ЗначенияIfMatch = _Помощник.РаспаристьМногозначныйЗаголовок(ЗаголовокIfMatch);
Если ЗначениеЗаполнено(ЗначенияIfNoneMatch) Тогда
Если ЗначенияIfNoneMatch.Найти(ETag) <> Неопределено Или ЗначенияIfNoneMatch.Найти("*") <> Неопределено Тогда
@@ -444,11 +444,11 @@
&GET
Процедура ТочкаBytes(ПараметрыЗапросаИменные, Ответ, КоличествоБайт) Экспорт
- КоличествоБайт = Помощник.ВЧисло(КоличествоБайт);
+ КоличествоБайт = _Помощник.ВЧисло(КоличествоБайт);
НачальноеЧисло = ПараметрыЗапросаИменные.Получить("seed");
Если Не НачальноеЧисло = Неопределено Тогда
- НачальноеЧисло = Помощник.ВЧисло(НачальноеЧисло);
+ НачальноеЧисло = _Помощник.ВЧисло(НачальноеЧисло);
ГенераторСлучайныхЧисел = Новый ГенераторСлучайныхЧисел(НачальноеЧисло);
Иначе
ГенераторСлучайныхЧисел = Новый ГенераторСлучайныхЧисел();
@@ -473,8 +473,8 @@
&GET
Процедура ТочкаLinks(Ответ, Количество, Смещение) Экспорт
- Количество = Помощник.ВЧисло(Количество);
- Смещение = Помощник.ВЧисло(Смещение);
+ Количество = _Помощник.ВЧисло(Количество);
+ Смещение = _Помощник.ВЧисло(Смещение);
Html = Новый Массив();
Html.Добавить("
Links");
@@ -504,7 +504,7 @@
&GET
Процедура ТочкаLinksRedirect(Ответ, Количество) Экспорт
- Количество = Помощник.ВЧисло(Количество);
+ Количество = _Помощник.ВЧисло(Количество);
URL = СтрШаблон("/links/%1/0", Формат(Количество, "ЧГ="));
Ответ.Перенаправить(URL);
@@ -515,7 +515,7 @@
&GET
Процедура ТочкаImage(Запрос, Ответ) Экспорт
- Accept = Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "Accept");
+ Accept = _Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "Accept");
Если Accept = "image/webp" Тогда
ТочкаImageWebp(Ответ);
@@ -528,7 +528,7 @@
Или Accept = "*/*" Тогда
ТочкаImagePng(Ответ);
Иначе
- Помощник.ЗаполнитьОтветПоСостоянию(Ответ, КодыСостоянияHTTP.Неприемлемо_406);
+ _Помощник.ЗаполнитьОтветПоСостоянию(Ответ, КодыСостоянияHTTP.Неприемлемо_406);
КонецЕсли;
КонецПроцедуры
@@ -593,29 +593,29 @@
&ТочкаМаршрута("gzip")
&GET
Процедура ТочкаGZip(Запрос, Ответ) Экспорт
- Данные = Помощник.ПолучитьДанныеЗапроса("gzipped, headers, method, origin", Запрос);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные, "gzip");
+ Данные = _Помощник.ПолучитьДанныеЗапроса("gzipped, headers, method, origin", Запрос);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные, "gzip");
КонецПроцедуры
&ТочкаМаршрута("deflate")
&GET
Процедура ТочкаDeflate(Запрос, Ответ) Экспорт
- Данные = Помощник.ПолучитьДанныеЗапроса("deflated, headers, method, origin", Запрос);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные, "deflate");
+ Данные = _Помощник.ПолучитьДанныеЗапроса("deflated, headers, method, origin", Запрос);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные, "deflate");
КонецПроцедуры
&ТочкаМаршрута("brotli")
&GET
Процедура ТочкаBrotli(Запрос, Ответ) Экспорт
- Данные = Помощник.ПолучитьДанныеЗапроса("brotli, headers, method, origin", Запрос);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные, "brotli");
+ Данные = _Помощник.ПолучитьДанныеЗапроса("brotli, headers, method, origin", Запрос);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные, "brotli");
КонецПроцедуры
&ТочкаМаршрута("zstd")
&GET
Процедура ТочкаZStd(Запрос, Ответ) Экспорт
- Данные = Помощник.ПолучитьДанныеЗапроса("zstd, headers, method, origin", Запрос);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные, "zstd");
+ Данные = _Помощник.ПолучитьДанныеЗапроса("zstd, headers, method, origin", Запрос);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные, "zstd");
КонецПроцедуры
#КонецОбласти
@@ -625,14 +625,14 @@
&Контроллер("/")
Процедура ПриСозданииОбъекта(&Пластилин("ПомощникПодготовкиОтветов") ПомощникПодготовкиОтветов)
- Помощник = ПомощникПодготовкиОтветов;
- КаталогШаблонов = "./src/app/view";
+ _Помощник = ПомощникПодготовкиОтветов;
+ _КаталогШаблонов = "./src/app/view";
КонецПроцедуры
Процедура Redirect(Количество, ПередаватьАбсолютныйПуть, Ответ)
- Количество = Помощник.ВЧисло(Количество);
+ Количество = _Помощник.ВЧисло(Количество);
Если Количество <= 0 Тогда
Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеНайдено_404);
@@ -648,7 +648,7 @@
КонецЕсли;
Если ПередаватьАбсолютныйПуть Тогда
- Адрес = Помощник.ПолучитьURL(Путь);
+ Адрес = _Помощник.URL(Путь);
Иначе
Адрес = Путь;
КонецЕсли;
@@ -659,25 +659,25 @@
Процедура ПередатьДанныеВОтветJsonGet(Запрос, Ответ)
- Данные = Помощник.ПолучитьДанныеЗапроса("args, headers, origin, url", Запрос);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные);
+ Данные = _Помощник.ПолучитьДанныеЗапроса("args, headers, origin, url", Запрос);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные);
КонецПроцедуры
Процедура ПередатьПолныеДанныеВОтветJson(Запрос, ДанныеФормы, Ответ)
- Данные = Помощник.ПолучитьДанныеЗапроса("args, data, files, form, headers, json, origin, url",
+ Данные = _Помощник.ПолучитьДанныеЗапроса("args, data, files, form, headers, json, origin, url",
Запрос,
ДанныеФормы);
- Помощник.ЗаполнитьОтветJson(Ответ, Данные);
+ _Помощник.ЗаполнитьОтветJson(Ответ, Данные);
КонецПроцедуры
Процедура ОтдатьФайл(Путь, ТипКонтента, Ответ)
Разделитель = ПолучитьРазделительПути();
- ПутьКФайлу = КаталогШаблонов + Разделитель + Путь;
+ ПутьКФайлу = _КаталогШаблонов + Разделитель + Путь;
Ответ.Заголовки["Content-Type"] = ТипКонтента;
Ответ.ТелоДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
diff --git "a/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214.os" "b/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214.os"
index 6751fc8..dda2906 100644
--- "a/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214.os"
+++ "b/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214.os"
@@ -2,7 +2,7 @@
&Опция(
Имя = "h host",
- Описание = "Имя хоста / IP адрес сервиса"
+ Описание = "Имя хоста или IP-адрес сервиса"
)
&ТСтрока
&ПоУмолчанию("127.0.0.1")
@@ -10,14 +10,14 @@
&Опция(
Имя = "p port",
- Описание = "Порт сервиса"
+ Описание = "TCP-порт сервиса"
)
&ТЧисло
&ПоУмолчанию(3333)
Перем _Порт; // Число
&Опция(
- Имя = "parrent-pid",
+ Имя = "parent-pid",
Описание = "PID родительского процесса"
)
&ТЧисло
diff --git "a/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\240\320\276\320\264\320\270\321\202\320\265\320\273\321\214\321\201\320\272\320\276\320\263\320\276\320\237\321\200\320\276\321\206\320\265\321\201\321\201\320\260.os" "b/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\240\320\276\320\264\320\270\321\202\320\265\320\273\321\214\321\201\320\272\320\276\320\263\320\276\320\237\321\200\320\276\321\206\320\265\321\201\321\201\320\260.os"
index 62cdc82..3799356 100644
--- "a/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\240\320\276\320\264\320\270\321\202\320\265\320\273\321\214\321\201\320\272\320\276\320\263\320\276\320\237\321\200\320\276\321\206\320\265\321\201\321\201\320\260.os"
+++ "b/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\240\320\276\320\264\320\270\321\202\320\265\320\273\321\214\321\201\320\272\320\276\320\263\320\276\320\237\321\200\320\276\321\206\320\265\321\201\321\201\320\260.os"
@@ -1,25 +1,32 @@
-Перем _ОжиданиеМеждуПроверками; // Число
-Перем _ФоновоеЗадание; // ФоновоеЗадание, Неопределено
+#Использовать logos
+
+Перем _ОжиданиеМеждуПроверками; // Число - Миллисекунды между проверками
+Перем _ФоновоеЗадание; // ФоновоеЗадание, Неопределено
+Перем _Лог; // Лог
&Желудь
Процедура ПриСозданииОбъекта()
_ОжиданиеМеждуПроверками = 5000;
+ _Лог = Логирование.ПолучитьЛог("oscript.lib.httpbin");
КонецПроцедуры
#Область ПрограммныйИнтерфейс
// Запускает фоновое наблюдение за процессом с указанным идентификатором.
-// Если процесс завершается, текущий процесс также будет автоматически завершен.
+// Если наблюдаемый процесс завершается, текущий процесс автоматически завершится.
//
// Параметры:
-// PID - Число - Идентификатор процесса, за которым необходимо наблюдать
+// PID - Число - Идентификатор родительского процесса для наблюдения
+//
Процедура НачатьНаблюдение(PID) Экспорт
Если Не _ФоновоеЗадание = Неопределено
И _ФоновоеЗадание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
- ВызватьИсключение "Наблюдение за родительским процессом уже запущено";
+ ВызватьИсключение "Невозможно запустить наблюдение: уже активно наблюдение за процессом.";
КонецЕсли;
+ _Лог.Отладка("Запуск наблюдения за родительским процессом PID=%1", PID);
+
ПараметрыМетода = Новый Массив();
ПараметрыМетода.Добавить(PID);
@@ -44,6 +51,7 @@
Процесс = НайтиПроцессПоИдентификатору(PID);
Если Процесс = Неопределено Тогда
+ _Лог.Отладка("Родительский процесс PID=%1 завершён. Завершение текущего процесса", PID);
ТекущийПроцесс().Завершить();
КонецЕсли;
diff --git "a/src/core/\320\232\320\273\320\260\321\201\321\201\321\213/HttpBin.os" "b/src/core/\320\232\320\273\320\260\321\201\321\201\321\213/HttpBin.os"
index 1bcdabd..f9eb541 100644
--- "a/src/core/\320\232\320\273\320\260\321\201\321\201\321\213/HttpBin.os"
+++ "b/src/core/\320\232\320\273\320\260\321\201\321\201\321\213/HttpBin.os"
@@ -3,79 +3,79 @@
#Использовать 1connector
#Использовать fs
+#Использовать logos
-Перем _ЗапускатьВФоне; // Булево
-Перем _ОжидатьЗапуск; // Булево
-Перем _ТаймаутЗапуска; // Число
-Перем _ТаймаутСоединенияHttp; // Число
-Перем _ИмяХоста; // Строка
-Перем _Порт; // Число
-Перем _Процесс; // Процесс, Неопределено
+Перем _ТаймаутЗапуска; // Число - Таймаут запуска сервиса в секундах
+Перем _ТаймаутПроверки; // Число - Таймаут HTTP-проверки доступности в секундах
+Перем _ИмяХоста; // Строка - IP-адрес или доменное имя хоста
+Перем _Порт; // Число - Номер TCP-порта сервиса
+Перем _Процесс; // Процесс, Неопределено - Объект запущенного процесса
+Перем _Лог; // Лог - Логгер для отладочной информации
#Область ПрограммныйИнтерфейс
-// Запускает сервис
+// Запускает HTTP-сервис в синхронном режиме с ожиданием полной готовности.
+// Блокирует выполнение до тех пор, пока сервис не начнет отвечать на запросы
+// или не истечет таймаут запуска.
//
// Возвращаемое значение:
-// ЭтотОбъект
+// ЭтотОбъект - Для возможности цепочки вызовов
+//
Функция Запустить() Экспорт
+ Возврат ЗапуститьПроцесс(Истина);
+КонецФункции
- Если Активен() Тогда
- ВызватьИсключение "Процесс уже запущен";
- КонецЕсли;
-
- РабочийКаталог = ФС.НормализоватьПуть(ОбъединитьПути(ТекущийСценарий().Каталог, "../../.."));
- ИсполняемыйФайл = НайтиИсполняемыйФайл();
-
- СтрокаКоманды = Новый Массив();
- СтрокаКоманды.Добавить(ОбернутьВДвойныеКавычки(ИсполняемыйФайл));
- СтрокаКоманды.Добавить(ОбернутьВКавычки(ТочкаВходаКонсольногоПриложения()));
- СтрокаКоманды.Добавить("run");
- СтрокаКоманды.Добавить("--host");
- СтрокаКоманды.Добавить(_ИмяХоста);
- СтрокаКоманды.Добавить("--port");
- СтрокаКоманды.Добавить(Формат(_Порт, "ЧГ="));
- СтрокаКоманды.Добавить("--parrent-pid");
- СтрокаКоманды.Добавить(Формат(ТекущийПроцесс().Идентификатор, "ЧГ="));
- СтрокаКоманды = СтрСоединить(СтрокаКоманды, " ");
-
- _Процесс = СоздатьПроцесс(СтрокаКоманды, РабочийКаталог, , , , ПеременныеСреды());
- _Процесс.Запустить();
+// Запускает HTTP-сервис в асинхронном режиме без ожидания готовности.
+// Возвращает управление немедленно после старта процесса.
+// Используйте метод Отвечает() для проверки готовности сервиса.
+//
+// Возвращаемое значение:
+// ЭтотОбъект - Для возможности цепочки вызовов
+//
+Функция ЗапуститьАсинх() Экспорт
+ Возврат ЗапуститьПроцесс(Ложь);
+КонецФункции
- Если _ОжидатьЗапуск Тогда
- НачатьОжиданиеЗапуска();
+// Останавливает работающий HTTP-сервис.
+// Если сервис не запущен, метод ничего не делает.
+//
+// Возвращаемое значение:
+// ЭтотОбъект - Для возможности цепочки вызовов
+//
+Функция Остановить() Экспорт
- Если Не Активен() Тогда
- ВызватьИсключение СтрШаблон("Не удалось запустить сервис по адресу %1:%2", Хост(), Порт());
- КонецЕсли;
- КонецЕсли;
-
- Если Не _ЗапускатьВФоне Тогда
- _Процесс.ОжидатьЗавершения();
+ Если Активен() Тогда
+ _Процесс.Завершить();
КонецЕсли;
Возврат ЭтотОбъект;
-
+
КонецФункции
-// Останавливает сервис
+// Ожидает завершения работы сервиса.
+// Блокирует выполнение до тех пор, пока процесс сервиса не завершится.
//
// Возвращаемое значение:
-// ЭтотОбъект
-Функция Остановить() Экспорт
+// ЭтотОбъект - Для возможности цепочки вызовов
+//
+Функция ОжидатьЗавершения() Экспорт
- Если Активен() Тогда
- _Процесс.Завершить();
+ Если Не Активен() Тогда
+ ВызватьИсключение "Невозможно ожидать завершения: процесс сервиса не запущен";
КонецЕсли;
+ _Процесс.ОжидатьЗавершения();
+
Возврат ЭтотОбъект;
КонецФункции
-// Проверяет, что сервис отвечает на HTTP запросы без ошибок
+// Проверяет доступность сервиса через HTTP-запрос.
+// Выполняет HEAD-запрос к корневому URL сервиса и проверяет код ответа.
//
// Возвращаемое значение:
-// Булево
+// Булево - Истина, если сервис доступен и возвращает HTTP 200 OK
+//
Функция Отвечает() Экспорт
Если Не Активен() Тогда
@@ -85,9 +85,14 @@
КодСостояния = "";
Попытка
- Ответ = КоннекторHTTP.Head(URL(), Новый Структура("Таймаут", _ТаймаутСоединенияHttp));
+ Ответ = КоннекторHTTP.Head(URL(), Новый Структура("Таймаут", _ТаймаутПроверки));
КодСостояния = Ответ.КодСостояния;
Исключение
+ _Лог.Отладка(
+ "Сервис недоступен по адресу %1. Ошибка: %2",
+ URL(),
+ ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())
+ );
Возврат Ложь;
КонецПопытки;
@@ -95,103 +100,100 @@
КонецФункции
-// Проверяет, что процесс сервиса активен
+// Проверяет, что процесс сервиса запущен и не завершен.
//
// Возвращаемое значение:
// Булево
+//
Функция Активен() Экспорт
Возврат Не _Процесс = Неопределено И Не _Процесс.Завершен;
КонецФункции
-// Возвращает адрес сервиса
+// Возвращает номер TCP-порта, на котором работает сервис.
//
// Возвращаемое значение:
-// Строка
-Функция URL() Экспорт
- Возврат СтрШаблон("http://%1:%2", _ИмяХоста, _Порт);
-КонецФункции
-
-// Порт сервиса
+// Число - Номер порта (1-65535)
//
-// Возвращаемое значение:
-// Строка
Функция Порт() Экспорт
Возврат _Порт;
КонецФункции
-// Устанавливает порт сервиса
+// Устанавливает TCP-порт для запуска сервиса.
+// Должен быть вызван до запуска сервиса.
//
// Параметры:
-// Порт - Число - Номер порта
+// Порт - Число - Номер порта (1-65535)
//
// Возвращаемое значение:
-// ЭтотОбъект
+// ЭтотОбъект - Для возможности цепочки вызовов
+//
Функция УстановитьПорт(Порт) Экспорт
_Порт = Порт;
Возврат ЭтотОбъект;
КонецФункции
-// Имя хоста / ip адрес сервиса
+// Возвращает имя хоста или IP-адрес сервиса.
//
// Возвращаемое значение:
-// Строка
+// Строка - Имя хоста или IP-адрес (например, "127.0.0.1" или "localhost")
+//
Функция Хост() Экспорт
Возврат _ИмяХоста;
КонецФункции
-// Устанавливает хост сервиса
+// Устанавливает имя хоста или IP-адрес для запуска сервиса.
+// Должен быть вызван до запуска сервиса.
//
// Параметры:
-// Хост - Строка - Имя хоста / ip адрес
+// Хост - Строка - Имя хоста или IP-адрес (например, "127.0.0.1", "localhost", "0.0.0.0")
//
// Возвращаемое значение:
-// ЭтотОбъект
+// ЭтотОбъект - Для возможности цепочки вызовов
+//
Функция УстановитьХост(Хост) Экспорт
_ИмяХоста = Хост;
Возврат ЭтотОбъект;
КонецФункции
-// Запуск сервиса будет выполнен в фоновом режиме
+// Формирует полный URL-адрес сервиса с опциональным путем к ресурсу.
//
// Параметры:
-// Флаг - Булево
+// АдресРесурса - Строка - Путь к ресурсу (например, "/get").
+// Слэш в начале необязателен.
//
// Возвращаемое значение:
-// ЭтотОбъект
-Функция ЗапускатьВФоне(Флаг = Истина) Экспорт
- _ЗапускатьВФоне = Флаг;
- Возврат ЭтотОбъект;
-КонецФункции
-
-// Ожидать завершение запуска сервиса
+// Строка - Полный URL (например, "http://127.0.0.1:3333/get")
//
-// Параметры:
-// Флаг - Булево
-//
-// Возвращаемое значение:
-// ЭтотОбъект
-Функция ОжидатьЗапуск(Флаг = Истина) Экспорт
- _ОжидатьЗапуск = Флаг;
- Возврат ЭтотОбъект;
+Функция URL(Знач АдресРесурса = "") Экспорт
+
+ Если Лев(АдресРесурса, 1) <> "/" Тогда
+ АдресРесурса = "/" + АдресРесурса;
+ КонецЕсли;
+
+ Возврат СтрШаблон("http://%1:%2%3", _ИмяХоста, _Порт, АдресРесурса);
+
КонецФункции
-// Устанавливает таймаут запуска сервиса, запущенного в фоновом режиме
+// Устанавливает максимальное время ожидания запуска сервиса.
+// Применяется при синхронном запуске через метод Запустить().
//
// Параметры:
-// Таймаут - Количество - Таймаут в секундах
+// Таймаут - Число - Таймаут в секундах
//
// Возвращаемое значение:
-// ЭтотОбъект
-Функция УстановитьТаймаут(Таймаут) Экспорт
+// ЭтотОбъект - Для возможности цепочки вызовов
+//
+Функция УстановитьТаймаутЗапуска(Таймаут) Экспорт
_ТаймаутЗапуска = Таймаут;
Возврат ЭтотОбъект;
КонецФункции
-// Таймаут запуска сервиса в секундах, запущенного в фоновом режиме
+// Возвращает текущее значение таймаута запуска сервиса.
//
// Возвращаемое значение:
-// Число
-Функция Таймаут() Экспорт
+// Число - Таймаут в секундах
+//
+Функция ТаймаутЗапуска() Экспорт
Возврат _ТаймаутЗапуска;
КонецФункции
@@ -199,36 +201,82 @@
#Область СлужебныеПроцедурыИФункции
-// Cервис тестирования HTTP клиента.
+// Конструктор класса.
+//
+// По умолчанию сервис настроен на локальный адрес 127.0.0.1:3333
+// с таймаутом запуска 5 секунд.
+//
+// Параметры:
+// Хост - Строка - IP-адрес или имя хоста (по умолчанию "127.0.0.1")
+// Порт - Число - Номер TCP-порта (по умолчанию 3333)
//
-// Сервис по умолчанию запускается по адресу 127.0.0.1:3333 в фоновом режиме
-// и с ожиданием завершения запуска сервиса.
-Процедура ПриСозданииОбъекта()
+Процедура ПриСозданииОбъекта(Хост = "127.0.0.1", Порт = 3333) // BSLLS:MagicNumber-off
- ХостПоУмолчанию = "127.0.0.1";
- ПортПоУмолчанию = 3333;
ТаймаутЗапускаПоУмолчанию = 5;
- _ТаймаутСоединенияHttp = 3;
+ _ТаймаутПроверки = 3;
- УстановитьХост(ХостПоУмолчанию);
- УстановитьПорт(ПортПоУмолчанию);
- ЗапускатьВФоне();
- ОжидатьЗапуск();
- УстановитьТаймаут(ТаймаутЗапускаПоУмолчанию);
+ УстановитьХост(Хост);
+ УстановитьПорт(Порт);
+ УстановитьТаймаутЗапуска(ТаймаутЗапускаПоУмолчанию);
+
+ _Лог = Логирование.ПолучитьЛог("oscript.lib.httpbin");
КонецПроцедуры
+Функция ЗапуститьПроцесс(Синхронно)
+
+ Если Активен() Тогда
+ ВызватьИсключение "Невозможно запустить сервис: процесс уже запущен.";
+ КонецЕсли;
+
+ РабочийКаталог = ФС.НормализоватьПуть(ОбъединитьПути(ТекущийСценарий().Каталог, "../../.."));
+ ИсполняемыйФайл = НайтиИсполняемыйФайл();
+
+ СтрокаКоманды = Новый Массив();
+ СтрокаКоманды.Добавить(ОбернутьВДвойныеКавычки(ИсполняемыйФайл));
+ СтрокаКоманды.Добавить(ОбернутьВКавычки(ТочкаВходаКонсольногоПриложения()));
+ СтрокаКоманды.Добавить("run");
+ СтрокаКоманды.Добавить("--host");
+ СтрокаКоманды.Добавить(_ИмяХоста);
+ СтрокаКоманды.Добавить("--port");
+ СтрокаКоманды.Добавить(Формат(_Порт, "ЧГ="));
+ СтрокаКоманды.Добавить("--parent-pid");
+ СтрокаКоманды.Добавить(Формат(ТекущийПроцесс().Идентификатор, "ЧГ="));
+ СтрокаКоманды = СтрСоединить(СтрокаКоманды, " ");
+
+ _Лог.Отладка("Запуск процесса: %1", СтрокаКоманды);
+
+ _Процесс = СоздатьПроцесс(СтрокаКоманды, РабочийКаталог, , , , ПеременныеСреды());
+ _Процесс.Запустить();
+
+ Если Синхронно Тогда
+ НачатьОжиданиеЗапуска();
+
+ Если Не Активен() Тогда
+ ВызватьИсключение СтрШаблон("Не удалось запустить сервис по адресу %1:%2", Хост(), Порт());
+ КонецЕсли;
+ КонецЕсли;
+
+ Возврат ЭтотОбъект;
+
+КонецФункции
+
Процедура НачатьОжиданиеЗапуска()
ЗадержкаМеждуПопытками = 100; // Миллисекунд
ВремяНачала = ТекущаяУниверсальнаяДата();
+ НомерПопытки = 0;
Пока Истина Цикл
+ НомерПопытки = НомерПопытки + 1;
КодСостояния = Неопределено;
ТекстИсключения = "";
+
+ _Лог.Отладка("Проверка готовности сервиса %1. Попытка: %2", URL(), НомерПопытки);
+
Попытка
- Ответ = КоннекторHTTP.Head(URL(), Новый Структура("Таймаут", _ТаймаутСоединенияHttp));
+ Ответ = КоннекторHTTP.Head(URL(), Новый Структура("Таймаут", _ТаймаутПроверки));
КодСостояния = Ответ.КодСостояния;
Исключение
ТекстИсключения = СокрП(КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
@@ -236,6 +284,7 @@
КонецПопытки;
Если КодСостояния = КодыСостоянияHTTP.ОК_200 Тогда
+ _Лог.Отладка("Сервис %1 успешно запущен и готов к работе.", URL());
Прервать;
КонецЕсли;
@@ -243,17 +292,24 @@
Если ПрошлоСекунд > _ТаймаутЗапуска Или ЗначениеЗаполнено(КодСостояния) Тогда
ТекстОшибки = СтрШаблон(
- "Не удалось запустить веб-сервер по адресу %1:%2 в течение %3 сек.",
+ "Превышен таймаут запуска сервиса %1:%2 (%3 сек). "
+ + "Сервис не начал отвечать на запросы в течение отведенного времени.",
_ИмяХоста,
_Порт,
_ТаймаутЗапуска);
Если ЗначениеЗаполнено(КодСостояния) Тогда
ТекстОшибки = СтрШаблон("%1:
- |%2", ТекстОшибки, КодыСостоянияHTTP.Представление(КодСостояния));
+ |%2",
+ ТекстОшибки,
+ КодыСостоянияHTTP.Представление(КодСостояния)
+ );
Иначе
ТекстОшибки = СтрШаблон("%1:
- |%2", ТекстОшибки, ТекстИсключения);
+ |%2",
+ ТекстОшибки,
+ ТекстИсключения
+ );
КонецЕсли;
ВызватьИсключение ТекстОшибки;
@@ -285,7 +341,7 @@
КонецЕсли;
КонецЦикла;
- ВызватьИсключение "Не удалось найти исполняемый файл oscript";
+ ВызватьИсключение СтрШаблон("Не найден исполняемый файл oscript в каталоге: %1", КаталогПрограммы);
КонецФункции
diff --git "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\276\320\274\320\276\321\211\320\275\320\270\320\272\320\237\320\276\320\264\320\263\320\276\321\202\320\276\320\262\320\272\320\270\320\236\321\202\320\262\320\265\321\202\320\276\320\262.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\276\320\274\320\276\321\211\320\275\320\270\320\272\320\237\320\276\320\264\320\263\320\276\321\202\320\276\320\262\320\272\320\270\320\236\321\202\320\262\320\265\321\202\320\276\320\262.os"
index 187bbc7..71817a1 100644
--- "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\276\320\274\320\276\321\211\320\275\320\270\320\272\320\237\320\276\320\264\320\263\320\276\321\202\320\276\320\262\320\272\320\270\320\236\321\202\320\262\320\265\321\202\320\276\320\262.os"
+++ "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\276\320\274\320\276\321\211\320\275\320\270\320\272\320\237\320\276\320\264\320\263\320\276\321\202\320\276\320\262\320\272\320\270\320\236\321\202\320\262\320\265\321\202\320\276\320\262.os"
@@ -3,67 +3,70 @@
#Использовать 1connector
#Использовать compressor
-Перем ТекстRobots Экспорт; // Строка
-Перем ASCII_Deny Экспорт; // Строка
-Перем ASCII_Teapot; // Строка
-Перем АдресРедиректа; // Строка
-Перем ПоддерживаемыеМедиаТипы; // Массив из Строка
+Перем ТекстRobots Экспорт; // Строка - Содержимое файла robots.txt
+Перем ASCII_Deny Экспорт; // Строка - ASCII-арт для страницы отказа доступа
+Перем ASCII_Teapot; // Строка - ASCII-арт чайника для HTTP 418
+Перем АдресРедиректа; // Строка - URL для редиректов (по умолчанию)
+Перем ПоддерживаемыеМедиаТипы; // Массив из Строка - Список поддерживаемых MIME-типов
-Перем НастройкиВебСервера; // Настройки - см. winow
-Перем Парсеры; // Парсеры - см. winow
-Перем ЭтоНативныйВебСервер; // Булево
+Перем НастройкиВебСервера; // Настройки - Настройки веб-сервера (см. winow)
+Перем Парсеры; // Парсеры - Утилиты парсинга (см. winow)
+Перем ЭтоНативныйВебСервер; // Булево - Признак использования нативного веб-сервера
#Область ПрограммныйИнтерфейс
-// Заполняет ответ json данными
+// Формирует JSON-ответ с опциональным сжатием.
//
// Параметры:
-// Ответ - Ответ - см. winow
-// Данные - Соответствие, Структура - Данные для передачи в тело ответа
-// АлгоритмСжатия - Строка - Алгоритм сжатия данных (gzip, deflate, brotli, zstd)
+// Ответ - Ответ - Объект ответа для заполнения (см. winow)
+// Данные - Соответствие, Структура - Данные для сериализации в JSON
+// АлгоритмСжатия - Строка, Неопределено - Алгоритм сжатия тела ответа (gzip, deflate, brotli, zstd)
+//
Процедура ЗаполнитьОтветJson(Ответ, Данные, АлгоритмСжатия = Неопределено) Экспорт
Ответ.УстановитьТипКонтента("json");
ТекстJson = ВJson(Данные);
-
+ Компрессор = Неопределено;
+
Если АлгоритмСжатия = "gzip" Тогда
Компрессор = Новый GZipКомпрессор();
- Ответ.Заголовки["Content-Encoding"] = "gzip";
- Ответ.ТелоДвоичныеДанные = Компрессор.Упаковать(ПолучитьДвоичныеДанныеИзСтроки(ТекстJson));
+ ЗаголовокСжатия = "gzip";
ИначеЕсли АлгоритмСжатия = "deflate" Тогда
Компрессор = Новый DeflateКомпрессор();
- Ответ.Заголовки["Content-Encoding"] = "deflate";
- Ответ.ТелоДвоичныеДанные = Компрессор.Упаковать(ПолучитьДвоичныеДанныеИзСтроки(ТекстJson));
+ ЗаголовокСжатия = "deflate";
ИначеЕсли АлгоритмСжатия = "brotli" Тогда
Компрессор = Новый BrotliКомпрессор();
- Ответ.Заголовки["Content-Encoding"] = "br";
- Ответ.ТелоДвоичныеДанные = Компрессор.Упаковать(ПолучитьДвоичныеДанныеИзСтроки(ТекстJson));
+ ЗаголовокСжатия = "br";
ИначеЕсли АлгоритмСжатия = "zstd" Тогда
Компрессор = Новый ZStdКомпрессор();
- Ответ.Заголовки["Content-Encoding"] = "zstd";
- Ответ.ТелоДвоичныеДанные = Компрессор.Упаковать(ПолучитьДвоичныеДанныеИзСтроки(ТекстJson));
+ ЗаголовокСжатия = "zstd";
- Иначе
+ КонецЕсли;
+ Если Не Компрессор = Неопределено Тогда
+ ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки(ТекстJson);
+ Ответ.Заголовки["Content-Encoding"] = ЗаголовокСжатия;
+ Ответ.ТелоДвоичныеДанные = Компрессор.Упаковать(ДвоичныеДанные);
+ Иначе
Ответ.ТелоТекст = ТекстJson;
-
КонецЕсли;
КонецПроцедуры
-// Заполняет ответ по состоянию
+// Заполняет ответ стандартным содержимым для указанного HTTP-статуса.
//
// Параметры:
-// Ответ - Ответ - см. winow
-// КодСостояния - Число - Код состояния HTTP
+// Ответ - Ответ - Объект ответа для заполнения (см. winow)
+// КодСостояния - Число - Код HTTP-статуса
+//
Процедура ЗаполнитьОтветПоСостоянию(Ответ, КодСостояния) Экспорт
Ответ.УстановитьСостояние(КодСостояния);
@@ -106,15 +109,32 @@
КонецПроцедуры
-// Данные входящего запроса
+// Извлекает и структурирует данные из входящего HTTP-запроса.
+// Возвращает только указанные ключи из полного набора данных запроса.
//
// Параметры:
-// Ключи - Строка - Ключи через запятую
-// Запрос - ВходящийЗапрос - см. winow
-// ДанныеФормы - ДанныеСоставнойФормы - Данные составной формы (multipart) (см. winow)
+// Ключи - Строка - Имена возвращаемых полей через запятую.
+// Доступные поля: url, method, args, headers, origin, json,
+// data, form, files, gzipped, deflated, brotli, zstd
+// Запрос - ВходящийЗапрос - Объект входящего HTTP-запроса (см. winow)
+// ДанныеФормы - ДанныеСоставнойФормы, Неопределено - Данные multipart/form-data (см. winow)
//
// Возвращаемое значение:
-// Структура
+// Структура - Структура с запрошенными полями:
+// * url - Строка - Полный URL запроса
+// * method - Строка - HTTP-метод (GET, POST, PUT и т.д.)
+// * args - Соответствие - Параметры строки запроса
+// * headers - Соответствие - Заголовки запроса
+// * origin - Строка - IP-адрес клиента
+// * json - Соответствие, null - Распарсенное JSON-тело (если Content-Type: application/json)
+// * data - Строка - Сырое тело запроса (если не form-data)
+// * form - Соответствие - Данные формы (application/x-www-form-urlencoded или multipart)
+// * files - Соответствие - Загруженные файлы из multipart/form-data
+// * gzipped - Булево - Всегда Истина (поддержка gzip)
+// * deflated - Булево - Всегда Истина (поддержка deflate)
+// * brotli - Булево - Всегда Истина (поддержка brotli)
+// * zstd - Булево - Всегда Истина (поддержка zstd)
+//
Функция ПолучитьДанныеЗапроса(Ключи, Запрос, ДанныеФормы = Неопределено) Экспорт
ДанныеФормы = РазделитьДанныеФормы(ДанныеФормы);
@@ -129,7 +149,7 @@
КонецЕсли;
ДанныеЗапроса = Новый Структура();
- ДанныеЗапроса.Вставить("url", ПолучитьURL(Запрос.ПолныйПуть));
+ ДанныеЗапроса.Вставить("url", URL(Запрос.ПолныйПуть));
ДанныеЗапроса.Вставить("method", Запрос.Метод);
ДанныеЗапроса.Вставить("args", Запрос.ПараметрыИменные);
ДанныеЗапроса.Вставить("headers", ЗаголовкиДляJson(Запрос.Заголовки));
@@ -149,20 +169,24 @@
КонецФункции
-// Данные аутентификации из входящего запроса
+// Извлекает данные аутентификации из заголовка Authorization.
+// Поддерживает схемы Basic и Bearer.
//
// Параметры:
-// Запрос - ВходящийЗапрос - см. winow
+// Запрос - ВходящийЗапрос - Объект входящего HTTP-запроса (см. winow)
//
// Возвращаемое значение:
-// Структура:
-// Общее:
-// * Тип - Строка - Тип аутентификации (basic, bearer)
-// Basic:
+// Структура - Данные аутентификации:
+// Общие поля:
+// * Тип - Строка - Схема аутентификации ("basic", "bearer" или пустая строка)
+//
+// Для Basic-аутентификации (Тип = "basic"):
// * ИмяПользователя - Строка, Неопределено - Имя пользователя
// * Пароль - Строка, Неопределено - Пароль
-// Bearer:
-// * Токен - Строка - Токен
+//
+// Для Bearer-токена (Тип = "bearer"):
+// * Токен - Строка - Значение токена
+//
Функция ДанныеАутентификации(Запрос) Экспорт
Данные = Новый Структура("Тип", "");
@@ -205,14 +229,15 @@
КонецФункции
-// Значение заголовка по имени
+// Извлекает значение HTTP-заголовка по имени (регистронезависимый поиск).
//
// Параметры:
// Заголовки - Соответствие, СловарьЗаголовков - Коллекция заголовков
-// Имя - Строка - Имя заголовка
+// Имя - Строка - Имя заголовка для поиска (регистр не важен)
//
// Возвращаемое значение:
-// Строка, Неопределено
+// Строка, Соответствие, Неопределено
+//
Функция ЗначениеЗаголовка(Заголовки, Знач Имя) Экспорт
Имя = НРег(Имя);
@@ -232,13 +257,15 @@
КонецФункции
-// Парсит многозначный заголовок
+// Парсит многозначный HTTP-заголовок (например, If-Match, If-None-Match).
+// Обрабатывает значения с кавычками и опциональным префиксом W/ (weak ETag).
//
// Параметры:
-// Значение - Строка - Значение заголовка
+// Значение - Строка - Значение заголовка с несколькими значениями через запятую
//
// Возвращаемое значение:
-// Массив из Строка - Массив значений
+// Массив из Строка - Массив извлечённых значений
+//
Функция РаспаристьМногозначныйЗаголовок(Значение) Экспорт
Результат = Новый Массив();
@@ -261,56 +288,66 @@
КонецФункции
-// Получает URL к запущенному веб-серверу с добавлением пути
+// Формирует полный URL-адрес сервиса с опциональным путем к ресурсу.
//
// Параметры:
-// ПолныйПуть - Строка - Путь после порта веб-сервера, включая "/"
+// АдресРесурса - Строка - Путь к ресурсу (например, "/get").
+// Слэш в начале необязателен.
//
// Возвращаемое значение:
-// Строка - URL
-Функция ПолучитьURL(ПолныйПуть) Экспорт
+// Строка - Полный URL
+//
+Функция URL(Знач АдресРесурса = "") Экспорт
+
+ СтандартныйПорт = 80;
+
+ Если Лев(АдресРесурса, 1) <> "/" Тогда
+ АдресРесурса = "/" + АдресРесурса;
+ КонецЕсли;
- Возврат "http://"
- + НастройкиВебСервера.ИмяХоста
- + ?(НастройкиВебСервера.Порт = 80, "", ":" + Формат(НастройкиВебСервера.Порт, "ЧГ="))
- + ПолныйПуть;
+ Возврат СтрШаблон(
+ "http://%1%2%3",
+ НастройкиВебСервера.ИмяХоста,
+ ?(НастройкиВебСервера.Порт = СтандартныйПорт, "", ":" + Формат(НастройкиВебСервера.Порт, "ЧГ=")),
+ АдресРесурса
+ );
КонецФункции
-// Преобразует универсальную дату в дату формата rfc1123-date.
-// См. https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html, п. 3.3.1.
+// Форматирует дату в формат RFC 1123 для использования в HTTP-заголовках.
+// Соответствует спецификации HTTP/1.1 (RFC 2616, раздел 3.3.1).
//
// Параметры:
-// Дата - Дата, Неопределено - Если не указано, то беретеся текущая универсальная дата
+// Дата - Дата, Неопределено - Дата для форматирования
//
// Возвращаемое значение:
-// Строка
-Функция ДатаHTTP(Знач Дата = Неопределено) Экспорт
-
- Если Дата = Неопределено Тогда
- Дата = ТекущаяУниверсальнаяДата();
- КонецЕсли;
+// Строка - Дата в формате "Day, DD Mon YYYY HH:MM:SS GMT"
+//
+Функция ДатаВФорматеHTTP(Дата) Экспорт
Возврат Формат(Дата, "Л=en; ДФ='ддд, дд МММ гггг ЧЧ:мм:сс ''GMT'''");
КонецФункции
-// Получает число из строки
+// Безопасно преобразует строку в число.
//
// Параметры:
-// Значение - Строка - Число строкой
+// Значение - Строка - Строковое представление числа
//
// Возвращаемое значение:
-// Число
+// Число - Преобразованное число или 0, если преобразование невозможно
+//
Функция ВЧисло(Значение) Экспорт
Возврат Новый ОписаниеТипов("Число").ПривестиЗначение(Значение);
КонецФункции
-// Дополняет соответствие
+// Копирует все элементы из исходного соответствия в приемник.
+// Существующие ключи в приемнике будут перезаписаны значениями из источника.
//
// Параметры:
-// Приемник - Соответствие - Приемник
-// Источник - Соответствие - Источник
+// Приемник - Соответствие - Соответствие, в которое копируются данные
+// Источник - Соответствие - Соответствие-источник данных
+//
Процедура ДополнитьСоответствие(Приемник, Источник) Экспорт
Для Каждого Элемент Из Источник Цикл
@@ -319,15 +356,18 @@
КонецПроцедуры
-// Выбирает случайный элемент с учетом веса
+// Выбирает случайный элемент из взвешенного списка.
+// Вероятность выбора элемента пропорциональна его весу.
+// Использует бинарный поиск для эффективного выбора.
//
// Параметры:
-// Список - Массив из Структура - Взвешенный список:
-// * Значение - Произвольный - Значение
-// * Вес - Число - Вес
+// Список - Массив из Структура - Взвешенный список элементов:
+// * Значение - Произвольный - Значение элемента для возврата
+// * Вес - Число - Вес элемента (чем больше, тем выше вероятность выбора)
//
// Возвращаемое значение:
-// Произвольный - Случайное значение из списка
+// Произвольный - Случайно выбранное значение с учётом весов
+//
Функция ВыбратьСлучайныйЭлементСУчетомВеса(Список) Экспорт
ОбщийВес = 0;
@@ -355,16 +395,16 @@
КонецФункции
-// Разделяет строку по разделителю
+// Разделяет строку на две части по первому вхождению разделителя.
//
// Параметры:
-// Строка - Строка
-// Разделитель - Строка
+// Строка - Строка - Исходная строка для разделения
+// Разделитель - Строка - Строка-разделитель
//
// Возвращаемое значение:
-// Структура:
-// * Лево - Строка
-// * Право - Строка
+// Структура - Части строки:
+// * Лево - Строка - Часть до разделителя
+// * Право - Строка - Часть после разделителя
Функция РазделитьСтроку(Строка, Разделитель) Экспорт
Результат = Новый Структура("Лево, Право", "", "");
@@ -426,6 +466,16 @@
КонецПроцедуры
+// Извлекает реальный IP-адрес клиента с учётом прокси-серверов.
+// Сначала проверяет заголовок X-Forwarded-For, затем использует прямой адрес.
+// Удаляет IPv6-префикс ::ffff: для IPv4-адресов.
+//
+// Параметры:
+// Запрос - ВходящийЗапрос - Объект входящего HTTP-запроса (см. winow)
+//
+// Возвращаемое значение:
+// Строка - IP-адрес клиента (IPv4 или IPv6)
+//
Функция IPАдресУдаленногоУзла(Запрос)
Адрес = ЗначениеЗаголовка(Запрос.Заголовки, "X-Forwarded-For");
@@ -442,6 +492,16 @@
КонецФункции
+// Парсит данные multipart/form-data и разделяет их на поля формы и файлы.
+//
+// Параметры:
+// ДанныеФормы - ДанныеСоставнойФормы, Неопределено - Данные составной формы
+//
+// Возвращаемое значение:
+// Структура:
+// * Данные - Соответствие - Текстовые поля формы
+// * Файлы - Соответствие - Загруженные файлы
+//
Функция РазделитьДанныеФормы(ДанныеФормы)
Результат = Новый Структура();
@@ -529,6 +589,17 @@
КонецФункции
+// Преобразует двоичные данные в текстовое представление.
+// Для бинарных данных возвращает Data URL (base64).
+// Для текстовых данных возвращает UTF-8 строку.
+//
+// Параметры:
+// ДвоичныеДанные - ДвоичныеДанные - Данные для преобразования
+// ТипКонтента - Строка - MIME-тип данных
+//
+// Возвращаемое значение:
+// Строка
+//
Функция ТекстовоеПредставлениеДвоичныхДанных(ДвоичныеДанные, ТипКонтента)
Если ДвоичныеДанныеСодержатУправляющиеСимволы(ДвоичныеДанные) Тогда
@@ -543,6 +614,15 @@
КонецФункции
+// Проверяет, содержат ли двоичные данные управляющие символы (бинарные данные).
+// Анализирует первые 1024 байта для определения типа содержимого.
+//
+// Параметры:
+// ДвоичныеДанные - ДвоичныеДанные - Данные для проверки
+//
+// Возвращаемое значение:
+// Булево - Истина, если данные содержат управляющие символы (бинарные)
+//
Функция ДвоичныеДанныеСодержатУправляющиеСимволы(ДвоичныеДанные)
КоличествоЧитаемыхБайтов = 1024;
@@ -575,6 +655,15 @@
КонецФункции
+// Нормализует заголовки для сериализации в JSON.
+// Для нативного веб-сервера преобразует СтроковыеЗначения в простые строки.
+//
+// Параметры:
+// Заголовки - Соответствие, СловарьЗаголовков - Коллекция заголовков
+//
+// Возвращаемое значение:
+// Соответствие - Нормализованные заголовки для JSON
+//
Функция ЗаголовкиДляJson(Заголовки)
Если ЭтоНативныйВебСервер Тогда
@@ -603,6 +692,14 @@
КонецФункции
+// Сериализует данные в JSON с форматированием.
+//
+// Параметры:
+// Данные - Соответствие, Структура - Данные для сериализации
+//
+// Возвращаемое значение:
+// Строка
+//
Функция ВJson(Данные)
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, " ");
@@ -615,6 +712,16 @@
КонецФункции
+// Создаёт новую структуру с указанными ключами из исходной структуры.
+// Копирует только запрошенные поля. Значения null заменяются на пустые строки.
+//
+// Параметры:
+// ИсходнаяСтруктура - Структура - Структура-источник данных
+// КопируемыеКлючи - Строка - Имена копируемых полей через запятую
+//
+// Возвращаемое значение:
+// Структура - Новая структура с запрошенными полями
+//
Функция СкопироватьСтруктуру(ИсходнаяСтруктура, КопируемыеКлючи)
Результат = Новый Структура(КопируемыеКлючи);
diff --git a/tests/HttpBin_API_test.os b/tests/HttpBin_API_test.os
index f4c86ff..ce20913 100644
--- a/tests/HttpBin_API_test.os
+++ b/tests/HttpBin_API_test.os
@@ -1216,7 +1216,7 @@
КонецФункции
Функция URL(АдресРсесурса)
- Возврат СтрШаблон("%1/%2", HttpBin.URL(), АдресРсесурса);
+ Возврат HttpBin.URL(АдресРсесурса);
КонецФункции
Функция ПараметрыКоннектора(Заголовки = Неопределено)
@@ -1235,5 +1235,5 @@
КонецФункции
Функция Base64ZipФайла()
- Возврат "UEsDBAoAAAAAAEAIalpdCci4BwAAAAcAAAALAAAAaHR0cGJpbi50eHRIVFRQQklOUEsBAh8ACgAAAAAAQAhqWl0JyLgHAAAABwAAAAsAJAAAAAAAAAAgAAAAAAAAAGh0dHBiaW4udHh0CgAgAAAAAAABABgAYXX04T6R2wFhdfThPpHbAdjuOJY+kdsBUEsFBgAAAAABAAEAXQAAADAAAAAAAA==";
+ Возврат "UEsDBAoAAAAAAEAIalpdCci4BwAAAAcAAAALAAAAaHR0cGJpbi50eHRIVFRQQklOUEsBAh8ACgAAAAAAQAhqWl0JyLgHAAAABwAAAAsAJAAAAAAAAAAgAAAAAAAAAGh0dHBiaW4udHh0CgAgAAAAAAABABgAYXX04T6R2wFhdfThPpHbAdjuOJY+kdsBUEsFBgAAAAABAAEAXQAAADAAAAAAAA=="; // BSLLS:LineLength-off
КонецФункции
\ No newline at end of file
diff --git a/tests/HttpBin_test.os b/tests/HttpBin_test.os
index 521c5b1..8cd4da2 100644
--- a/tests/HttpBin_test.os
+++ b/tests/HttpBin_test.os
@@ -12,36 +12,29 @@
КонецПроцедуры
&Тест
-Процедура Должен_ЗапуститьСервисВФонеСОжиданиемИОстановить() Экспорт
+Процедура ТестДолжен_ЗапуститьСервисСинхронноИОстановить() Экспорт
- HttpBin = Новый HttpBin()
- .ЗапускатьВФоне()
- .ОжидатьЗапуск()
- .Запустить();
+ HttpBin = Новый HttpBin().Запустить();
Ожидаем.Что(HttpBin.Отвечает(), "Должен быть запущен").ЭтоИстина();
КонецПроцедуры
&Тест
-Процедура Должен_ЗапуститьСервисВФонеБезОжиданияИОстановить() Экспорт
+Процедура ТестДолжен_ЗапуститьСервисАсинхронноИОстановить() Экспорт
// Подготовка
+ HttpBin = Новый HttpBin();
+
ВремяНачалаЗапуска = ТекущаяУниверсальнаяДатаВМиллисекундах();
-
- HttpBin = Новый HttpBin()
- .ЗапускатьВФоне()
- .ОжидатьЗапуск()
- .Запустить();
-
+ HttpBin.Запустить();
ВремяЗапуска = ТекущаяУниверсальнаяДатаВМиллисекундах() - ВремяНачалаЗапуска;
-
HttpBin.Остановить();
Приостановить(200);
// Действие
- HttpBin.ОжидатьЗапуск(Ложь).Запустить();
+ HttpBin.ЗапуститьАсинх();
Приостановить(ВремяЗапуска);