├── .github └── workflows │ ├── qa.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── appsettings.json ├── autumn-properties.json ├── lib.config ├── packagedef ├── sonar-project.properties ├── src ├── app │ ├── HttpBin_ОсновнойКонтрол.os │ └── view │ │ ├── UTF-8-demo.txt │ │ ├── forms-post.html │ │ ├── images │ │ ├── jackal.jpg │ │ ├── pig_icon.png │ │ ├── svg_logo.svg │ │ └── wolf_1.webp │ │ ├── index.html │ │ ├── moby.html │ │ ├── sample.xml │ │ └── static │ │ ├── openapi.json │ │ └── swagger │ │ ├── index.css │ │ ├── swagger-ui-bundle.js │ │ ├── swagger-ui-standalone-preset.js │ │ └── swagger-ui.css ├── cmd │ ├── main.os │ └── Классы │ │ └── HttpBin_КомандаЗапустить.os ├── core │ └── Классы │ │ └── HttpBin.os └── internal │ └── Классы │ └── ПомощникПодготовкиОтветов.os ├── tasks ├── coverage.os ├── oscript.cfg └── test.os └── tests ├── HttpBin_API_test.os └── HttpBin_test.os /.github/workflows/qa.yml: -------------------------------------------------------------------------------- 1 | name: Контроль качества 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | sonar: 9 | if: github.repository_owner == 'stivo182' 10 | uses: autumn-library/workflows/.github/workflows/sonar.yml@main 11 | with: 12 | github_repository: stivo182/oscript-httpbin 13 | oscript_version: dev 14 | secrets: 15 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Публикация релиза 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | workflow_dispatch: 8 | 9 | jobs: 10 | release: 11 | uses: autumn-library/workflows/.github/workflows/release.yml@main 12 | with: 13 | package_mask: "httpbin-*.ospx" 14 | secrets: 15 | PUSH_TOKEN: ${{ secrets.PUSH_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Тестирование 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | test: 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | oscript_version: ['stable', 'dev'] 14 | uses: autumn-library/workflows/.github/workflows/test.yml@main 15 | with: 16 | oscript_version: ${{ matrix.oscript_version }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | coverage/* 3 | out/* 4 | oscript_modules/* 5 | *.ospx -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Dmitry Ivanov, 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # httpbin 2 | 3 | [![Release](https://img.shields.io/github/release/Stivo182/oscript-httpbin.svg)](https://github.com/Stivo182/oscript-httpbin/releases) 4 | [![Тестирование](https://github.com/stivo182/oscript-httpbin/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/stivo182/oscript-httpbin/actions/workflows/test.yml) 5 | [![Статус порога качества](https://sonar.openbsl.ru/api/project_badges/measure?project=httpbin&metric=alert_status&token=sqb_2f7c84743fd1b295085c25a1b96cc8d975cd4dc7)](https://sonar.openbsl.ru/dashboard?id=httpbin) 6 | [![Покрытие](https://sonar.openbsl.ru/api/project_badges/measure?project=httpbin&metric=coverage&token=sqb_2f7c84743fd1b295085c25a1b96cc8d975cd4dc7)](https://sonar.openbsl.ru/dashboard?id=httpbin) 7 | 8 | Cервис позволяющий локально тестировать HTTP клиент. Разработан на [OneScript](https://github.com/EvilBeaver/OneScript) + [WINOW](https://github.com/autumn-library/winow). Поддерживает бо́льшую часть эндпоинтов [httpbin.org](https://httpbin.org/). 9 | 10 | * 1\. [Установка](#установка) 11 | * 2\. [Использование](#использование) 12 | * 2.1\. [CLI приложение](#cli-приложение) 13 | * 2.2\. [Тестирование через asserts и 1connector](#тестирование-через-asserts-и-1connector) 14 | * 2.3\. [Swagger UI](#swagger-ui) 15 | * 3\. [Совместимость](#совместимость) 16 | * 4\. [Программный интерфейс](#программный-интерфейс) 17 | * 5\. [Ограничения](#ограничения) 18 | * 6\. [Сравнение с httpbin.org](#сравнение-с-httpbinorg) 19 | 20 | ## Установка 21 | 22 | ``` bash 23 | opm install httpbin 24 | ``` 25 | 26 | ## Использование 27 | 28 | ### CLI приложение 29 | 30 | Запуск сервиса через команду **run**: `httpbin run` 31 | 32 | Опции команды:
33 | `-h`, `--host` - имя хоста / IP адрес сервиса
34 | `-p`, `--port` - порт сервиса 35 | 36 | ### Тестирование через [asserts](https://github.com/oscript-library/asserts) и [1connector](https://github.com/vbondarevsky/1connector) 37 | 38 | _test.os:_ 39 | ``` bsl 40 | #Использовать asserts 41 | #Использовать 1connector 42 | #Использовать httpbin 43 | 44 | Перем HttpBin; 45 | 46 | &Инициализация 47 | Процедура ПередЗапускомТестов() Экспорт 48 | HttpBin = Новый HttpBin(); 49 | HttpBin.Запустить(); 50 | КонецПроцедуры 51 | 52 | &Завершение 53 | Процедура ПослеЗапускаТестов() Экспорт 54 | HttpBin.Остановить(); 55 | КонецПроцедуры 56 | 57 | &Тест 58 | Процедура ТестДолжен_ПроверитьПараметрыЗапроса() Экспорт 59 | ПараметрыЗапроса = Новый Структура(); 60 | ПараметрыЗапроса.Вставить("key", "value"); 61 | 62 | Ответ = КоннекторHTTP.Get(HttpBin.URL() + "/get", ПараметрыЗапроса); 63 | 64 | Ожидаем.Что(Ответ.КодСостояния).Равно(200); 65 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("application/json"); 66 | Ожидаем.Что(Ответ.Json()["args"]["key"]).Равно("value"); 67 | КонецПроцедуры 68 | ``` 69 | 70 | ### Swagger UI 71 | 72 | На стартовой странице сервиса (адрес по умолчанию: `http://127.0.0.1:3334`) доступна визуальная документация API, а также возможность отправки запросов и получения ответов. 73 | 74 | ## Совместимость 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
WindowsLinuxMacOS
OneScript 1.9OneScript 2.0OneScript 1.9OneScript 2.0OneScript 1.9OneScript 2.0
103 | 104 | ## Программный интерфейс 105 | 106 | ### Класс `HttpBin` 107 | 108 | Сервис по умолчанию запускается по адресу `127.0.0.1:3334` в фоновом режиме и с ожиданием завершения запуска сервиса.
109 | Класс реализован с текучим интерфейсом. 110 | 111 | | Метод | Описание | 112 | | --- | --- | 113 | | `Запустить()` | Запускает сервис | 114 | | `Остановить()` | Останавливает сервис | 115 | | `URL()` | URL сервиса | 116 | | `Хост()` | Хост сервиса | 117 | | `Порт()` | Порт сервиса | 118 | | `УстановитьХост(<Хост>)` | Устанавливает хост сервиса | 119 | | `УстановитьПорт(<Порт>)` | Устанавливает порт сервиса | 120 | | `ЗапускатьВФоне(<Флаг>)` | Запуск сервиса будет выполнен в фоновом режиме | 121 | | `ОжидатьЗапуск(<Флаг>)` | Ожидать завершение запуска сервиса, запущенного в фоновом режиме | 122 | | `УстановитьТаймаутОжидания(<Таймаут>)` | Устанавливает таймаут ожидания запуска сервиса, запущенного в фоновом режиме | 123 | 124 | ## Ограничения 125 | 126 | На данный момент нет поддержки https. 127 | 128 | ## Сравнение с httpbin.org 129 | 130 | | Эндпоинт | oscript-httpbin | httpbin.org | 131 | | --- | :-: | :-: | 132 | | `/ip` | ✅ | ✅ | 133 | | `/uuid` | ✅ | ✅ | 134 | | `/uuid/:n` | ✅ | ❌ | 135 | | `/user-agent` | ✅ | ✅ | 136 | | `/headers` | ✅ | ✅ | 137 | | `/get` | ✅ | ✅ | 138 | | `/post` | ✅ | ✅ | 139 | | `/put` | ✅ | ✅ | 140 | | `/patch` | ✅ | ✅ | 141 | | `/delete` | ✅ | ✅ | 142 | | `/anything` | ✅ | ✅ | 143 | | `/anything/:anything` | ✅ | ✅ | 144 | | `/base64/:value` | ✅ | ✅ | 145 | | `/encoding/utf8` | ✅ | ✅ | 146 | | `/gzip` | ✅ | ✅ | 147 | | `/deflate` | ✅ | ✅ | 148 | | `/brotli` | ✅ | ✅ | 149 | | `/zstd` | ✅ | ❌ | 150 | | `/status/:code` | ✅ | ✅ | 151 | | `/response-headers?key=val` | ✅ | ✅ | 152 | | `/redirect/:n` | ✅ | ✅ | 153 | | `/redirect-to?url=foo` | ✅ | ✅ | 154 | | `/redirect-to?url=foo&status_code=307` | ✅ | ✅ | 155 | | `/relative-redirect/:n` | ✅ | ✅ | 156 | | `/absolute-redirect/:n` | ✅ | ✅ | 157 | | `/cookies` | ✅ | ✅ | 158 | | `/cookies/set?name=value` | ✅ | ✅ | 159 | | `/cookies/set/:name/:value` | ✅ | ✅ | 160 | | `/cookies/delete?name` | ✅ | ✅ | 161 | | `/basic-auth/:user/:passwd` | ✅ | ✅ | 162 | | `/hidden-basic-auth/:user/:passwd` | ✅ | ✅ | 163 | | `/digest-auth/:qop/:user/:passwd/:algorithm` | ❌ | ✅ | 164 | | `/digest-auth/:qop/:user/:passwd` | ❌ | ✅ | 165 | | `/bearer` | ✅ | ✅ | 166 | | `/stream/:n` | ❌ | ✅ | 167 | | `/delay/:n` | ✅ | ✅ | 168 | | `/drip?numbytes=n&duration=s&delay=s&code=code` | ❌ | ✅ | 169 | | `/range/:n?duration=s&chunk_size=code` | ❌ | ✅ | 170 | | `/html` | ✅ | ✅ | 171 | | `/robots.txt` | ✅ | ✅ | 172 | | `/deny` | ✅ | ✅ | 173 | | `/cache` | ✅ | ✅ | 174 | | `/cache/:n` | ✅ | ✅ | 175 | | `/etag/:etag` | ✅ | ✅ | 176 | | `/bytes/:n` | ✅ | ✅ | 177 | | `/stream-bytes/:n` | ❌ | ✅ | 178 | | `/links/:n` | ✅ | ✅ | 179 | | `/links/:n/:offset` | ✅ | ✅ | 180 | | `/image` | ✅ | ✅ | 181 | | `/image/png` | ✅ | ✅ | 182 | | `/image/jpeg` | ✅ | ✅ | 183 | | `/image/webp` | ✅ | ✅ | 184 | | `/image/svg` | ✅ | ✅ | 185 | | `/forms/post` | ✅ | ✅ | 186 | | `/xml` | ✅ | ✅ | 187 | | `/json` | ✅ | ✅ | 188 | -------------------------------------------------------------------------------- /appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Kestrel": { 3 | "Limits": { 4 | "MaxRequestBodySize": 2147483647 5 | } 6 | }, 7 | "FormOptions": { 8 | "ValueLengthLimit": 2147483647, 9 | "MultipartBodyLengthLimit": 2147483647, 10 | "MultipartHeadersLengthLimit ": 2147483647 11 | } 12 | } -------------------------------------------------------------------------------- /autumn-properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "winow": { 3 | "КаталогСПриложениями": "./src/app", 4 | "КаталогиСФайлами": { 5 | "/static": "./src/app/view/static" 6 | } 7 | }, 8 | "cli": { 9 | "ИмяПриложения": "httpbin", 10 | "ПолноеИмяПриложения": "Cервис тестирования HTTP клиента", 11 | "ВерсияПриложения": "1.3.0" 12 | } 13 | } -------------------------------------------------------------------------------- /lib.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /packagedef: -------------------------------------------------------------------------------- 1 | Описание.Имя("httpbin") 2 | .Версия("1.3.0") 3 | .Автор("Dmitry Ivanov") 4 | .АдресАвтора("https://github.com/Stivo182") 5 | .Описание("Сервис тестирования HTTP клиента") 6 | .ВерсияСреды("1.9.2") 7 | .ВключитьФайл("src") 8 | .ВключитьФайл("tests") 9 | .ВключитьФайл("packagedef") 10 | .ВключитьФайл("lib.config") 11 | .ВключитьФайл("autumn-properties.json") 12 | .ВключитьФайл("README.md") 13 | .ВключитьФайл("LICENSE") 14 | .ЗависитОт("winow", "0.9.4") 15 | .ЗависитОт("autumn-cli", "1.1.0") 16 | .ЗависитОт("compressor") 17 | .ЗависитОт("1connector") 18 | .РазработкаЗависитОт("1testrunner") 19 | .РазработкаЗависитОт("asserts") 20 | .РазработкаЗависитОт("coverage") 21 | .РазработкаЗависитОт("1commands") 22 | .ИсполняемыйФайл("src/cmd/main.os", "httpbin") -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=httpbin 2 | sonar.projectName=httpbin 3 | sonar.links.homepage=https://github.com/stivo182/oscript-httpbin 4 | sonar.links.scm=https://github.com/stivo182/oscript-httpbin 5 | sonar.links.issue=https://github.com/stivo182/oscript-httpbin/issues 6 | sonar.sources=./src 7 | sonar.tests=./tests 8 | sonar.sourceEncoding=UTF-8 9 | sonar.coverageReportPaths=out/genericCoverage.xml 10 | -------------------------------------------------------------------------------- /src/app/HttpBin_ОсновнойКонтрол.os: -------------------------------------------------------------------------------- 1 | #Использовать "../internal" 2 | #Использовать 1connector 3 | 4 | Перем Помощник; // см. ПомощникПодготовкиОтветов 5 | Перем КаталогШаблонов; // Строка 6 | 7 | #Область ТочкиМаршрута 8 | 9 | &Отображение("./src/app/view/index.html") 10 | &ТочкаМаршрута("/") 11 | &GET 12 | Процедура Главная(Ответ) Экспорт 13 | 14 | КонецПроцедуры 15 | 16 | &Отображение("./src/app/view/moby.html") 17 | &ТочкаМаршрута("html") 18 | &GET 19 | Процедура ТочкаHtml(Ответ) Экспорт 20 | 21 | КонецПроцедуры 22 | 23 | &ТочкаМаршрута("robots.txt") 24 | &GET 25 | Процедура ТочкаRobots(Ответ) Экспорт 26 | 27 | Ответ.ТелоТекст = Помощник.ТекстRobots; 28 | 29 | КонецПроцедуры 30 | 31 | &ТочкаМаршрута("deny") 32 | &GET 33 | Процедура ТочкаDeny(Ответ) Экспорт 34 | 35 | Ответ.ТелоТекст = Помощник.ASCII_Deny; 36 | Ответ.Заголовки.Удалить("Content-Type"); 37 | 38 | КонецПроцедуры 39 | 40 | &ТочкаМаршрута("ip") 41 | &GET 42 | Процедура ТочкаIP(Запрос, Ответ) Экспорт 43 | 44 | Данные = Помощник.ПолучитьДанныеЗапроса("origin", Запрос); 45 | Помощник.ЗаполнитьОтветJson(Ответ, Данные); 46 | 47 | КонецПроцедуры 48 | 49 | &ТочкаМаршрута("uuid") 50 | &GET 51 | Процедура ТочкаUuid(Ответ) Экспорт 52 | 53 | Данные = Новый Структура("uuid", Строка(Новый УникальныйИдентификатор())); 54 | 55 | Помощник.ЗаполнитьОтветJson(Ответ, Данные); 56 | 57 | КонецПроцедуры 58 | 59 | &ТочкаМаршрута("uuid/{Количество}") 60 | &GET 61 | Процедура ТочкаUuidN(Ответ, Знач Количество) Экспорт 62 | 63 | Количество = Помощник.ВЧисло(Количество); 64 | 65 | Массив = Новый Массив(); 66 | Пока Количество > 0 Цикл 67 | Количество = Количество - 1; 68 | Массив.Добавить(Строка(Новый УникальныйИдентификатор())); 69 | КонецЦикла; 70 | 71 | Данные = Новый Структура("uuid", Массив); 72 | 73 | Помощник.ЗаполнитьОтветJson(Ответ, Данные); 74 | 75 | КонецПроцедуры 76 | 77 | &ТочкаМаршрута("headers") 78 | &GET 79 | Процедура ТочкаHeaders(Запрос, Ответ) Экспорт 80 | 81 | Данные = Помощник.ПолучитьДанныеЗапроса("headers", Запрос); 82 | Помощник.ЗаполнитьОтветJson(Ответ, Данные); 83 | 84 | КонецПроцедуры 85 | 86 | &ТочкаМаршрута("user-agent") 87 | &GET 88 | Процедура ТочкаUserAgent(Запрос, Ответ) Экспорт 89 | 90 | UserAgent = Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "User-Agent"); 91 | 92 | Результат = Новый Соответствие(); 93 | Результат.Вставить("user-agent", UserAgent); 94 | 95 | Помощник.ЗаполнитьОтветJson(Ответ, Результат); 96 | 97 | КонецПроцедуры 98 | 99 | &ТочкаМаршрута("get") 100 | &GET 101 | Процедура ТочкаGet(Запрос, Ответ) Экспорт 102 | ПередатьДанныеВОтветJsonGet(Запрос, Ответ); 103 | КонецПроцедуры 104 | 105 | &ТочкаМаршрута("anything") 106 | Процедура ТочкаAnything(Запрос, ДанныеФормы, Ответ) Экспорт 107 | 108 | Данные = Помощник.ПолучитьДанныеЗапроса("args, data, files, form, headers, json, method, origin, url", 109 | Запрос, 110 | ДанныеФормы); 111 | 112 | Помощник.ЗаполнитьОтветJson(Ответ, Данные); 113 | 114 | КонецПроцедуры 115 | 116 | &ТочкаМаршрута("post") 117 | &POST 118 | Процедура ТочкаPost(Запрос, ДанныеФормы, Ответ) Экспорт 119 | ПередатьПолныеДанныеВОтветJson(Запрос, ДанныеФормы, Ответ); 120 | КонецПроцедуры 121 | 122 | &ТочкаМаршрута("put") 123 | &PUT 124 | Процедура ТочкаPut(Запрос, ДанныеФормы, Ответ) Экспорт 125 | ПередатьПолныеДанныеВОтветJson(Запрос, ДанныеФормы, Ответ); 126 | КонецПроцедуры 127 | 128 | &ТочкаМаршрута("patch") 129 | &PATCH 130 | Процедура ТочкаPatch(Запрос, ДанныеФормы, Ответ) Экспорт 131 | ПередатьПолныеДанныеВОтветJson(Запрос, ДанныеФормы, Ответ); 132 | КонецПроцедуры 133 | 134 | &ТочкаМаршрута("delete") 135 | &DELETE 136 | Процедура ТочкаDelete(Запрос, ДанныеФормы, Ответ) Экспорт 137 | ПередатьПолныеДанныеВОтветJson(Запрос, ДанныеФормы, Ответ); 138 | КонецПроцедуры 139 | 140 | &ТочкаМаршрута("redirect/{Количество}") 141 | &GET 142 | Процедура ТочкаRedirect(ПараметрыЗапросаИменные, Ответ, Количество) Экспорт 143 | 144 | ПередаватьАбсолютныйПуть = НРег(ПараметрыЗапросаИменные.Получить("absolute")) = "true"; 145 | 146 | Redirect(Количество, ПередаватьАбсолютныйПуть, Ответ); 147 | 148 | КонецПроцедуры 149 | 150 | &ТочкаМаршрута("redirect-to") 151 | Процедура ТочкаRedirectTo(Запрос, ДанныеФормы, Ответ) Экспорт 152 | 153 | Данные = Помощник.ПолучитьДанныеЗапроса("args, form", Запрос, ДанныеФормы); 154 | 155 | Если Запрос.Метод = "GET" Тогда 156 | URL = Данные["args"].Получить("url"); 157 | КодСостояния = Помощник.ВЧисло(Данные["args"].Получить("status_code")); 158 | Иначе 159 | URL = Данные["form"].Получить("url"); 160 | КодСостояния = Помощник.ВЧисло(Данные["form"].Получить("status_code")); 161 | КонецЕсли; 162 | 163 | Если КодСостояния >= КодыСостоянияHTTP.МножествоВыборов_300 164 | И КодСостояния <= КодыСостоянияHTTP.ПостоянноеПеренаправление_308 Тогда 165 | Ответ.УстановитьСостояние(КодСостояния); 166 | Иначе 167 | Ответ.УстановитьСостояние(КодыСостоянияHTTP.ПеремещеноВременно_302); 168 | КонецЕсли; 169 | 170 | Ответ.Заголовки["Location"] = URL; 171 | 172 | КонецПроцедуры 173 | 174 | &ТочкаМаршрута("relative-redirect/{Количество}") 175 | &GET 176 | Процедура ТочкаRelativeRedirect(Ответ, Количество) Экспорт 177 | 178 | Redirect(Количество, Ложь, Ответ); 179 | 180 | КонецПроцедуры 181 | 182 | &ТочкаМаршрута("absolute-redirect/{Количество}") 183 | &GET 184 | Процедура ТочкаAbsoluteRedirect(Ответ, Количество) Экспорт 185 | 186 | Redirect(Количество, Истина, Ответ); 187 | 188 | КонецПроцедуры 189 | 190 | &ТочкаМаршрута("status/{КодыСостояний}") 191 | Процедура ТочкаStatus(Ответ, КодыСостояний) Экспорт 192 | 193 | Если Не СтрНайти(КодыСостояний, ",") Тогда 194 | 195 | РазделеннаяСтрока = Помощник.РазделитьСтроку(КодыСостояний, ":"); 196 | КодСостояния = Помощник.ВЧисло(РазделеннаяСтрока.Лево); 197 | 198 | Если КодСостояния = 0 Тогда 199 | Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеверныйЗапрос_400); 200 | Ответ.ТелоТекст = "Invalid status code"; 201 | Возврат; 202 | КонецЕсли; 203 | 204 | Иначе 205 | 206 | ВзвешенныйСписок = Новый Массив(); 207 | Для каждого Строка Из СтрРазделить(КодыСостояний, ",") Цикл 208 | 209 | РазделеннаяСтрока = Помощник.РазделитьСтроку(Строка, ":"); 210 | 211 | КодСостояния = Помощник.ВЧисло(РазделеннаяСтрока.Лево); 212 | Если КодСостояния = 0 Тогда 213 | Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеверныйЗапрос_400); 214 | Ответ.ТелоТекст = "Invalid status code"; 215 | Возврат; 216 | КонецЕсли; 217 | 218 | Если ПустаяСтрока(РазделеннаяСтрока.Право) Тогда 219 | Вес = 1; 220 | Иначе 221 | Вес = Помощник.ВЧисло(РазделеннаяСтрока.Право); 222 | КонецЕсли; 223 | 224 | ВзвешенныйСписок.Добавить(Новый Структура("Значение, Вес", КодСостояния, Вес)); 225 | КонецЦикла; 226 | 227 | КодСостояния = Помощник.ВыбратьСлучайныйЭлементСУчетомВеса(ВзвешенныйСписок); 228 | 229 | КонецЕсли; 230 | 231 | Помощник.ЗаполнитьОтветПоСостоянию(Ответ, КодСостояния); 232 | 233 | КонецПроцедуры 234 | 235 | &ТочкаМаршрута("response-headers") 236 | &GET 237 | &POST 238 | Процедура ТочкаResponseHeaders(Запрос, Ответ) Экспорт 239 | 240 | Ответ.УстановитьТипКонтента("json"); 241 | 242 | Данные = Новый Соответствие(); 243 | Помощник.ДополнитьСоответствие(Данные, Ответ.Заголовки); 244 | Помощник.ДополнитьСоответствие(Данные, Запрос.ПараметрыИменные); 245 | 246 | Помощник.ЗаполнитьОтветJson(Ответ, Данные); 247 | 248 | КонецПроцедуры 249 | 250 | &ТочкаМаршрута("cookies") 251 | &GET 252 | Процедура ТочкаCookies(КукиЗапроса, Ответ) Экспорт 253 | 254 | Куки = Новый Соответствие(); 255 | Для Каждого Элемент Из КукиЗапроса.Получить() Цикл 256 | Куки.Вставить(Элемент.Ключ, Элемент.Значение.Значение); 257 | КонецЦикла; 258 | Куки.Удалить("SessionID"); 259 | 260 | Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("cookies", Куки)); 261 | 262 | КонецПроцедуры 263 | 264 | &ТочкаМаршрута("cookies/set/{Имя}/{Значение}") 265 | &GET 266 | Процедура ТочкаSetCookie(Ответ, Имя, Значение) Экспорт 267 | 268 | Ответ.Куки.Добавить(Имя, Значение); 269 | Ответ.Перенаправить("/cookies"); 270 | 271 | КонецПроцедуры 272 | 273 | &ТочкаМаршрута("cookies/set") 274 | &GET 275 | Процедура ТочкаSetCookies(ПараметрыЗапросаИменные, Ответ) Экспорт 276 | 277 | Для Каждого Параметр Из ПараметрыЗапросаИменные Цикл 278 | Ответ.Куки.Добавить(Параметр.Ключ, Параметр.Значение); 279 | КонецЦикла; 280 | 281 | Ответ.Перенаправить("/cookies"); 282 | 283 | КонецПроцедуры 284 | 285 | &ТочкаМаршрута("cookies/delete") 286 | &GET 287 | Процедура ТочкаDeleteCookies(ПараметрыЗапросаИменные, Ответ) Экспорт 288 | 289 | Для Каждого Параметр Из ПараметрыЗапросаИменные Цикл 290 | Кука = Ответ.Куки.Добавить(Параметр.Ключ, ""); 291 | Кука.ДатаИстечения = Дата(1970, 1, 1); 292 | КонецЦикла; 293 | 294 | Ответ.Перенаправить("/cookies"); 295 | 296 | КонецПроцедуры 297 | 298 | &Отображение("./src/app/view/forms-post.html") 299 | &ТочкаМаршрута("forms/post") 300 | &GET 301 | Процедура ТочкаFormsPost(Ответ) Экспорт 302 | 303 | Ответ.Модель = Новый Структура(); 304 | Ответ.Модель.Вставить("Адрес", "/post"); 305 | 306 | КонецПроцедуры 307 | 308 | &ТочкаМаршрута("basic-auth/{ИмяПользователя}/{Пароль}") 309 | &GET 310 | Процедура ТочкаBasicAuth(Запрос, Ответ, ИмяПользователя, Пароль) Экспорт 311 | 312 | ДанныеАутентификации = Помощник.ДанныеАутентификации(Запрос); 313 | 314 | Если ДанныеАутентификации.Тип = "basic" 315 | И ДанныеАутентификации.ИмяПользователя = ИмяПользователя 316 | И ДанныеАутентификации.Пароль = Пароль Тогда 317 | Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("authenticated, user", Истина, ИмяПользователя)); 318 | Иначе 319 | Помощник.ЗаполнитьОтветПоСостоянию(Ответ, КодыСостоянияHTTP.НеАвторизован_401); 320 | КонецЕсли; 321 | 322 | КонецПроцедуры 323 | 324 | &ТочкаМаршрута("hidden-basic-auth/{ИмяПользователя}/{Пароль}") 325 | &GET 326 | Процедура ТочкаHiddenBasicAuth(Запрос, Ответ, ИмяПользователя, Пароль) Экспорт 327 | 328 | ДанныеАутентификации = Помощник.ДанныеАутентификации(Запрос); 329 | 330 | Если ДанныеАутентификации.Тип = "basic" 331 | И ДанныеАутентификации.ИмяПользователя = ИмяПользователя 332 | И ДанныеАутентификации.Пароль = Пароль Тогда 333 | Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("authenticated, user", Истина, ИмяПользователя)); 334 | Иначе 335 | Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеНайдено_404); 336 | КонецЕсли; 337 | 338 | КонецПроцедуры 339 | 340 | &ТочкаМаршрута("bearer") 341 | &GET 342 | Процедура ТочкаBearer(Запрос, Ответ) Экспорт 343 | 344 | ДанныеАутентификации = Помощник.ДанныеАутентификации(Запрос); 345 | 346 | Если ДанныеАутентификации.Тип = "bearer" Тогда 347 | Помощник.ЗаполнитьОтветJson(Ответ, Новый Структура("authenticated, token", Истина, ДанныеАутентификации.Токен)); 348 | Иначе 349 | Ответ.Заголовки["WWW-Authenticate"] = "Bearer"; 350 | Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеАвторизован_401); 351 | КонецЕсли; 352 | 353 | КонецПроцедуры 354 | 355 | &ТочкаМаршрута("delay/{Секунд}") 356 | Процедура ТочкаDelay(Запрос, ДанныеФормы, Ответ, Секунд) Экспорт 357 | 358 | Секунд = Помощник.ВЧисло(Секунд); 359 | Приостановить(Секунд * 1000); 360 | 361 | ПередатьПолныеДанныеВОтветJson(Запрос, ДанныеФормы, Ответ); 362 | 363 | КонецПроцедуры 364 | 365 | &ТочкаМаршрута("base64/{Строка}") 366 | &GET 367 | Процедура ТочкаBase64(Ответ, Строка) Экспорт 368 | 369 | Попытка 370 | Ответ.ТелоТекст = ПолучитьСтрокуИзДвоичныхДанных(Base64Значение(Строка)); 371 | Исключение 372 | Ответ.ТелоТекст = "Incorrect Base64 data try: SFRUUEJJTiBpcyBhd2Vzb21l"; 373 | КонецПопытки; 374 | 375 | КонецПроцедуры 376 | 377 | &ТочкаМаршрута("cache") 378 | &GET 379 | Процедура ТочкаCache(Запрос, Ответ) Экспорт 380 | 381 | ЕстьЗаголовки = Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-Modified-Since") <> Неопределено 382 | Или Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-None-Match") <> Неопределено; 383 | 384 | Если Не ЕстьЗаголовки Тогда 385 | Ответ.Заголовки["Last-Modified"] = Помощник.ДатаHTTP(); 386 | Ответ.Заголовки["ETag"] = СтрЗаменить(Новый УникальныйИдентификатор(), "-", ""); 387 | 388 | ПередатьДанныеВОтветJsonGet(Запрос, Ответ); 389 | Иначе 390 | Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеИзменялось_304); 391 | КонецЕсли; 392 | 393 | КонецПроцедуры 394 | 395 | &ТочкаМаршрута("cache/{Секунд}") 396 | &GET 397 | Процедура ТочкаУстановитьCacheControl(Запрос, Ответ, Секунд) Экспорт 398 | 399 | Секунд = Помощник.ВЧисло(Секунд); 400 | 401 | Ответ.Заголовки["Cache-Control"] = СтрШаблон("public, max-age=%1", Формат(Секунд, "ЧГ=")); 402 | 403 | ПередатьДанныеВОтветJsonGet(Запрос, Ответ); 404 | 405 | КонецПроцедуры 406 | 407 | &ТочкаМаршрута("etag/{ETag}") 408 | &GET 409 | Процедура ТочкаETag(Запрос, Ответ, ETag) Экспорт 410 | 411 | ЗаголовокIfNoneMatch = Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-None-Match"); 412 | ЗаголовокIfMatch = Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "If-Match"); 413 | 414 | ЗначенияIfNoneMatch = Помощник.РаспаристьМногозначныйЗаголовок(ЗаголовокIfNoneMatch); 415 | ЗначенияIfMatch = Помощник.РаспаристьМногозначныйЗаголовок(ЗаголовокIfMatch); 416 | 417 | Если ЗначениеЗаполнено(ЗначенияIfNoneMatch) Тогда 418 | Если ЗначенияIfNoneMatch.Найти(ETag) <> Неопределено Или ЗначенияIfNoneMatch.Найти("*") <> Неопределено Тогда 419 | Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеИзменялось_304); 420 | Ответ.Заголовки["ETag"] = ETag; 421 | Возврат; 422 | КонецЕсли; 423 | ИначеЕсли ЗначениеЗаполнено(ЗначенияIfMatch) Тогда 424 | Если ЗначенияIfMatch.Найти(ETag) = Неопределено И ЗначенияIfMatch.Найти("*") = Неопределено Тогда 425 | Ответ.УстановитьСостояние(КодыСостоянияHTTP.УсловиеЛожно_412); 426 | Возврат; 427 | КонецЕсли; 428 | КонецЕсли; 429 | 430 | Ответ.Заголовки["ETag"] = ETag; 431 | 432 | ПередатьДанныеВОтветJsonGet(Запрос, Ответ); 433 | 434 | КонецПроцедуры 435 | 436 | &Отображение("./src/app/view/UTF-8-demo.txt") 437 | &ТочкаМаршрута("encoding/utf8") 438 | &GET 439 | Процедура ТочкаEncodingUTF8(Запрос, Ответ) Экспорт 440 | 441 | КонецПроцедуры 442 | 443 | &ТочкаМаршрута("bytes/{КоличествоБайт}") 444 | &GET 445 | Процедура ТочкаBytes(ПараметрыЗапросаИменные, Ответ, КоличествоБайт) Экспорт 446 | 447 | КоличествоБайт = Помощник.ВЧисло(КоличествоБайт); 448 | 449 | НачальноеЧисло = ПараметрыЗапросаИменные.Получить("seed"); 450 | Если Не НачальноеЧисло = Неопределено Тогда 451 | НачальноеЧисло = Помощник.ВЧисло(НачальноеЧисло); 452 | ГенераторСлучайныхЧисел = Новый ГенераторСлучайныхЧисел(НачальноеЧисло); 453 | Иначе 454 | ГенераторСлучайныхЧисел = Новый ГенераторСлучайныхЧисел(); 455 | КонецЕсли; 456 | 457 | ПотокДанных = Новый ПотокВПамяти(); 458 | ЗаписьДанных = Новый ЗаписьДанных(ПотокДанных); 459 | 460 | Для Сч = 1 По КоличествоБайт Цикл 461 | Число = ГенераторСлучайныхЧисел.СлучайноеЧисло(0, 255); 462 | ЗаписьДанных.ЗаписатьБайт(Число); 463 | КонецЦикла; 464 | 465 | ЗаписьДанных.Закрыть(); 466 | 467 | Ответ.Заголовки.Вставить("Content-Type", "application/octet-stream"); 468 | Ответ.ТелоДвоичныеДанные = ПотокДанных.ЗакрытьИПолучитьДвоичныеДанные(); 469 | 470 | КонецПроцедуры 471 | 472 | &ТочкаМаршрута("links/{Количество}/{Смещение}") 473 | &GET 474 | Процедура ТочкаLinks(Ответ, Количество, Смещение) Экспорт 475 | 476 | Количество = Помощник.ВЧисло(Количество); 477 | Смещение = Помощник.ВЧисло(Смещение); 478 | 479 | Html = Новый Массив(); 480 | Html.Добавить("Links"); 481 | 482 | ШаблонСсылки = "%2 "; 483 | ФорматЧисла = "ЧГ="; 484 | 485 | Для Сч = 1 По Количество Цикл 486 | Если Сч = Смещение Тогда 487 | Html.Добавить(Формат(Сч, ФорматЧисла) + " "); 488 | Иначе 489 | Ссылка = СтрШаблон(ШаблонСсылки, 490 | Формат(Количество, ФорматЧисла), 491 | Формат(Сч, ФорматЧисла)); 492 | 493 | Html.Добавить(Ссылка); 494 | КонецЕсли; 495 | КонецЦикла; 496 | 497 | Html.Добавить(""); 498 | 499 | Ответ.ТелоТекст = СтрСоединить(Html, ""); 500 | 501 | КонецПроцедуры 502 | 503 | &ТочкаМаршрута("links/{Количество}") 504 | &GET 505 | Процедура ТочкаLinksRedirect(Ответ, Количество) Экспорт 506 | 507 | Количество = Помощник.ВЧисло(Количество); 508 | 509 | URL = СтрШаблон("/links/%1/0", Формат(Количество, "ЧГ=")); 510 | Ответ.Перенаправить(URL); 511 | 512 | КонецПроцедуры 513 | 514 | &ТочкаМаршрута("image") 515 | &GET 516 | Процедура ТочкаImage(Запрос, Ответ) Экспорт 517 | 518 | Accept = Помощник.ЗначениеЗаголовка(Запрос.Заголовки, "Accept"); 519 | 520 | Если Accept = "image/webp" Тогда 521 | ТочкаImageWebp(Ответ); 522 | ИначеЕсли Accept = "image/svg+xml" Тогда 523 | ТочкаImageSvg(Ответ); 524 | ИначеЕсли Accept = "image/jpeg" Тогда 525 | ТочкаImageJpeg(Ответ); 526 | ИначеЕсли Accept = "image/png" 527 | Или Accept = "image/*" 528 | Или Accept = "*/*" Тогда 529 | ТочкаImagePng(Ответ); 530 | Иначе 531 | Помощник.ЗаполнитьОтветПоСостоянию(Ответ, КодыСостоянияHTTP.Неприемлемо_406); 532 | КонецЕсли; 533 | 534 | КонецПроцедуры 535 | 536 | &ТочкаМаршрута("image/png") 537 | &GET 538 | Процедура ТочкаImagePng(Ответ) Экспорт 539 | ОтдатьФайл("images/pig_icon.png", "image/png", Ответ); 540 | КонецПроцедуры 541 | 542 | &ТочкаМаршрута("image/jpeg") 543 | &GET 544 | Процедура ТочкаImageJpeg(Ответ) Экспорт 545 | ОтдатьФайл("images/jackal.jpg", "image/jpeg", Ответ); 546 | КонецПроцедуры 547 | 548 | &ТочкаМаршрута("image/webp") 549 | &GET 550 | Процедура ТочкаImageWebp(Ответ) Экспорт 551 | ОтдатьФайл("images/wolf_1.webp", "image/webp", Ответ); 552 | КонецПроцедуры 553 | 554 | &ТочкаМаршрута("image/svg") 555 | &GET 556 | Процедура ТочкаImageSvg(Ответ) Экспорт 557 | ОтдатьФайл("images/svg_logo.svg", "image/svg+xml", Ответ); 558 | КонецПроцедуры 559 | 560 | &Отображение("./src/app/view/sample.xml") 561 | &ТочкаМаршрута("xml") 562 | &GET 563 | Процедура ТочкаXML(Ответ) Экспорт 564 | Ответ.Заголовки["Content-Type"] = "application/xml"; 565 | КонецПроцедуры 566 | 567 | &ТочкаМаршрута("json") 568 | &GET 569 | Процедура ТочкаJson(Ответ) Экспорт 570 | 571 | Ответ.УстановитьТипКонтента("json"); 572 | Ответ.ТелоТекст = "{ 573 | | ""title"": ""Sample Slide Show"", 574 | | ""date"": ""date of publication"", 575 | | ""author"": ""Yours Truly"", 576 | | ""slides"": [ 577 | | { 578 | | ""type"": ""all"", ""title"": ""Wake up to WonderWidgets!"" 579 | | }, 580 | | { 581 | | ""type"": ""all"", 582 | | ""title"": ""Overview"", 583 | | ""items"": [ 584 | | ""Why WonderWidgets are great"", 585 | | ""Who buys WonderWidgets"" 586 | | ] 587 | | } 588 | | ] 589 | |}"; 590 | 591 | КонецПроцедуры 592 | 593 | &ТочкаМаршрута("gzip") 594 | &GET 595 | Процедура ТочкаGZip(Запрос, Ответ) Экспорт 596 | Данные = Помощник.ПолучитьДанныеЗапроса("gzipped, headers, method, origin", Запрос); 597 | Помощник.ЗаполнитьОтветJson(Ответ, Данные, "gzip"); 598 | КонецПроцедуры 599 | 600 | &ТочкаМаршрута("deflate") 601 | &GET 602 | Процедура ТочкаDeflate(Запрос, Ответ) Экспорт 603 | Данные = Помощник.ПолучитьДанныеЗапроса("deflated, headers, method, origin", Запрос); 604 | Помощник.ЗаполнитьОтветJson(Ответ, Данные, "deflate"); 605 | КонецПроцедуры 606 | 607 | &ТочкаМаршрута("brotli") 608 | &GET 609 | Процедура ТочкаBrotli(Запрос, Ответ) Экспорт 610 | Данные = Помощник.ПолучитьДанныеЗапроса("brotli, headers, method, origin", Запрос); 611 | Помощник.ЗаполнитьОтветJson(Ответ, Данные, "brotli"); 612 | КонецПроцедуры 613 | 614 | &ТочкаМаршрута("zstd") 615 | &GET 616 | Процедура ТочкаZStd(Запрос, Ответ) Экспорт 617 | Данные = Помощник.ПолучитьДанныеЗапроса("zstd, headers, method, origin", Запрос); 618 | Помощник.ЗаполнитьОтветJson(Ответ, Данные, "zstd"); 619 | КонецПроцедуры 620 | 621 | #КонецОбласти 622 | 623 | #Область СлужебныеПроцедурыИФункции 624 | 625 | &Контроллер("/") 626 | Процедура ПриСозданииОбъекта(&Пластилин("ПомощникПодготовкиОтветов") ПомощникПодготовкиОтветов) 627 | 628 | Помощник = ПомощникПодготовкиОтветов; 629 | КаталогШаблонов = "./src/app/view"; 630 | 631 | КонецПроцедуры 632 | 633 | Процедура Redirect(Количество, ПередаватьАбсолютныйПуть, Ответ) 634 | 635 | Количество = Помощник.ВЧисло(Количество); 636 | 637 | Если Количество <= 0 Тогда 638 | Ответ.УстановитьСостояние(КодыСостоянияHTTP.НеНайдено_404); 639 | Возврат; 640 | КонецЕсли; 641 | 642 | Если Количество = 1 Тогда 643 | Путь = "/get"; 644 | Иначе 645 | Путь = СтрШаблон("/%1-redirect/%2", 646 | ?(ПередаватьАбсолютныйПуть, "absolute", "relative"), 647 | Формат(Количество - 1, "ЧГ=")); 648 | КонецЕсли; 649 | 650 | Если ПередаватьАбсолютныйПуть Тогда 651 | Адрес = Помощник.ПолучитьURL(Путь); 652 | Иначе 653 | Адрес = Путь; 654 | КонецЕсли; 655 | 656 | Ответ.Перенаправить(Адрес); 657 | 658 | КонецПроцедуры 659 | 660 | Процедура ПередатьДанныеВОтветJsonGet(Запрос, Ответ) 661 | 662 | Данные = Помощник.ПолучитьДанныеЗапроса("args, headers, origin, url", Запрос); 663 | Помощник.ЗаполнитьОтветJson(Ответ, Данные); 664 | 665 | КонецПроцедуры 666 | 667 | Процедура ПередатьПолныеДанныеВОтветJson(Запрос, ДанныеФормы, Ответ) 668 | 669 | Данные = Помощник.ПолучитьДанныеЗапроса("args, data, files, form, headers, json, origin, url", 670 | Запрос, 671 | ДанныеФормы); 672 | 673 | Помощник.ЗаполнитьОтветJson(Ответ, Данные); 674 | 675 | КонецПроцедуры 676 | 677 | Процедура ОтдатьФайл(Путь, ТипКонтента, Ответ) 678 | 679 | Разделитель = ПолучитьРазделительПути(); 680 | ПутьКФайлу = КаталогШаблонов + Разделитель + Путь; 681 | 682 | Ответ.Заголовки["Content-Type"] = ТипКонтента; 683 | Ответ.ТелоДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу); 684 | 685 | КонецПроцедуры 686 | 687 | #КонецОбласти -------------------------------------------------------------------------------- /src/app/view/UTF-8-demo.txt: -------------------------------------------------------------------------------- 1 |

Unicode Demo

2 | 3 |

Taken from http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt

5 | 6 |
  7 | 
  8 | UTF-8 encoded sample plain-text file
  9 | ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
 10 | 
 11 | Markus Kuhn [ˈmaʳkʊs kuːn]  — 2002-07-25
 12 | 
 13 | 
 14 | The ASCII compatible UTF-8 encoding used in this plain-text file
 15 | is defined in Unicode, ISO 10646-1, and RFC 2279.
 16 | 
 17 | 
 18 | Using Unicode/UTF-8, you can write in emails and source code things such as
 19 | 
 20 | Mathematics and sciences:
 21 | 
 22 |   ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i),      ⎧⎡⎛┌─────┐⎞⎤⎫
 23 |                                             ⎪⎢⎜│a²+b³ ⎟⎥⎪
 24 |   ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),    ⎪⎢⎜│───── ⎟⎥⎪
 25 |                                             ⎪⎢⎜⎷ c₈   ⎟⎥⎪
 26 |   ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ,                   ⎨⎢⎜       ⎟⎥⎬
 27 |                                             ⎪⎢⎜ ∞     ⎟⎥⎪
 28 |   ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫),      ⎪⎢⎜ ⎲     ⎟⎥⎪
 29 |                                             ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪
 30 |   2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm     ⎩⎣⎝i=1    ⎠⎦⎭
 31 | 
 32 | Linguistics and dictionaries:
 33 | 
 34 |   ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
 35 |   Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]
 36 | 
 37 | APL:
 38 | 
 39 |   ((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈
 40 | 
 41 | Nicer typography in plain text files:
 42 | 
 43 |   ╔══════════════════════════════════════════╗
 44 |   ║                                          ║
 45 |   ║   • ‘single’ and “double” quotes         ║
 46 |   ║                                          ║
 47 |   ║   • Curly apostrophes: “We’ve been here” ║
 48 |   ║                                          ║
 49 |   ║   • Latin-1 apostrophe and accents: '´`  ║
 50 |   ║                                          ║
 51 |   ║   • ‚deutsche‘ „Anführungszeichen“       ║
 52 |   ║                                          ║
 53 |   ║   • †, ‡, ‰, •, 3–4, —, −5/+5, ™, …      ║
 54 |   ║                                          ║
 55 |   ║   • ASCII safety test: 1lI|, 0OD, 8B     ║
 56 |   ║                      ╭─────────╮         ║
 57 |   ║   • the euro symbol: │ 14.95 € │         ║
 58 |   ║                      ╰─────────╯         ║
 59 |   ╚══════════════════════════════════════════╝
 60 | 
 61 | Combining characters:
 62 | 
 63 |   STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑
 64 | 
 65 | Greek (in Polytonic):
 66 | 
 67 |   The Greek anthem:
 68 | 
 69 |   Σὲ γνωρίζω ἀπὸ τὴν κόψη
 70 |   τοῦ σπαθιοῦ τὴν τρομερή,
 71 |   σὲ γνωρίζω ἀπὸ τὴν ὄψη
 72 |   ποὺ μὲ βία μετράει τὴ γῆ.
 73 | 
 74 |   ᾿Απ᾿ τὰ κόκκαλα βγαλμένη
 75 |   τῶν ῾Ελλήνων τὰ ἱερά
 76 |   καὶ σὰν πρῶτα ἀνδρειωμένη
 77 |   χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!
 78 | 
 79 |   From a speech of Demosthenes in the 4th century BC:
 80 | 
 81 |   Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
 82 |   ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
 83 |   λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
 84 |   τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿
 85 |   εἰς τοῦτο προήκοντα,  ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
 86 |   πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
 87 |   οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
 88 |   οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
 89 |   ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
 90 |   τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
 91 |   γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
 92 |   προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
 93 |   σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
 94 |   τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
 95 |   τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
 96 |   τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.
 97 | 
 98 |   Δημοσθένους, Γ´ ᾿Ολυνθιακὸς
 99 | 
100 | Georgian:
101 | 
102 |   From a Unicode conference invitation:
103 | 
104 |   გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
105 |   კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
106 |   ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
107 |   ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
108 |   ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
109 |   ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
110 |   ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.
111 | 
112 | Russian:
113 | 
114 |   From a Unicode conference invitation:
115 | 
116 |   Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
117 |   Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
118 |   Конференция соберет широкий круг экспертов по  вопросам глобального
119 |   Интернета и Unicode, локализации и интернационализации, воплощению и
120 |   применению Unicode в различных операционных системах и программных
121 |   приложениях, шрифтах, верстке и многоязычных компьютерных системах.
122 | 
123 | Thai (UCS Level 2):
124 | 
125 |   Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
126 |   classic 'San Gua'):
127 | 
128 |   [----------------------------|------------------------]
129 |     ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช  พระปกเกศกองบู๊กู้ขึ้นใหม่
130 |   สิบสองกษัตริย์ก่อนหน้าแลถัดไป       สององค์ไซร้โง่เขลาเบาปัญญา
131 |     ทรงนับถือขันทีเป็นที่พึ่ง           บ้านเมืองจึงวิปริตเป็นนักหนา
132 |   โฮจิ๋นเรียกทัพทั่วหัวเมืองมา         หมายจะฆ่ามดชั่วตัวสำคัญ
133 |     เหมือนขับไสไล่เสือจากเคหา      รับหมาป่าเข้ามาเลยอาสัญ
134 |   ฝ่ายอ้องอุ้นยุแยกให้แตกกัน          ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
135 |     พลันลิฉุยกุยกีกลับก่อเหตุ          ช่างอาเพศจริงหนาฟ้าร้องไห้
136 |   ต้องรบราฆ่าฟันจนบรรลัย           ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ
137 | 
138 |   (The above is a two-column text. If combining characters are handled
139 |   correctly, the lines of the second column should be aligned with the
140 |   | character above.)
141 | 
142 | Ethiopian:
143 | 
144 |   Proverbs in the Amharic language:
145 | 
146 |   ሰማይ አይታረስ ንጉሥ አይከሰስ።
147 |   ብላ ካለኝ እንደአባቴ በቆመጠኝ።
148 |   ጌጥ ያለቤቱ ቁምጥና ነው።
149 |   ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
150 |   የአፍ ወለምታ በቅቤ አይታሽም።
151 |   አይጥ በበላ ዳዋ ተመታ።
152 |   ሲተረጉሙ ይደረግሙ።
153 |   ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
154 |   ድር ቢያብር አንበሳ ያስር።
155 |   ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
156 |   እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
157 |   የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
158 |   ሥራ ከመፍታት ልጄን ላፋታት።
159 |   ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
160 |   የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
161 |   ተንጋሎ ቢተፉ ተመልሶ ባፉ።
162 |   ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
163 |   እግርህን በፍራሽህ ልክ ዘርጋ።
164 | 
165 | Runes:
166 | 
167 |   ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ
168 | 
169 |   (Old English, which transcribed into Latin reads 'He cwaeth that he
170 |   bude thaem lande northweardum with tha Westsae.' and means 'He said
171 |   that he lived in the northern land near the Western Sea.')
172 | 
173 | Braille:
174 | 
175 |   ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌
176 | 
177 |   ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
178 |   ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
179 |   ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
180 |   ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
181 |   ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑
182 |   ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲
183 | 
184 |   ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
185 | 
186 |   ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
187 |   ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
188 |   ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
189 |   ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹
190 |   ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎
191 |   ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
192 |   ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
193 |   ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
194 |   ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
195 | 
196 |   (The first couple of paragraphs of "A Christmas Carol" by Dickens)
197 | 
198 | Compact font selection example text:
199 | 
200 |   ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
201 |   abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
202 |   –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
203 |   ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა
204 | 
205 | Greetings in various languages:
206 | 
207 |   Hello world, Καλημέρα κόσμε, コンニチハ
208 | 
209 | Box drawing alignment tests:                                          █
210 |                                                                       ▉
211 |   ╔══╦══╗  ┌──┬──┐  ╭──┬──╮  ╭──┬──╮  ┏━━┳━━┓  ┎┒┏┑   ╷  ╻ ┏┯┓ ┌┰┐    ▊ ╱╲╱╲╳╳╳
212 |   ║┌─╨─┐║  │╔═╧═╗│  │╒═╪═╕│  │╓─╁─╖│  ┃┌─╂─┐┃  ┗╃╄┙  ╶┼╴╺╋╸┠┼┨ ┝╋┥    ▋ ╲╱╲╱╳╳╳
213 |   ║│╲ ╱│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╿ │┃  ┍╅╆┓   ╵  ╹ ┗┷┛ └┸┘    ▌ ╱╲╱╲╳╳╳
214 |   ╠╡ ╳ ╞╣  ├╢   ╟┤  ├┼─┼─┼┤  ├╫─╂─╫┤  ┣┿╾┼╼┿┫  ┕┛┖┚     ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
215 |   ║│╱ ╲│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╽ │┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▎
216 |   ║└─╥─┘║  │╚═╤═╝│  │╘═╪═╛│  │╙─╀─╜│  ┃└─╂─┘┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▏
217 |   ╚══╩══╝  └──┴──┘  ╰──┴──╯  ╰──┴──╯  ┗━━┻━━┛  ▗▄▖▛▀▜   └╌╌┘ ╎ ┗╍╍┛ ┋  ▁▂▃▄▅▆▇█
218 |                                                ▝▀▘▙▄▟
219 | 
220 | 
221 | -------------------------------------------------------------------------------- /src/app/view/forms-post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |

9 |

10 |

11 |
12 | Pizza Size 13 |

14 |

15 |

16 |
17 |
18 | Pizza Toppings 19 |

20 |

21 |

22 |

23 |
24 |

25 |

26 |

27 |
28 | 29 | -------------------------------------------------------------------------------- /src/app/view/images/jackal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stivo182/oscript-httpbin/6ac72a32049488592c89f3ef1a5365184cc045f2/src/app/view/images/jackal.jpg -------------------------------------------------------------------------------- /src/app/view/images/pig_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stivo182/oscript-httpbin/6ac72a32049488592c89f3ef1a5365184cc045f2/src/app/view/images/pig_icon.png -------------------------------------------------------------------------------- /src/app/view/images/svg_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | SVG Logo 4 | 5 | 7 | 8 | 15 | 16 | 23 | 24 | 33 | 34 | 35 | 40 | 41 | 46 | 47 | 52 | 53 | 58 | 59 | 64 | 65 | 70 | 71 | 76 | 77 | 82 | 83 | 84 | 85 | 104 | 105 | 157 | 158 | 159 | 160 | 169 | 170 | 181 | 182 | 183 | SVG 184 | 215 | 227 | 228 | 256 | 257 | 258 | 259 | 260 | -------------------------------------------------------------------------------- /src/app/view/images/wolf_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stivo182/oscript-httpbin/6ac72a32049488592c89f3ef1a5365184cc045f2/src/app/view/images/wolf_1.webp -------------------------------------------------------------------------------- /src/app/view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | oscript-httpbin 6 | 7 | 8 | 11 | 12 | 13 |
14 | 15 | 16 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/app/view/moby.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Herman Melville - Moby-Dick

7 | 8 |
9 |

10 | Availing himself of the mild, summer-cool weather that now reigned in these latitudes, and in preparation for the peculiarly active pursuits shortly to be anticipated, Perth, the begrimed, blistered old blacksmith, had not removed his portable forge to the hold again, after concluding his contributory work for Ahab's leg, but still retained it on deck, fast lashed to ringbolts by the foremast; being now almost incessantly invoked by the headsmen, and harpooneers, and bowsmen to do some little job for them; altering, or repairing, or new shaping their various weapons and boat furniture. Often he would be surrounded by an eager circle, all waiting to be served; holding boat-spades, pike-heads, harpoons, and lances, and jealously watching his every sooty movement, as he toiled. Nevertheless, this old man's was a patient hammer wielded by a patient arm. No murmur, no impatience, no petulance did come from him. Silent, slow, and solemn; bowing over still further his chronically broken back, he toiled away, as if toil were life itself, and the heavy beating of his hammer the heavy beating of his heart. And so it was.—Most miserable! A peculiar walk in this old man, a certain slight but painful appearing yawing in his gait, had at an early period of the voyage excited the curiosity of the mariners. And to the importunity of their persisted questionings he had finally given in; and so it came to pass that every one now knew the shameful story of his wretched fate. Belated, and not innocently, one bitter winter's midnight, on the road running between two country towns, the blacksmith half-stupidly felt the deadly numbness stealing over him, and sought refuge in a leaning, dilapidated barn. The issue was, the loss of the extremities of both feet. Out of this revelation, part by part, at last came out the four acts of the gladness, and the one long, and as yet uncatastrophied fifth act of the grief of his life's drama. He was an old man, who, at the age of nearly sixty, had postponedly encountered that thing in sorrow's technicals called ruin. He had been an artisan of famed excellence, and with plenty to do; owned a house and garden; embraced a youthful, daughter-like, loving wife, and three blithe, ruddy children; every Sunday went to a cheerful-looking church, planted in a grove. But one night, under cover of darkness, and further concealed in a most cunning disguisement, a desperate burglar slid into his happy home, and robbed them all of everything. And darker yet to tell, the blacksmith himself did ignorantly conduct this burglar into his family's heart. It was the Bottle Conjuror! Upon the opening of that fatal cork, forth flew the fiend, and shrivelled up his home. Now, for prudent, most wise, and economic reasons, the blacksmith's shop was in the basement of his dwelling, but with a separate entrance to it; so that always had the young and loving healthy wife listened with no unhappy nervousness, but with vigorous pleasure, to the stout ringing of her young-armed old husband's hammer; whose reverberations, muffled by passing through the floors and walls, came up to her, not unsweetly, in her nursery; and so, to stout Labor's iron lullaby, the blacksmith's infants were rocked to slumber. Oh, woe on woe! Oh, Death, why canst thou not sometimes be timely? Hadst thou taken this old blacksmith to thyself ere his full ruin came upon him, then had the young widow had a delicious grief, and her orphans a truly venerable, legendary sire to dream of in their after years; and all of them a care-killing competency. 11 |

12 |
13 | 14 | -------------------------------------------------------------------------------- /src/app/view/sample.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | Wake up to WonderWidgets! 14 | 15 | 16 | 17 | 18 | Overview 19 | Why WonderWidgets are great 20 | 21 | Who buys WonderWidgets 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/app/view/static/openapi.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "title": "oscript-httpbin", 4 | "description": "Cервис тестирования HTTP клиента разработанный на OneScript + WINOW", 5 | "contact": { 6 | "email": "ivanovdmitry91@gmail.com", 7 | "url": "https://github.com/Stivo182/oscript-httpbin" 8 | }, 9 | "version": "1.1.0" 10 | }, 11 | "paths": { 12 | "/absolute-redirect/{n}": { 13 | "get": { 14 | "parameters": [ 15 | { 16 | "in": "path", 17 | "name": "n", 18 | "type": "int" 19 | } 20 | ], 21 | "produces": [ 22 | "text/html" 23 | ], 24 | "responses": { 25 | "302": { 26 | "description": "A redirection." 27 | } 28 | }, 29 | "summary": "Absolutely 302 Redirects n times.", 30 | "tags": [ 31 | "Redirects" 32 | ] 33 | } 34 | }, 35 | "/anything": { 36 | "delete": { 37 | "produces": [ 38 | "application/json" 39 | ], 40 | "responses": { 41 | "200": { 42 | "description": "Anything passed in request" 43 | } 44 | }, 45 | "summary": "Returns anything passed in request data.", 46 | "tags": [ 47 | "Anything" 48 | ] 49 | }, 50 | "get": { 51 | "produces": [ 52 | "application/json" 53 | ], 54 | "responses": { 55 | "200": { 56 | "description": "Anything passed in request" 57 | } 58 | }, 59 | "summary": "Returns anything passed in request data.", 60 | "tags": [ 61 | "Anything" 62 | ] 63 | }, 64 | "patch": { 65 | "produces": [ 66 | "application/json" 67 | ], 68 | "responses": { 69 | "200": { 70 | "description": "Anything passed in request" 71 | } 72 | }, 73 | "summary": "Returns anything passed in request data.", 74 | "tags": [ 75 | "Anything" 76 | ] 77 | }, 78 | "post": { 79 | "produces": [ 80 | "application/json" 81 | ], 82 | "responses": { 83 | "200": { 84 | "description": "Anything passed in request" 85 | } 86 | }, 87 | "summary": "Returns anything passed in request data.", 88 | "tags": [ 89 | "Anything" 90 | ] 91 | }, 92 | "put": { 93 | "produces": [ 94 | "application/json" 95 | ], 96 | "responses": { 97 | "200": { 98 | "description": "Anything passed in request" 99 | } 100 | }, 101 | "summary": "Returns anything passed in request data.", 102 | "tags": [ 103 | "Anything" 104 | ] 105 | } 106 | }, 107 | "/anything/{anything}": { 108 | "delete": { 109 | "produces": [ 110 | "application/json" 111 | ], 112 | "responses": { 113 | "200": { 114 | "description": "Anything passed in request" 115 | } 116 | }, 117 | "summary": "Returns anything passed in request data.", 118 | "tags": [ 119 | "Anything" 120 | ] 121 | }, 122 | "get": { 123 | "produces": [ 124 | "application/json" 125 | ], 126 | "responses": { 127 | "200": { 128 | "description": "Anything passed in request" 129 | } 130 | }, 131 | "summary": "Returns anything passed in request data.", 132 | "tags": [ 133 | "Anything" 134 | ] 135 | }, 136 | "patch": { 137 | "produces": [ 138 | "application/json" 139 | ], 140 | "responses": { 141 | "200": { 142 | "description": "Anything passed in request" 143 | } 144 | }, 145 | "summary": "Returns anything passed in request data.", 146 | "tags": [ 147 | "Anything" 148 | ] 149 | }, 150 | "post": { 151 | "produces": [ 152 | "application/json" 153 | ], 154 | "responses": { 155 | "200": { 156 | "description": "Anything passed in request" 157 | } 158 | }, 159 | "summary": "Returns anything passed in request data.", 160 | "tags": [ 161 | "Anything" 162 | ] 163 | }, 164 | "put": { 165 | "produces": [ 166 | "application/json" 167 | ], 168 | "responses": { 169 | "200": { 170 | "description": "Anything passed in request" 171 | } 172 | }, 173 | "summary": "Returns anything passed in request data.", 174 | "tags": [ 175 | "Anything" 176 | ] 177 | } 178 | }, 179 | "/base64/{value}": { 180 | "get": { 181 | "parameters": [ 182 | { 183 | "default": "SFRUUEJJTiBpcyBhd2Vzb21l", 184 | "in": "path", 185 | "name": "value", 186 | "type": "string" 187 | } 188 | ], 189 | "produces": [ 190 | "text/html" 191 | ], 192 | "responses": { 193 | "200": { 194 | "description": "Decoded base64 content." 195 | } 196 | }, 197 | "summary": "Decodes base64url-encoded string.", 198 | "tags": [ 199 | "Dynamic data" 200 | ] 201 | } 202 | }, 203 | "/basic-auth/{user}/{passwd}": { 204 | "get": { 205 | "parameters": [ 206 | { 207 | "in": "path", 208 | "name": "user", 209 | "type": "string" 210 | }, 211 | { 212 | "in": "path", 213 | "name": "passwd", 214 | "type": "string" 215 | } 216 | ], 217 | "produces": [ 218 | "application/json" 219 | ], 220 | "responses": { 221 | "200": { 222 | "description": "Sucessful authentication." 223 | }, 224 | "401": { 225 | "description": "Unsuccessful authentication." 226 | } 227 | }, 228 | "summary": "Prompts the user for authorization using HTTP Basic Auth.", 229 | "tags": [ 230 | "Auth" 231 | ] 232 | } 233 | }, 234 | "/bearer": { 235 | "get": { 236 | "parameters": [ 237 | { 238 | "in": "header", 239 | "name": "Authorization", 240 | "schema": { 241 | "type": "string" 242 | } 243 | } 244 | ], 245 | "produces": [ 246 | "application/json" 247 | ], 248 | "responses": { 249 | "200": { 250 | "description": "Sucessful authentication." 251 | }, 252 | "401": { 253 | "description": "Unsuccessful authentication." 254 | } 255 | }, 256 | "summary": "Prompts the user for authorization using bearer authentication.", 257 | "tags": [ 258 | "Auth" 259 | ] 260 | } 261 | }, 262 | "/brotli": { 263 | "get": { 264 | "produces": [ 265 | "application/json" 266 | ], 267 | "responses": { 268 | "200": { 269 | "description": "Brotli-encoded data." 270 | } 271 | }, 272 | "summary": "Returns Brotli-encoded data.", 273 | "tags": [ 274 | "Response formats" 275 | ] 276 | } 277 | }, 278 | "/bytes/{n}": { 279 | "get": { 280 | "parameters": [ 281 | { 282 | "in": "path", 283 | "name": "n", 284 | "type": "int" 285 | } 286 | ], 287 | "produces": [ 288 | "application/octet-stream" 289 | ], 290 | "responses": { 291 | "200": { 292 | "description": "Bytes." 293 | } 294 | }, 295 | "summary": "Returns n random bytes generated with given seed", 296 | "tags": [ 297 | "Dynamic data" 298 | ] 299 | } 300 | }, 301 | "/cache": { 302 | "get": { 303 | "parameters": [ 304 | { 305 | "in": "header", 306 | "name": "If-Modified-Since" 307 | }, 308 | { 309 | "in": "header", 310 | "name": "If-None-Match" 311 | } 312 | ], 313 | "produces": [ 314 | "application/json" 315 | ], 316 | "responses": { 317 | "200": { 318 | "description": "Cached response" 319 | }, 320 | "304": { 321 | "description": "Modified" 322 | } 323 | }, 324 | "summary": "Returns a 304 if an If-Modified-Since header or If-None-Match is present. Returns the same as a GET otherwise.", 325 | "tags": [ 326 | "Response inspection" 327 | ] 328 | } 329 | }, 330 | "/cache/{value}": { 331 | "get": { 332 | "parameters": [ 333 | { 334 | "in": "path", 335 | "name": "value", 336 | "type": "integer" 337 | } 338 | ], 339 | "produces": [ 340 | "application/json" 341 | ], 342 | "responses": { 343 | "200": { 344 | "description": "Cache control set" 345 | } 346 | }, 347 | "summary": "Sets a Cache-Control header for n seconds.", 348 | "tags": [ 349 | "Response inspection" 350 | ] 351 | } 352 | }, 353 | "/cookies": { 354 | "get": { 355 | "produces": [ 356 | "application/json" 357 | ], 358 | "responses": { 359 | "200": { 360 | "description": "Set cookies." 361 | } 362 | }, 363 | "summary": "Returns cookie data.", 364 | "tags": [ 365 | "Cookies" 366 | ] 367 | } 368 | }, 369 | "/cookies/delete": { 370 | "get": { 371 | "parameters": [ 372 | { 373 | "allowEmptyValue": true, 374 | "explode": true, 375 | "in": "query", 376 | "name": "freeform", 377 | "schema": { 378 | "additionalProperties": { 379 | "type": "string" 380 | }, 381 | "type": "object" 382 | }, 383 | "style": "form" 384 | } 385 | ], 386 | "produces": [ 387 | "text/plain" 388 | ], 389 | "responses": { 390 | "200": { 391 | "description": "Redirect to cookie list" 392 | } 393 | }, 394 | "summary": "Deletes cookie(s) as provided by the query string and redirects to cookie list.", 395 | "tags": [ 396 | "Cookies" 397 | ] 398 | } 399 | }, 400 | "/cookies/set": { 401 | "get": { 402 | "parameters": [ 403 | { 404 | "allowEmptyValue": true, 405 | "explode": true, 406 | "in": "query", 407 | "name": "freeform", 408 | "schema": { 409 | "additionalProperties": { 410 | "type": "string" 411 | }, 412 | "type": "object" 413 | }, 414 | "style": "form" 415 | } 416 | ], 417 | "produces": [ 418 | "text/plain" 419 | ], 420 | "responses": { 421 | "200": { 422 | "description": "Redirect to cookie list" 423 | } 424 | }, 425 | "summary": "Sets cookie(s) as provided by the query string and redirects to cookie list.", 426 | "tags": [ 427 | "Cookies" 428 | ] 429 | } 430 | }, 431 | "/cookies/set/{name}/{value}": { 432 | "get": { 433 | "parameters": [ 434 | { 435 | "in": "path", 436 | "name": "name", 437 | "type": "string" 438 | }, 439 | { 440 | "in": "path", 441 | "name": "value", 442 | "type": "string" 443 | } 444 | ], 445 | "produces": [ 446 | "text/plain" 447 | ], 448 | "responses": { 449 | "200": { 450 | "description": "Set cookies and redirects to cookie list." 451 | } 452 | }, 453 | "summary": "Sets a cookie and redirects to cookie list.", 454 | "tags": [ 455 | "Cookies" 456 | ] 457 | } 458 | }, 459 | "/deflate": { 460 | "get": { 461 | "produces": [ 462 | "application/json" 463 | ], 464 | "responses": { 465 | "200": { 466 | "description": "Defalte-encoded data." 467 | } 468 | }, 469 | "summary": "Returns Deflate-encoded data.", 470 | "tags": [ 471 | "Response formats" 472 | ] 473 | } 474 | }, 475 | "/delay/{delay}": { 476 | "delete": { 477 | "parameters": [ 478 | { 479 | "in": "path", 480 | "name": "delay", 481 | "type": "int" 482 | } 483 | ], 484 | "produces": [ 485 | "application/json" 486 | ], 487 | "responses": { 488 | "200": { 489 | "description": "A delayed response." 490 | } 491 | }, 492 | "summary": "Returns a delayed response (max of 10 seconds).", 493 | "tags": [ 494 | "Dynamic data" 495 | ] 496 | }, 497 | "get": { 498 | "parameters": [ 499 | { 500 | "in": "path", 501 | "name": "delay", 502 | "type": "int" 503 | } 504 | ], 505 | "produces": [ 506 | "application/json" 507 | ], 508 | "responses": { 509 | "200": { 510 | "description": "A delayed response." 511 | } 512 | }, 513 | "summary": "Returns a delayed response (max of 10 seconds).", 514 | "tags": [ 515 | "Dynamic data" 516 | ] 517 | }, 518 | "patch": { 519 | "parameters": [ 520 | { 521 | "in": "path", 522 | "name": "delay", 523 | "type": "int" 524 | } 525 | ], 526 | "produces": [ 527 | "application/json" 528 | ], 529 | "responses": { 530 | "200": { 531 | "description": "A delayed response." 532 | } 533 | }, 534 | "summary": "Returns a delayed response (max of 10 seconds).", 535 | "tags": [ 536 | "Dynamic data" 537 | ] 538 | }, 539 | "post": { 540 | "parameters": [ 541 | { 542 | "in": "path", 543 | "name": "delay", 544 | "type": "int" 545 | } 546 | ], 547 | "produces": [ 548 | "application/json" 549 | ], 550 | "responses": { 551 | "200": { 552 | "description": "A delayed response." 553 | } 554 | }, 555 | "summary": "Returns a delayed response (max of 10 seconds).", 556 | "tags": [ 557 | "Dynamic data" 558 | ] 559 | }, 560 | "put": { 561 | "parameters": [ 562 | { 563 | "in": "path", 564 | "name": "delay", 565 | "type": "int" 566 | } 567 | ], 568 | "produces": [ 569 | "application/json" 570 | ], 571 | "responses": { 572 | "200": { 573 | "description": "A delayed response." 574 | } 575 | }, 576 | "summary": "Returns a delayed response (max of 10 seconds).", 577 | "tags": [ 578 | "Dynamic data" 579 | ] 580 | } 581 | }, 582 | "/delete": { 583 | "delete": { 584 | "produces": [ 585 | "application/json" 586 | ], 587 | "responses": { 588 | "200": { 589 | "description": "The request's DELETE parameters." 590 | } 591 | }, 592 | "summary": "The request's DELETE parameters.", 593 | "tags": [ 594 | "HTTP Methods" 595 | ] 596 | } 597 | }, 598 | "/deny": { 599 | "get": { 600 | "produces": [ 601 | "text/plain" 602 | ], 603 | "responses": { 604 | "200": { 605 | "description": "Denied message" 606 | } 607 | }, 608 | "summary": "Returns page denied by robots.txt rules.", 609 | "tags": [ 610 | "Response formats" 611 | ] 612 | } 613 | }, 614 | "/encoding/utf8": { 615 | "get": { 616 | "produces": [ 617 | "text/html" 618 | ], 619 | "responses": { 620 | "200": { 621 | "description": "Encoded UTF-8 content." 622 | } 623 | }, 624 | "summary": "Returns a UTF-8 encoded body.", 625 | "tags": [ 626 | "Response formats" 627 | ] 628 | } 629 | }, 630 | "/etag/{etag}": { 631 | "get": { 632 | "parameters": [ 633 | { 634 | "in": "header", 635 | "name": "If-None-Match" 636 | }, 637 | { 638 | "in": "header", 639 | "name": "If-Match" 640 | } 641 | ], 642 | "produces": [ 643 | "application/json" 644 | ], 645 | "responses": { 646 | "200": { 647 | "description": "Normal response" 648 | }, 649 | "412": { 650 | "description": "match" 651 | } 652 | }, 653 | "summary": "Assumes the resource has the given etag and responds to If-None-Match and If-Match headers appropriately.", 654 | "tags": [ 655 | "Response inspection" 656 | ] 657 | } 658 | }, 659 | "/get": { 660 | "get": { 661 | "produces": [ 662 | "application/json" 663 | ], 664 | "responses": { 665 | "200": { 666 | "description": "The request's query parameters." 667 | } 668 | }, 669 | "summary": "The request's query parameters.", 670 | "tags": [ 671 | "HTTP Methods" 672 | ] 673 | } 674 | }, 675 | "/gzip": { 676 | "get": { 677 | "produces": [ 678 | "application/json" 679 | ], 680 | "responses": { 681 | "200": { 682 | "description": "GZip-encoded data." 683 | } 684 | }, 685 | "summary": "Returns GZip-encoded data.", 686 | "tags": [ 687 | "Response formats" 688 | ] 689 | } 690 | }, 691 | "/headers": { 692 | "get": { 693 | "produces": [ 694 | "application/json" 695 | ], 696 | "responses": { 697 | "200": { 698 | "description": "The request's headers." 699 | } 700 | }, 701 | "summary": "Return the incoming request's HTTP headers.", 702 | "tags": [ 703 | "Request inspection" 704 | ] 705 | } 706 | }, 707 | "/hidden-basic-auth/{user}/{passwd}": { 708 | "get": { 709 | "parameters": [ 710 | { 711 | "in": "path", 712 | "name": "user", 713 | "type": "string" 714 | }, 715 | { 716 | "in": "path", 717 | "name": "passwd", 718 | "type": "string" 719 | } 720 | ], 721 | "produces": [ 722 | "application/json" 723 | ], 724 | "responses": { 725 | "200": { 726 | "description": "Sucessful authentication." 727 | }, 728 | "404": { 729 | "description": "Unsuccessful authentication." 730 | } 731 | }, 732 | "summary": "Prompts the user for authorization using HTTP Basic Auth.", 733 | "tags": [ 734 | "Auth" 735 | ] 736 | } 737 | }, 738 | "/html": { 739 | "get": { 740 | "produces": [ 741 | "text/html" 742 | ], 743 | "responses": { 744 | "200": { 745 | "description": "An HTML page." 746 | } 747 | }, 748 | "summary": "Returns a simple HTML document.", 749 | "tags": [ 750 | "Response formats" 751 | ] 752 | } 753 | }, 754 | "/image": { 755 | "get": { 756 | "produces": [ 757 | "image/webp", 758 | "image/svg+xml", 759 | "image/jpeg", 760 | "image/png", 761 | "image/*" 762 | ], 763 | "responses": { 764 | "200": { 765 | "description": "An image." 766 | } 767 | }, 768 | "summary": "Returns a simple image of the type suggest by the Accept header.", 769 | "tags": [ 770 | "Images" 771 | ] 772 | } 773 | }, 774 | "/image/jpeg": { 775 | "get": { 776 | "produces": [ 777 | "image/jpeg" 778 | ], 779 | "responses": { 780 | "200": { 781 | "description": "A JPEG image." 782 | } 783 | }, 784 | "summary": "Returns a simple JPEG image.", 785 | "tags": [ 786 | "Images" 787 | ] 788 | } 789 | }, 790 | "/image/png": { 791 | "get": { 792 | "produces": [ 793 | "image/png" 794 | ], 795 | "responses": { 796 | "200": { 797 | "description": "A PNG image." 798 | } 799 | }, 800 | "summary": "Returns a simple PNG image.", 801 | "tags": [ 802 | "Images" 803 | ] 804 | } 805 | }, 806 | "/image/svg": { 807 | "get": { 808 | "produces": [ 809 | "image/svg+xml" 810 | ], 811 | "responses": { 812 | "200": { 813 | "description": "An SVG image." 814 | } 815 | }, 816 | "summary": "Returns a simple SVG image.", 817 | "tags": [ 818 | "Images" 819 | ] 820 | } 821 | }, 822 | "/image/webp": { 823 | "get": { 824 | "produces": [ 825 | "image/webp" 826 | ], 827 | "responses": { 828 | "200": { 829 | "description": "A WEBP image." 830 | } 831 | }, 832 | "summary": "Returns a simple WEBP image.", 833 | "tags": [ 834 | "Images" 835 | ] 836 | } 837 | }, 838 | "/ip": { 839 | "get": { 840 | "produces": [ 841 | "application/json" 842 | ], 843 | "responses": { 844 | "200": { 845 | "description": "The Requester's IP Address." 846 | } 847 | }, 848 | "summary": "Returns the requester's IP Address.", 849 | "tags": [ 850 | "Request inspection" 851 | ] 852 | } 853 | }, 854 | "/json": { 855 | "get": { 856 | "produces": [ 857 | "application/json" 858 | ], 859 | "responses": { 860 | "200": { 861 | "description": "An JSON document." 862 | } 863 | }, 864 | "summary": "Returns a simple JSON document.", 865 | "tags": [ 866 | "Response formats" 867 | ] 868 | } 869 | }, 870 | "/links/{n}/{offset}": { 871 | "get": { 872 | "parameters": [ 873 | { 874 | "in": "path", 875 | "name": "n", 876 | "type": "int" 877 | }, 878 | { 879 | "in": "path", 880 | "name": "offset", 881 | "type": "int" 882 | } 883 | ], 884 | "produces": [ 885 | "text/html" 886 | ], 887 | "responses": { 888 | "200": { 889 | "description": "HTML links." 890 | } 891 | }, 892 | "summary": "Generate a page containing n links to other pages which do the same.", 893 | "tags": [ 894 | "Dynamic data" 895 | ] 896 | } 897 | }, 898 | "/patch": { 899 | "patch": { 900 | "produces": [ 901 | "application/json" 902 | ], 903 | "responses": { 904 | "200": { 905 | "description": "The request's PATCH parameters." 906 | } 907 | }, 908 | "summary": "The request's PATCH parameters.", 909 | "tags": [ 910 | "HTTP Methods" 911 | ] 912 | } 913 | }, 914 | "/post": { 915 | "post": { 916 | "produces": [ 917 | "application/json" 918 | ], 919 | "responses": { 920 | "200": { 921 | "description": "The request's POST parameters." 922 | } 923 | }, 924 | "summary": "The request's POST parameters.", 925 | "tags": [ 926 | "HTTP Methods" 927 | ] 928 | } 929 | }, 930 | "/put": { 931 | "put": { 932 | "produces": [ 933 | "application/json" 934 | ], 935 | "responses": { 936 | "200": { 937 | "description": "The request's PUT parameters." 938 | } 939 | }, 940 | "summary": "The request's PUT parameters.", 941 | "tags": [ 942 | "HTTP Methods" 943 | ] 944 | } 945 | }, 946 | "/redirect-to": { 947 | "delete": { 948 | "produces": [ 949 | "text/html" 950 | ], 951 | "responses": { 952 | "302": { 953 | "description": "A redirection." 954 | } 955 | }, 956 | "summary": "302/3XX Redirects to the given URL.", 957 | "tags": [ 958 | "Redirects" 959 | ] 960 | }, 961 | "get": { 962 | "parameters": [ 963 | { 964 | "in": "query", 965 | "name": "url", 966 | "required": true, 967 | "type": "string" 968 | }, 969 | { 970 | "in": "query", 971 | "name": "status_code", 972 | "type": "int" 973 | } 974 | ], 975 | "produces": [ 976 | "text/html" 977 | ], 978 | "responses": { 979 | "302": { 980 | "description": "A redirection." 981 | } 982 | }, 983 | "summary": "302/3XX Redirects to the given URL.", 984 | "tags": [ 985 | "Redirects" 986 | ] 987 | }, 988 | "patch": { 989 | "produces": [ 990 | "text/html" 991 | ], 992 | "responses": { 993 | "302": { 994 | "description": "A redirection." 995 | } 996 | }, 997 | "summary": "302/3XX Redirects to the given URL.", 998 | "tags": [ 999 | "Redirects" 1000 | ] 1001 | }, 1002 | "post": { 1003 | "parameters": [ 1004 | { 1005 | "in": "formData", 1006 | "name": "url", 1007 | "required": true, 1008 | "type": "string" 1009 | }, 1010 | { 1011 | "in": "formData", 1012 | "name": "status_code", 1013 | "required": false, 1014 | "type": "int" 1015 | } 1016 | ], 1017 | "produces": [ 1018 | "text/html" 1019 | ], 1020 | "responses": { 1021 | "302": { 1022 | "description": "A redirection." 1023 | } 1024 | }, 1025 | "summary": "302/3XX Redirects to the given URL.", 1026 | "tags": [ 1027 | "Redirects" 1028 | ] 1029 | }, 1030 | "put": { 1031 | "parameters": [ 1032 | { 1033 | "in": "formData", 1034 | "name": "url", 1035 | "required": true, 1036 | "type": "string" 1037 | }, 1038 | { 1039 | "in": "formData", 1040 | "name": "status_code", 1041 | "required": false, 1042 | "type": "int" 1043 | } 1044 | ], 1045 | "produces": [ 1046 | "text/html" 1047 | ], 1048 | "responses": { 1049 | "302": { 1050 | "description": "A redirection." 1051 | } 1052 | }, 1053 | "summary": "302/3XX Redirects to the given URL.", 1054 | "tags": [ 1055 | "Redirects" 1056 | ] 1057 | } 1058 | }, 1059 | "/redirect/{n}": { 1060 | "get": { 1061 | "parameters": [ 1062 | { 1063 | "in": "path", 1064 | "name": "n", 1065 | "type": "int" 1066 | } 1067 | ], 1068 | "produces": [ 1069 | "text/html" 1070 | ], 1071 | "responses": { 1072 | "302": { 1073 | "description": "A redirection." 1074 | } 1075 | }, 1076 | "summary": "302 Redirects n times.", 1077 | "tags": [ 1078 | "Redirects" 1079 | ] 1080 | } 1081 | }, 1082 | "/relative-redirect/{n}": { 1083 | "get": { 1084 | "parameters": [ 1085 | { 1086 | "in": "path", 1087 | "name": "n", 1088 | "type": "int" 1089 | } 1090 | ], 1091 | "produces": [ 1092 | "text/html" 1093 | ], 1094 | "responses": { 1095 | "302": { 1096 | "description": "A redirection." 1097 | } 1098 | }, 1099 | "summary": "Relatively 302 Redirects n times.", 1100 | "tags": [ 1101 | "Redirects" 1102 | ] 1103 | } 1104 | }, 1105 | "/response-headers": { 1106 | "get": { 1107 | "parameters": [ 1108 | { 1109 | "allowEmptyValue": true, 1110 | "explode": true, 1111 | "in": "query", 1112 | "name": "freeform", 1113 | "schema": { 1114 | "additionalProperties": { 1115 | "type": "string" 1116 | }, 1117 | "type": "object" 1118 | }, 1119 | "style": "form" 1120 | } 1121 | ], 1122 | "produces": [ 1123 | "application/json" 1124 | ], 1125 | "responses": { 1126 | "200": { 1127 | "description": "Response headers" 1128 | } 1129 | }, 1130 | "summary": "Returns a set of response headers from the query string.", 1131 | "tags": [ 1132 | "Response inspection" 1133 | ] 1134 | }, 1135 | "post": { 1136 | "parameters": [ 1137 | { 1138 | "allowEmptyValue": true, 1139 | "explode": true, 1140 | "in": "query", 1141 | "name": "freeform", 1142 | "schema": { 1143 | "additionalProperties": { 1144 | "type": "string" 1145 | }, 1146 | "type": "object" 1147 | }, 1148 | "style": "form" 1149 | } 1150 | ], 1151 | "produces": [ 1152 | "application/json" 1153 | ], 1154 | "responses": { 1155 | "200": { 1156 | "description": "Response headers" 1157 | } 1158 | }, 1159 | "summary": "Returns a set of response headers from the query string.", 1160 | "tags": [ 1161 | "Response inspection" 1162 | ] 1163 | } 1164 | }, 1165 | "/robots.txt": { 1166 | "get": { 1167 | "produces": [ 1168 | "text/plain" 1169 | ], 1170 | "responses": { 1171 | "200": { 1172 | "description": "Robots file" 1173 | } 1174 | }, 1175 | "summary": "Returns some robots.txt rules.", 1176 | "tags": [ 1177 | "Response formats" 1178 | ] 1179 | } 1180 | }, 1181 | "/status/{codes}": { 1182 | "delete": { 1183 | "parameters": [ 1184 | { 1185 | "in": "path", 1186 | "name": "codes" 1187 | } 1188 | ], 1189 | "produces": [ 1190 | "text/plain" 1191 | ], 1192 | "responses": { 1193 | "100": { 1194 | "description": "Informational responses" 1195 | }, 1196 | "200": { 1197 | "description": "Success" 1198 | }, 1199 | "300": { 1200 | "description": "Redirection" 1201 | }, 1202 | "400": { 1203 | "description": "Client Errors" 1204 | }, 1205 | "500": { 1206 | "description": "Server Errors" 1207 | } 1208 | }, 1209 | "summary": "Return status code or random status code if more than one are given", 1210 | "tags": [ 1211 | "Status codes" 1212 | ] 1213 | }, 1214 | "get": { 1215 | "parameters": [ 1216 | { 1217 | "in": "path", 1218 | "name": "codes" 1219 | } 1220 | ], 1221 | "produces": [ 1222 | "text/plain" 1223 | ], 1224 | "responses": { 1225 | "100": { 1226 | "description": "Informational responses" 1227 | }, 1228 | "200": { 1229 | "description": "Success" 1230 | }, 1231 | "300": { 1232 | "description": "Redirection" 1233 | }, 1234 | "400": { 1235 | "description": "Client Errors" 1236 | }, 1237 | "500": { 1238 | "description": "Server Errors" 1239 | } 1240 | }, 1241 | "summary": "Return status code or random status code if more than one are given", 1242 | "tags": [ 1243 | "Status codes" 1244 | ] 1245 | }, 1246 | "patch": { 1247 | "parameters": [ 1248 | { 1249 | "in": "path", 1250 | "name": "codes" 1251 | } 1252 | ], 1253 | "produces": [ 1254 | "text/plain" 1255 | ], 1256 | "responses": { 1257 | "100": { 1258 | "description": "Informational responses" 1259 | }, 1260 | "200": { 1261 | "description": "Success" 1262 | }, 1263 | "300": { 1264 | "description": "Redirection" 1265 | }, 1266 | "400": { 1267 | "description": "Client Errors" 1268 | }, 1269 | "500": { 1270 | "description": "Server Errors" 1271 | } 1272 | }, 1273 | "summary": "Return status code or random status code if more than one are given", 1274 | "tags": [ 1275 | "Status codes" 1276 | ] 1277 | }, 1278 | "post": { 1279 | "parameters": [ 1280 | { 1281 | "in": "path", 1282 | "name": "codes" 1283 | } 1284 | ], 1285 | "produces": [ 1286 | "text/plain" 1287 | ], 1288 | "responses": { 1289 | "100": { 1290 | "description": "Informational responses" 1291 | }, 1292 | "200": { 1293 | "description": "Success" 1294 | }, 1295 | "300": { 1296 | "description": "Redirection" 1297 | }, 1298 | "400": { 1299 | "description": "Client Errors" 1300 | }, 1301 | "500": { 1302 | "description": "Server Errors" 1303 | } 1304 | }, 1305 | "summary": "Return status code or random status code if more than one are given", 1306 | "tags": [ 1307 | "Status codes" 1308 | ] 1309 | }, 1310 | "put": { 1311 | "parameters": [ 1312 | { 1313 | "in": "path", 1314 | "name": "codes" 1315 | } 1316 | ], 1317 | "produces": [ 1318 | "text/plain" 1319 | ], 1320 | "responses": { 1321 | "100": { 1322 | "description": "Informational responses" 1323 | }, 1324 | "200": { 1325 | "description": "Success" 1326 | }, 1327 | "300": { 1328 | "description": "Redirection" 1329 | }, 1330 | "400": { 1331 | "description": "Client Errors" 1332 | }, 1333 | "500": { 1334 | "description": "Server Errors" 1335 | } 1336 | }, 1337 | "summary": "Return status code or random status code if more than one are given", 1338 | "tags": [ 1339 | "Status codes" 1340 | ] 1341 | } 1342 | }, 1343 | "/user-agent": { 1344 | "get": { 1345 | "produces": [ 1346 | "application/json" 1347 | ], 1348 | "responses": { 1349 | "200": { 1350 | "description": "The request's User-Agent header." 1351 | } 1352 | }, 1353 | "summary": "Return the incoming requests's User-Agent header.", 1354 | "tags": [ 1355 | "Request inspection" 1356 | ] 1357 | } 1358 | }, 1359 | "/uuid": { 1360 | "get": { 1361 | "produces": [ 1362 | "application/json" 1363 | ], 1364 | "responses": { 1365 | "200": { 1366 | "description": "A UUID4." 1367 | } 1368 | }, 1369 | "summary": "Return a UUID4.", 1370 | "tags": [ 1371 | "Dynamic data" 1372 | ] 1373 | } 1374 | }, 1375 | "/uuid/{n}": { 1376 | "get": { 1377 | "parameters": [ 1378 | { 1379 | "in": "path", 1380 | "name": "n", 1381 | "type": "int" 1382 | } 1383 | ], 1384 | "produces": [ 1385 | "application/json" 1386 | ], 1387 | "responses": { 1388 | "200": { 1389 | "description": "A UUID4." 1390 | } 1391 | }, 1392 | "summary": "Return a UUID4.", 1393 | "tags": [ 1394 | "Dynamic data" 1395 | ] 1396 | } 1397 | }, 1398 | "/xml": { 1399 | "get": { 1400 | "produces": [ 1401 | "application/xml" 1402 | ], 1403 | "responses": { 1404 | "200": { 1405 | "description": "An XML document." 1406 | } 1407 | }, 1408 | "summary": "Returns a simple XML document.", 1409 | "tags": [ 1410 | "Response formats" 1411 | ] 1412 | } 1413 | }, 1414 | "/zstd": { 1415 | "get": { 1416 | "produces": [ 1417 | "application/json" 1418 | ], 1419 | "responses": { 1420 | "200": { 1421 | "description": "Zstd-encoded data." 1422 | } 1423 | }, 1424 | "summary": "Returns Zstd-encoded data.", 1425 | "tags": [ 1426 | "Response formats" 1427 | ] 1428 | } 1429 | } 1430 | }, 1431 | "protocol": "http", 1432 | "schemes": [ 1433 | "http" 1434 | ], 1435 | "swagger": "2.0", 1436 | "tags": [ 1437 | { 1438 | "description": "Testing different HTTP verbs", 1439 | "name": "HTTP Methods" 1440 | }, 1441 | { 1442 | "description": "Auth methods", 1443 | "name": "Auth" 1444 | }, 1445 | { 1446 | "description": "Generates responses with given status code", 1447 | "name": "Status codes" 1448 | }, 1449 | { 1450 | "description": "Inspect the request data", 1451 | "name": "Request inspection" 1452 | }, 1453 | { 1454 | "description": "Inspect the response data like caching and headers", 1455 | "name": "Response inspection" 1456 | }, 1457 | { 1458 | "description": "Returns responses in different data formats", 1459 | "name": "Response formats" 1460 | }, 1461 | { 1462 | "description": "Generates random and dynamic data", 1463 | "name": "Dynamic data" 1464 | }, 1465 | { 1466 | "description": "Creates, reads and deletes Cookies", 1467 | "name": "Cookies" 1468 | }, 1469 | { 1470 | "description": "Returns different image formats", 1471 | "name": "Images" 1472 | }, 1473 | { 1474 | "description": "Returns different redirect responses", 1475 | "name": "Redirects" 1476 | }, 1477 | { 1478 | "description": "Returns anything that is passed to request", 1479 | "name": "Anything" 1480 | } 1481 | ] 1482 | } -------------------------------------------------------------------------------- /src/app/view/static/swagger/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | overflow: -moz-scrollbars-vertical; 4 | overflow-y: scroll; 5 | } 6 | 7 | *, 8 | *:before, 9 | *:after { 10 | box-sizing: inherit; 11 | } 12 | 13 | body { 14 | margin: 0; 15 | background: #fafafa; 16 | } 17 | 18 | .swagger-ui .topbar { 19 | display: none !important; 20 | } -------------------------------------------------------------------------------- /src/cmd/main.os: -------------------------------------------------------------------------------- 1 | #Использовать autumn 2 | #Использовать autumn-cli 3 | #Использовать "." 4 | #Использовать "../core" 5 | 6 | Процедура УстановитьКорневойКаталог() 7 | ТекущийКаталог = Новый Файл(ОбъединитьПути(ТекущийСценарий().Каталог, "../..")).ПолноеИмя; 8 | УстановитьТекущийКаталог(ТекущийКаталог); 9 | КонецПроцедуры 10 | 11 | УстановитьКорневойКаталог(); 12 | 13 | Поделка = Новый Поделка; 14 | Поделка.ЗапуститьПриложение(); -------------------------------------------------------------------------------- /src/cmd/Классы/HttpBin_КомандаЗапустить.os: -------------------------------------------------------------------------------- 1 | // BSLLS:UsingHardcodeNetworkAddress-off 2 | 3 | Перем HttpBin; // см. HttpBin 4 | 5 | &Опция(Имя = "h host", Описание = "Имя хоста / IP адрес сервиса") 6 | &ТСтрока 7 | &ПоУмолчанию("127.0.0.1") 8 | Перем Хост; // Строка 9 | 10 | &Опция(Имя = "p port", Описание = "Порт сервиса") 11 | &ТЧисло 12 | &ПоУмолчанию(3334) 13 | Перем Порт; // Число 14 | 15 | &КомандаПриложения(Имя = "run", Описание = "Запуск сервиса") 16 | Процедура ПриСозданииОбъекта(&Пластилин("HttpBin") _HttpBin) 17 | HttpBin = _HttpBin; 18 | КонецПроцедуры 19 | 20 | &ВыполнениеКоманды 21 | Процедура Запустить() Экспорт 22 | HttpBin 23 | .УстановитьХост(Хост) 24 | .УстановитьПорт(Порт) 25 | .ЗапускатьВФоне(Ложь) 26 | .Запустить(); 27 | КонецПроцедуры -------------------------------------------------------------------------------- /src/core/Классы/HttpBin.os: -------------------------------------------------------------------------------- 1 | // BSLLS:UsingHardcodeNetworkAddress-off 2 | 3 | #Использовать autumn 4 | #Использовать winow 5 | #Использовать compressor 6 | #Использовать 1connector 7 | 8 | Перем Поделка; // Ссылка на объект Поделка (autumn) 9 | Перем ВебСервер; // Ссылка на объект ПрикладнойВебСервер (winow) 10 | Перем НастройкиВебСервера; // Ссылка на объект Настройки (winow) 11 | Перем ЗапускательВебПриложения; // Ссылка на объект ЗапускательВебПриложения (winow) 12 | 13 | Перем ЗапускатьВФоне; // Булево 14 | Перем ОжидатьЗапуск; // Булево 15 | Перем ТаймаутОжидания; // Количество 16 | 17 | #Область ПрограммныйИнтерфейс 18 | 19 | // Запускает сервис 20 | // 21 | // Возвращаемое значение: 22 | // ЭтотОбъект 23 | Функция Запустить() Экспорт 24 | 25 | Если ЗапускатьВФоне Тогда 26 | ФоновыеЗадания.Выполнить(ЗапускательВебПриложения, "Запустить"); 27 | 28 | Если ОжидатьЗапуск Тогда 29 | НачатьОжиданиеЗапуска(); 30 | КонецЕсли; 31 | Иначе 32 | ЗапускательВебПриложения.Запустить(); 33 | КонецЕсли; 34 | 35 | Возврат ЭтотОбъект; 36 | 37 | КонецФункции 38 | 39 | // Останавливает сервис 40 | // 41 | // Возвращаемое значение: 42 | // ЭтотОбъект 43 | Функция Остановить() Экспорт 44 | ВебСервер.Стоп(); 45 | Возврат ЭтотОбъект; 46 | КонецФункции 47 | 48 | // Возвращает адрес сервиса 49 | // 50 | // Возвращаемое значение: 51 | // Строка 52 | Функция URL() Экспорт 53 | Возврат СтрШаблон("http://%1:%2", НастройкиВебСервера.ИмяХоста, НастройкиВебСервера.Порт); 54 | КонецФункции 55 | 56 | // Порт сервиса 57 | // 58 | // Возвращаемое значение: 59 | // Строка 60 | Функция Порт() Экспорт 61 | Возврат НастройкиВебСервера.Порт; 62 | КонецФункции 63 | 64 | // Устанавливает порт сервиса 65 | // 66 | // Параметры: 67 | // Порт - Число - Номер порта 68 | // 69 | // Возвращаемое значение: 70 | // ЭтотОбъект 71 | Функция УстановитьПорт(Порт) Экспорт 72 | НастройкиВебСервера.Порт = Порт; 73 | Возврат ЭтотОбъект; 74 | КонецФункции 75 | 76 | // Имя хоста / ip адрес сервиса 77 | // 78 | // Возвращаемое значение: 79 | // Строка 80 | Функция Хост() Экспорт 81 | Возврат НастройкиВебСервера.ИмяХоста; 82 | КонецФункции 83 | 84 | // Устанавливает хост сервиса 85 | // 86 | // Параметры: 87 | // Хост - Строка - Имя хоста / ip адрес 88 | // 89 | // Возвращаемое значение: 90 | // ЭтотОбъект 91 | Функция УстановитьХост(Хост) Экспорт 92 | НастройкиВебСервера.ИмяХоста = Хост; 93 | Возврат ЭтотОбъект; 94 | КонецФункции 95 | 96 | // Запуск сервиса будет выполнен в фоновом режиме 97 | // 98 | // Параметры: 99 | // Флаг - Булево 100 | // 101 | // Возвращаемое значение: 102 | // ЭтотОбъект 103 | Функция ЗапускатьВФоне(Флаг = Истина) Экспорт 104 | ЗапускатьВФоне = Флаг; 105 | Возврат ЭтотОбъект; 106 | КонецФункции 107 | 108 | // Ожидать завершение запуска сервиса, запущенного в фоновом режиме 109 | // 110 | // Параметры: 111 | // Флаг - Булево 112 | // 113 | // Возвращаемое значение: 114 | // ЭтотОбъект 115 | Функция ОжидатьЗапуск(Флаг = Истина) Экспорт 116 | ОжидатьЗапуск = Флаг; 117 | Возврат ЭтотОбъект; 118 | КонецФункции 119 | 120 | // Устанавливает таймаут ожидания запуска сервиса, запущенного в фоновом режиме 121 | // 122 | // Параметры: 123 | // Таймаут - Количество - Таймаут в секундах 124 | // 125 | // Возвращаемое значение: 126 | // ЭтотОбъект 127 | Функция УстановитьТаймаутОжидания(Таймаут) Экспорт 128 | ТаймаутОжидания = Таймаут; 129 | Возврат ЭтотОбъект; 130 | КонецФункции 131 | 132 | // Устанавливает задержку перед чтением сокета для прикладного веб-сервера (OneScript v1.9) 133 | // 134 | // Параметры: 135 | // Задержка - Число - Задержка в миллисекундах (По умолчанию 65 мс) 136 | // 137 | // Возвращаемое значение: 138 | // ЭтотОбъект 139 | Функция УстановитьЗадержкуПередЧтениемСокета(Задержка) Экспорт 140 | НастройкиВебСервера.ЗадержкаПередЧтениемСокета = Задержка; 141 | Возврат ЭтотОбъект; 142 | КонецФункции 143 | 144 | #КонецОбласти 145 | 146 | #Область СлужебныеПроцедурыИФункции 147 | 148 | // Cервис тестирования HTTP клиента. 149 | // 150 | // Сервис по умолчанию запускается по адресу 127.0.0.1:3334 в фоновом режиме 151 | // и с ожиданием завершения запуска сервиса. 152 | // 153 | // Параметры: 154 | // ПоделкаОсени - Объект - Ссылка на объект Поделка (autumn) 155 | &Желудь 156 | Процедура ПриСозданииОбъекта(&Пластилин("Поделка") ПоделкаОсени = Неопределено) 157 | 158 | ХостПоУмолчанию = "127.0.0.1"; 159 | ПортПоУмолчанию = 3334; 160 | ТаймаутОжиданияПоУмолчанию = 5; 161 | ЗадержкаПередЧтениемСокета = 65; 162 | 163 | Если ПоделкаОсени = Неопределено Тогда 164 | УстановитьКорневойКаталог(); 165 | 166 | Поделка = Новый Поделка; 167 | Поделка.ЗапуститьПриложение(); 168 | Иначе 169 | Поделка = ПоделкаОсени; 170 | КонецЕсли; 171 | 172 | ВебСервер = Поделка.НайтиЖелудь("ВебСервер"); 173 | НастройкиВебСервера = Поделка.НайтиЖелудь("Настройки"); 174 | ЗапускательВебПриложения = Поделка.НайтиЖелудь("ЗапускательВебПриложения"); 175 | 176 | НастройкиВебСервера.РазмерБуфера = 0; 177 | 178 | УстановитьХост(ХостПоУмолчанию); 179 | УстановитьПорт(ПортПоУмолчанию); 180 | ЗапускатьВФоне(); 181 | ОжидатьЗапуск(); 182 | УстановитьТаймаутОжидания(ТаймаутОжиданияПоУмолчанию); 183 | УстановитьЗадержкуПередЧтениемСокета(ЗадержкаПередЧтениемСокета); 184 | 185 | КонецПроцедуры 186 | 187 | Процедура НачатьОжиданиеЗапуска() 188 | 189 | ЗадержкаМеждуПопытками = 100; // Миллисекунд 190 | ВремяНачала = ТекущаяУниверсальнаяДата(); 191 | 192 | Пока Истина Цикл 193 | 194 | КодСостояния = Неопределено; 195 | ТекстИсключения = ""; 196 | Попытка 197 | Ответ = КоннекторHTTP.Head(URL(), Новый Структура("Таймаут", ТаймаутОжидания)); 198 | КодСостояния = Ответ.КодСостояния; 199 | Исключение 200 | ТекстИсключения = СокрП(КраткоеПредставлениеОшибки(ИнформацияОбОшибке())); 201 | ТекстИсключения = СтрПолучитьСтроку(ТекстИсключения, СтрЧислоСтрок(ТекстИсключения)); 202 | КонецПопытки; 203 | 204 | Если КодСостояния = КодыСостоянияHTTP.ОК_200 Тогда 205 | Прервать; 206 | КонецЕсли; 207 | 208 | ПрошлоСекунд = ТекущаяУниверсальнаяДата() - ВремяНачала; 209 | Если ПрошлоСекунд > ТаймаутОжидания Или ЗначениеЗаполнено(КодСостояния) Тогда 210 | 211 | ТекстОшибки = СтрШаблон( 212 | "Не удалось запустить веб-сервер по адресу %1:%2 в течение %3 сек.", 213 | НастройкиВебСервера.ИмяХоста, 214 | НастройкиВебСервера.Порт, 215 | ТаймаутОжидания); 216 | 217 | Если ЗначениеЗаполнено(КодСостояния) Тогда 218 | ТекстОшибки = СтрШаблон("%1: 219 | |%2", ТекстОшибки, КодыСостоянияHTTP.Представление(КодСостояния)); 220 | Иначе 221 | ТекстОшибки = СтрШаблон("%1: 222 | |%2", ТекстОшибки, ТекстИсключения); 223 | КонецЕсли; 224 | 225 | ВызватьИсключение ТекстОшибки; 226 | 227 | КонецЕсли; 228 | 229 | Приостановить(ЗадержкаМеждуПопытками); 230 | 231 | КонецЦикла; 232 | 233 | КонецПроцедуры 234 | 235 | Процедура УстановитьКорневойКаталог() 236 | ТекущийКаталог = Новый Файл(ОбъединитьПути(ТекущийСценарий().Каталог, "../../..")).ПолноеИмя; 237 | УстановитьТекущийКаталог(ТекущийКаталог); 238 | КонецПроцедуры 239 | 240 | #КонецОбласти -------------------------------------------------------------------------------- /src/internal/Классы/ПомощникПодготовкиОтветов.os: -------------------------------------------------------------------------------- 1 | // BSLLS:ExportVariables-off 2 | 3 | #Использовать 1connector 4 | 5 | Перем ТекстRobots Экспорт; // Строка 6 | Перем ASCII_Deny Экспорт; // Строка 7 | Перем ASCII_Teapot; // Строка 8 | Перем АдресРедиректа; // Строка 9 | Перем ПоддерживаемыеМедиаТипы; // Массив из Строка 10 | 11 | Перем НастройкиВебСервера; // Ссылка на объект Настройки (winow) 12 | Перем Парсеры; // Ссылка на объект Парсеры (winow) 13 | Перем ЭтоНативныйВебСервер; // Булево 14 | 15 | #Область ПрограммныйИнтерфейс 16 | 17 | // Заполняет ответ json данными 18 | // 19 | // Параметры: 20 | // Ответ - winow.Ответ - Ответ 21 | // Данные - Соответствие, Структура - Данные для передачи в тело ответа 22 | // АлгоритмСжатия - Строка - Алгоритм сжатия данных (gzip, deflate, brotli, zstd) 23 | Процедура ЗаполнитьОтветJson(Ответ, Данные, АлгоритмСжатия = Неопределено) Экспорт 24 | 25 | Ответ.УстановитьТипКонтента("json"); 26 | 27 | ТекстJson = ВJson(Данные); 28 | 29 | Если АлгоритмСжатия = "gzip" Тогда 30 | 31 | Компрессор = Новый GZipКомпрессор(); 32 | Ответ.Заголовки["Content-Encoding"] = "gzip"; 33 | Ответ.ТелоДвоичныеДанные = Компрессор.Упаковать(ПолучитьДвоичныеДанныеИзСтроки(ТекстJson)); 34 | 35 | ИначеЕсли АлгоритмСжатия = "deflate" Тогда 36 | 37 | Компрессор = Новый DeflateКомпрессор(); 38 | Ответ.Заголовки["Content-Encoding"] = "deflate"; 39 | Ответ.ТелоДвоичныеДанные = Компрессор.Упаковать(ПолучитьДвоичныеДанныеИзСтроки(ТекстJson)); 40 | 41 | ИначеЕсли АлгоритмСжатия = "brotli" Тогда 42 | 43 | Компрессор = Новый BrotliКомпрессор(); 44 | Ответ.Заголовки["Content-Encoding"] = "br"; 45 | Ответ.ТелоДвоичныеДанные = Компрессор.Упаковать(ПолучитьДвоичныеДанныеИзСтроки(ТекстJson)); 46 | 47 | ИначеЕсли АлгоритмСжатия = "zstd" Тогда 48 | 49 | Компрессор = Новый ZStdКомпрессор(); 50 | Ответ.Заголовки["Content-Encoding"] = "zstd"; 51 | Ответ.ТелоДвоичныеДанные = Компрессор.Упаковать(ПолучитьДвоичныеДанныеИзСтроки(ТекстJson)); 52 | 53 | Иначе 54 | 55 | Ответ.ТелоТекст = ТекстJson; 56 | 57 | КонецЕсли; 58 | 59 | КонецПроцедуры 60 | 61 | // Заполняет ответ по состоянию 62 | // 63 | // Параметры: 64 | // Ответ - winow.Ответ - Ответ 65 | // КодСостояния - Число - Код состояния HTTP 66 | Процедура ЗаполнитьОтветПоСостоянию(Ответ, КодСостояния) Экспорт 67 | 68 | Ответ.УстановитьСостояние(КодСостояния); 69 | 70 | Если КодСостояния >= КодыСостоянияHTTP.ПеремещеноНавсегда_301 71 | И КодСостояния <= КодыСостоянияHTTP.ВременноеПеренаправление_307 72 | И Не КодСостояния = КодыСостоянияHTTP.НеИзменялось_304 Тогда 73 | 74 | Ответ.Заголовки["Location"] = АдресРедиректа; 75 | 76 | ИначеЕсли КодСостояния = КодыСостоянияHTTP.НеАвторизован_401 Тогда 77 | 78 | Ответ.Заголовки["WWW-Authenticate"] = "Basic realm=""Fake Realm"""; 79 | 80 | ИначеЕсли КодСостояния = КодыСостоянияHTTP.НеобходимаОплата_402 Тогда 81 | 82 | Ответ.ТелоТекст = "Fuck you, pay me!"; 83 | Ответ.Заголовки["x-more-info"] = "http://vimeo.com/22053820"; 84 | 85 | ИначеЕсли КодСостояния = КодыСостоянияHTTP.Неприемлемо_406 Тогда 86 | 87 | Данные = Новый Структура(); 88 | Данные.Вставить("message", "Client did not request a supported media type."); 89 | Данные.Вставить("accept", ПоддерживаемыеМедиаТипы); 90 | 91 | ЗаполнитьОтветJson(Ответ, Данные); 92 | 93 | ИначеЕсли КодСостояния = КодыСостоянияHTTP.НеобходимаАутентификацияПрокси_407 Тогда 94 | 95 | Ответ.Заголовки["Proxy-Authenticate"] = "Basic realm=""Fake Realm"""; 96 | 97 | ИначеЕсли КодСостояния = 418 Тогда 98 | 99 | Ответ.ТелоТекст = ASCII_Teapot; 100 | Ответ.СостояниеТекст = "I'm a teapot"; 101 | Ответ.Заголовки["x-more-info"] = "http://tools.ietf.org/html/rfc2324"; 102 | Ответ.Заголовки.Удалить("Content-Type"); 103 | 104 | КонецЕсли; 105 | 106 | КонецПроцедуры 107 | 108 | // Данные входящего запроса 109 | // 110 | // Параметры: 111 | // Ключи - Строка - Ключи через запятую 112 | // Запрос - winow.ВходящийЗапрос - Входящий запрос 113 | // ДанныеФормы - winow.ДанныеСоставнойФормы - Данные составной формы (multipart) 114 | // 115 | // Возвращаемое значение: 116 | // Структура 117 | Функция ПолучитьДанныеЗапроса(Ключи, Запрос, ДанныеФормы = Неопределено) Экспорт 118 | 119 | ДанныеФормы = РазделитьДанныеФормы(ДанныеФормы); 120 | ТелоЗапросОбъект = Запрос.ТелоЗапросОбъект(); 121 | Данные = ""; 122 | 123 | ТипКонтента = НРег(ЗначениеЗаголовка(Запрос.Заголовки, "Content-Type")); 124 | Если СтрНайти(ТипКонтента, "application/x-www-form-urlencoded") Тогда 125 | ДанныеФормы.Данные = Парсеры.ПараметрыИзТекста(Запрос.Тело); 126 | ИначеЕсли Не ЗначениеЗаполнено(ДанныеФормы.Данные) И Не ЗначениеЗаполнено(ДанныеФормы.Файлы) Тогда 127 | Данные = Запрос.Тело; 128 | КонецЕсли; 129 | 130 | ДанныеЗапроса = Новый Структура(); 131 | ДанныеЗапроса.Вставить("url", ПолучитьURL(Запрос.ПолныйПуть)); 132 | ДанныеЗапроса.Вставить("method", Запрос.Метод); 133 | ДанныеЗапроса.Вставить("args", Запрос.ПараметрыИменные); 134 | ДанныеЗапроса.Вставить("headers", ЗаголовкиДляJson(Запрос.Заголовки)); 135 | ДанныеЗапроса.Вставить("origin", IPАдресУдаленногоУзла(Запрос)); 136 | ДанныеЗапроса.Вставить("json", ?(Не ТелоЗапросОбъект = Неопределено, ТелоЗапросОбъект, null)); 137 | ДанныеЗапроса.Вставить("data", Данные); 138 | ДанныеЗапроса.Вставить("form", ДанныеФормы.Данные); 139 | ДанныеЗапроса.Вставить("files", ДанныеФормы.Файлы); 140 | ДанныеЗапроса.Вставить("gzipped", Истина); 141 | ДанныеЗапроса.Вставить("deflated", Истина); 142 | ДанныеЗапроса.Вставить("brotli", Истина); 143 | ДанныеЗапроса.Вставить("zstd", Истина); 144 | 145 | Результат = СкопироватьСтруктуру(ДанныеЗапроса, Ключи); 146 | 147 | Возврат Результат; 148 | 149 | КонецФункции 150 | 151 | // Данные аутентификации из входящего запроса 152 | // 153 | // Параметры: 154 | // Запрос - winow.ВходящийЗапрос - Входящий запрос 155 | // 156 | // Возвращаемое значение: 157 | // Структура: 158 | // Общее: 159 | // * Тип - Строка - Тип аутентификации (basic, bearer) 160 | // Basic: 161 | // * ИмяПользователя - Строка, Неопределено - Имя пользователя 162 | // * Пароль - Строка, Неопределено - Пароль 163 | // Bearer: 164 | // * Токен - Строка - Токен 165 | Функция ДанныеАутентификации(Запрос) Экспорт 166 | 167 | Данные = Новый Структура("Тип", ""); 168 | 169 | Заголовок = ЗначениеЗаголовка(Запрос.Заголовки, "Authorization"); 170 | Если ПустаяСтрока(Заголовок) Тогда 171 | Возврат Данные; 172 | КонецЕсли; 173 | 174 | РазделеннаяСтрока = РазделитьСтроку(Заголовок, " "); 175 | 176 | ТипАутентификации = НРег(РазделеннаяСтрока.Лево); 177 | Данные.Тип = ТипАутентификации; 178 | 179 | Значение = РазделеннаяСтрока.Право; 180 | 181 | Если ТипАутентификации = "basic" Тогда 182 | 183 | Данные.Вставить("ИмяПользователя"); 184 | Данные.Вставить("Пароль"); 185 | 186 | Попытка 187 | ПользовательИПароль = ПолучитьСтрокуИзДвоичныхДанных(Base64Значение(Значение)); 188 | ПользовательИПароль = РазделитьСтроку(ПользовательИПароль, ":"); 189 | 190 | Данные.ИмяПользователя = ПользовательИПароль.Лево; 191 | Данные.Пароль = ПользовательИПароль.Право; 192 | Исключение 193 | Данные.ИмяПользователя = Неопределено; 194 | Данные.Пароль = Неопределено; 195 | КонецПопытки; 196 | 197 | ИначеЕсли ТипАутентификации = "bearer" Тогда 198 | 199 | Данные.Вставить("Токен", Значение); 200 | 201 | КонецЕсли; 202 | 203 | Возврат Данные; 204 | 205 | КонецФункции 206 | 207 | // Значение заголовка по имени 208 | // 209 | // Параметры: 210 | // Заголовки - Соответствие, СловарьЗаголовков - Коллекция заголовков 211 | // Имя - Строка - Имя заголовка 212 | // 213 | // Возвращаемое значение: 214 | // Строка, Неопределено 215 | Функция ЗначениеЗаголовка(Заголовки, Знач Имя) Экспорт 216 | 217 | Имя = НРег(Имя); 218 | 219 | Для Каждого Заголовок Из Заголовки Цикл 220 | Если НРег(Заголовок.Ключ) = Имя Тогда 221 | Если ТипЗнч(Заголовок.Значение) = Тип("Строка") 222 | Или ТипЗнч(Заголовок.Значение) = Тип("Соответствие") Тогда 223 | Возврат Заголовок.Значение; 224 | ИначеЕсли Заголовок.Значение.Количество() Тогда 225 | Возврат Заголовок.Значение[0]; 226 | Иначе 227 | Возврат Неопределено; 228 | КонецЕсли; 229 | КонецЕсли; 230 | КонецЦикла; 231 | 232 | КонецФункции 233 | 234 | // Парсит многозначный заголовок 235 | // 236 | // Параметры: 237 | // Значение - Строка - Значение заголовка 238 | // 239 | // Возвращаемое значение: 240 | // Массив из Строка - Массив значений 241 | Функция РаспаристьМногозначныйЗаголовок(Значение) Экспорт 242 | 243 | Результат = Новый Массив(); 244 | 245 | РегулярноеВыражение = Новый РегулярноеВыражение("\s*(W\/)?\""?([^""]*)\""?\s*"); 246 | Подстроки = СтрРазделить(Значение, ","); 247 | 248 | Для Каждого Подстрока Из Подстроки Цикл 249 | КоллекцияСовпадений = РегулярноеВыражение.НайтиСовпадения(Подстрока); 250 | 251 | Для Каждого Совпадение Из КоллекцияСовпадений Цикл 252 | НайденноеЗначение = Совпадение.Группы[2].Значение; 253 | Если ЗначениеЗаполнено(НайденноеЗначение) Тогда 254 | Результат.Добавить(НайденноеЗначение); 255 | КонецЕсли; 256 | КонецЦикла; 257 | КонецЦикла; 258 | 259 | Возврат Результат; 260 | 261 | КонецФункции 262 | 263 | // Получает URL к запущенному веб-серверу с добавлением пути 264 | // 265 | // Параметры: 266 | // ПолныйПуть - Строка - Путь после порта веб-сервера, включая "/" 267 | // 268 | // Возвращаемое значение: 269 | // Строка - URL 270 | Функция ПолучитьURL(ПолныйПуть) Экспорт 271 | 272 | Возврат "http://" 273 | + НастройкиВебСервера.ИмяХоста 274 | + ?(НастройкиВебСервера.Порт = 80, "", ":" + Формат(НастройкиВебСервера.Порт, "ЧГ=")) 275 | + ПолныйПуть; 276 | 277 | КонецФункции 278 | 279 | // Преобразует универсальную дату в дату формата rfc1123-date. 280 | // См. https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html, п. 3.3.1. 281 | // 282 | // Параметры: 283 | // Дата - Дата, Неопределено - Если не указано, то беретеся текущая универсальная дата 284 | // 285 | // Возвращаемое значение: 286 | // Строка 287 | Функция ДатаHTTP(Знач Дата = Неопределено) Экспорт 288 | 289 | Если Дата = Неопределено Тогда 290 | Дата = ТекущаяУниверсальнаяДата(); 291 | КонецЕсли; 292 | 293 | Возврат Формат(Дата, "Л=en; ДФ='ддд, дд МММ гггг ЧЧ:мм:сс ''GMT'''"); 294 | 295 | КонецФункции 296 | 297 | // Получает число из строки 298 | // 299 | // Параметры: 300 | // Значение - Строка - Число строкой 301 | // 302 | // Возвращаемое значение: 303 | // Число 304 | Функция ВЧисло(Значение) Экспорт 305 | Возврат Новый ОписаниеТипов("Число").ПривестиЗначение(Значение); 306 | КонецФункции 307 | 308 | // Дополняет соответствие 309 | // 310 | // Параметры: 311 | // Приемник - Соответствие - Приемник 312 | // Источник - Соответствие - Источник 313 | Процедура ДополнитьСоответствие(Приемник, Источник) Экспорт 314 | 315 | Для Каждого Элемент Из Источник Цикл 316 | Приемник.Вставить(Элемент.Ключ, Элемент.Значение); 317 | КонецЦикла; 318 | 319 | КонецПроцедуры 320 | 321 | // Выбирает случайный элемент с учетом веса 322 | // 323 | // Параметры: 324 | // Список - Массив из Структура - Взвешенный список: 325 | // * Значение - Произвольный - Значение 326 | // * Вес - Число - Вес 327 | // 328 | // Возвращаемое значение: 329 | // Произвольный - Случайное значение из списка 330 | Функция ВыбратьСлучайныйЭлементСУчетомВеса(Список) Экспорт 331 | 332 | ОбщийВес = 0; 333 | НакопленныеВеса = Новый Массив(); 334 | Для Каждого Элемент Из Список Цикл 335 | ОбщийВес = ОбщийВес + Элемент.Вес; 336 | НакопленныеВеса.Добавить(ОбщийВес); 337 | КонецЦикла; 338 | 339 | ГСЧ = Новый ГенераторСлучайныхЧисел(); 340 | СлучайноеЧисло = ГСЧ.СлучайноеЧисло(0, ОбщийВес); 341 | 342 | НижняяГраница = 0; 343 | ВерхняяГраница = Список.Количество(); 344 | Пока НижняяГраница < ВерхняяГраница Цикл 345 | Середина = Цел((НижняяГраница + ВерхняяГраница) / 2); 346 | Если СлучайноеЧисло < НакопленныеВеса[Середина] Тогда 347 | ВерхняяГраница = Середина; 348 | Иначе 349 | НижняяГраница = Середина + 1; 350 | КонецЕсли; 351 | КонецЦикла; 352 | 353 | Возврат Список[НижняяГраница].Значение; 354 | 355 | КонецФункции 356 | 357 | // Разделяет строку по разделителю 358 | // 359 | // Параметры: 360 | // Строка - Строка 361 | // Разделитель - Строка 362 | // 363 | // Возвращаемое значение: 364 | // Структура: 365 | // * Лево - Строка 366 | // * Право - Строка 367 | Функция РазделитьСтроку(Строка, Разделитель) Экспорт 368 | 369 | Результат = Новый Структура("Лево, Право", "", ""); 370 | ПозицияРазделителя = СтрНайти(Строка, Разделитель); 371 | 372 | Если ПозицияРазделителя = 0 Тогда 373 | Результат.Лево = Строка; 374 | Иначе 375 | Результат.Лево = Лев(Строка, ПозицияРазделителя - 1); 376 | Результат.Право = Сред(Строка, ПозицияРазделителя + СтрДлина(Разделитель)); 377 | КонецЕсли; 378 | 379 | Возврат Результат; 380 | 381 | КонецФункции 382 | 383 | #КонецОбласти 384 | 385 | #Область СлужебныеПроцедурыИФункции 386 | 387 | &Желудь 388 | Процедура ПриСозданииОбъекта( 389 | &Пластилин("Настройки") Настройки, 390 | &Пластилин("Парсеры") _Парсеры, 391 | &Пластилин("ВебСервер") _ВебСервер) 392 | 393 | НастройкиВебСервера = Настройки; 394 | Парсеры = _Парсеры; 395 | ЭтоНативныйВебСервер = ТипЗнч(_ВебСервер) = Тип("НативныйВебСервер"); 396 | 397 | ЗаполнитьПоддерживаемыеМедиаТипы(); 398 | АдресРедиректа = "/redirect/1"; 399 | 400 | ТекстRobots = "User-agent: * 401 | |Disallow: /deny"; 402 | 403 | ASCII_Deny = " 404 | | .-''''''-. 405 | | .' _ _ '. 406 | | / O O \ 407 | | : : 408 | | | | 409 | | : __ : 410 | | \ .-""` `""-. / 411 | | '. .' 412 | | '-......-' 413 | | YOU SHOULDN'T BE HERE"; 414 | 415 | ASCII_Teapot = " 416 | | -=[ teapot ]=- 417 | | 418 | | _...._ 419 | | .' _ _ `. 420 | | | .""` ^ `"". _, 421 | | \_;`""---""`|// 422 | | | ;/ 423 | | \_ _/ 424 | | `""""""`"; 425 | 426 | КонецПроцедуры 427 | 428 | Функция IPАдресУдаленногоУзла(Запрос) 429 | 430 | Адрес = ЗначениеЗаголовка(Запрос.Заголовки, "X-Forwarded-For"); 431 | Если Не ЗначениеЗаполнено(Адрес) Тогда 432 | Адрес = Запрос.АдресУдаленногоУзла; 433 | КонецЕсли; 434 | 435 | Подстрока = "::ffff:"; // BSLLS:UsingHardcodeNetworkAddress-off 436 | Если СтрНачинаетсяС(Адрес, Подстрока) Тогда 437 | Адрес = Сред(Адрес, СтрДлина(Подстрока) + 1); 438 | КонецЕсли; 439 | 440 | Возврат Адрес; 441 | 442 | КонецФункции 443 | 444 | Функция РазделитьДанныеФормы(ДанныеФормы) 445 | 446 | Результат = Новый Структура(); 447 | Результат.Вставить("Данные", Новый Соответствие()); 448 | Результат.Вставить("Файлы", Новый Соответствие()); 449 | 450 | Если ДанныеФормы = Неопределено Тогда 451 | Возврат Результат; 452 | КонецЕсли; 453 | 454 | Для Индекс = 0 По ДанныеФормы.Количество() - 1 Цикл 455 | 456 | Строка = ДанныеФормы.ПолучитьПоИндексу(Индекс); 457 | 458 | Если ЭтоНативныйВебСервер Тогда 459 | 460 | МетаданныеКонтента = Строка.Метаданные; 461 | 462 | Имя = МетаданныеКонтента["name"]; 463 | Если Не Имя = Неопределено Тогда 464 | Имя = Имя["name"]; 465 | КонецЕсли; 466 | 467 | ИмяФайла = МетаданныеКонтента["filename"]; 468 | Если Не ИмяФайла = Неопределено Тогда 469 | ИмяФайла = ИмяФайла["filename"]; 470 | КонецЕсли; 471 | 472 | Иначе 473 | 474 | МетаданныеКонтента = ЗначениеЗаголовка(Строка.Метаданные, "Content-Disposition"); 475 | 476 | Если МетаданныеКонтента = Неопределено Тогда 477 | Продолжить; 478 | КонецЕсли; 479 | 480 | Имя = МетаданныеКонтента["name"]; 481 | ИмяФайла = МетаданныеКонтента["filename"]; 482 | 483 | КонецЕсли; 484 | 485 | Значение = Неопределено; 486 | 487 | Если ИмяФайла = Неопределено Тогда 488 | 489 | Контейнер = Результат.Данные; 490 | 491 | Если Не Строка.Значение = Неопределено Тогда 492 | Значение = ПолучитьСтрокуИзДвоичныхДанных(Строка.Значение, КодировкаТекста.UTF8); 493 | КонецЕсли; 494 | 495 | Иначе 496 | 497 | Контейнер = Результат.Файлы; 498 | 499 | Заголовки = Строка.Метаданные["Заголовки"]; 500 | Если Не Заголовки = Неопределено Тогда 501 | ТипКонтента = ЗначениеЗаголовка(Заголовки, "Content-Type"); 502 | Иначе 503 | ТипКонтента = Неопределено; 504 | КонецЕсли; 505 | 506 | Если Не Строка.Значение = Неопределено Тогда 507 | ТипКонтента = ?(ТипКонтента = Неопределено, "application/octet-stream", ТипКонтента); 508 | Значение = ТекстовоеПредставлениеДвоичныхДанных(Строка.Значение, ТипКонтента); 509 | КонецЕсли; 510 | 511 | КонецЕсли; 512 | 513 | НайденноеЗначение = Контейнер[Имя]; 514 | Если НайденноеЗначение = Неопределено Тогда 515 | Контейнер.Вставить(Имя, Значение); 516 | ИначеЕсли ТипЗнч(НайденноеЗначение) = Тип("Массив") Тогда 517 | НайденноеЗначение.Добавить(Значение); 518 | Иначе 519 | МассивЗначений = Новый Массив(); 520 | МассивЗначений.Добавить(НайденноеЗначение); 521 | МассивЗначений.Добавить(Значение); 522 | Контейнер.Вставить(Имя, МассивЗначений); 523 | КонецЕсли; 524 | 525 | КонецЦикла; 526 | 527 | Возврат Результат; 528 | 529 | КонецФункции 530 | 531 | Функция ТекстовоеПредставлениеДвоичныхДанных(ДвоичныеДанные, ТипКонтента) 532 | 533 | Если ДвоичныеДанныеСодержатУправляющиеСимволы(ДвоичныеДанные) Тогда 534 | 535 | Возврат СтрШаблон("data:%1;base64,%2", ТипКонтента, Base64Строка(ДвоичныеДанные)); 536 | 537 | Иначе 538 | 539 | Возврат ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, КодировкаТекста.UTF8); 540 | 541 | КонецЕсли; 542 | 543 | КонецФункции 544 | 545 | Функция ДвоичныеДанныеСодержатУправляющиеСимволы(ДвоичныеДанные) 546 | 547 | КоличествоЧитаемыхБайтов = 1024; 548 | 549 | NUL = 0; 550 | BS = 8; 551 | CR = 13; 552 | SUB = 26; 553 | 554 | ЧтениеДанных = Новый ЧтениеДанных(ДвоичныеДанные); 555 | 556 | СчетчикБайтов = 0; 557 | Пока Истина Цикл 558 | 559 | Если ЧтениеДанных.ЧтениеЗавершено Или СчетчикБайтов = КоличествоЧитаемыхБайтов Тогда 560 | Прервать; 561 | КонецЕсли; 562 | 563 | Байт = ЧтениеДанных.ПрочитатьБайт(); 564 | 565 | Если Байт > NUL И Байт < BS Или Байт > CR И Байт < SUB Тогда 566 | Возврат Истина; 567 | КонецЕсли; 568 | 569 | СчетчикБайтов = СчетчикБайтов + 1; 570 | 571 | КонецЦикла; 572 | 573 | Возврат Ложь; 574 | 575 | КонецФункции 576 | 577 | Функция ЗаголовкиДляJson(Заголовки) 578 | 579 | Если ЭтоНативныйВебСервер Тогда 580 | 581 | НовыеЗаголовки = Новый Соответствие(); 582 | 583 | Для Каждого КлючИЗначение Из Заголовки Цикл 584 | 585 | Значение = Заголовки[КлючИЗначение.Ключ]; 586 | 587 | Если ТипЗнч(Значение) = Тип("СтроковыеЗначения") Тогда 588 | НовыеЗаголовки[КлючИЗначение.Ключ] = Значение[0]; 589 | Иначе 590 | НовыеЗаголовки[КлючИЗначение.Ключ] = Значение; 591 | КонецЕсли; 592 | 593 | КонецЦикла; 594 | 595 | Возврат НовыеЗаголовки; 596 | 597 | Иначе 598 | 599 | Возврат Заголовки; 600 | 601 | КонецЕсли; 602 | 603 | КонецФункции 604 | 605 | Функция ВJson(Данные) 606 | 607 | ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, " "); 608 | 609 | ЗаписьJSON = Новый ЗаписьJSON(); 610 | ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписиJSON); 611 | ЗаписатьJSON(ЗаписьJSON, Данные); 612 | 613 | Возврат ЗаписьJSON.Закрыть(); 614 | 615 | КонецФункции 616 | 617 | Функция СкопироватьСтруктуру(ИсходнаяСтруктура, КопируемыеКлючи) 618 | 619 | Результат = Новый Структура(КопируемыеКлючи); 620 | ЗаполнитьЗначенияСвойств(Результат, ИсходнаяСтруктура); 621 | 622 | Для Каждого КлючИЗначение Из Результат Цикл 623 | Если Результат[КлючИЗначение.Ключ] = NULL Тогда 624 | Результат[КлючИЗначение.Ключ] = ""; 625 | КонецЕсли; 626 | КонецЦикла; 627 | 628 | Возврат Результат; 629 | 630 | КонецФункции 631 | 632 | Процедура ЗаполнитьПоддерживаемыеМедиаТипы() 633 | 634 | ПоддерживаемыеМедиаТипы = Новый Массив(); 635 | ПоддерживаемыеМедиаТипы.Добавить("image/webp"); 636 | ПоддерживаемыеМедиаТипы.Добавить("image/svg+xml"); 637 | ПоддерживаемыеМедиаТипы.Добавить("image/jpeg"); 638 | ПоддерживаемыеМедиаТипы.Добавить("image/png"); 639 | ПоддерживаемыеМедиаТипы.Добавить("image/*"); 640 | 641 | КонецПроцедуры 642 | 643 | #КонецОбласти -------------------------------------------------------------------------------- /tasks/coverage.os: -------------------------------------------------------------------------------- 1 | #Использовать 1commands 2 | #Использовать fs 3 | #Использовать coverage 4 | 5 | СистемнаяИнформация = Новый СистемнаяИнформация; 6 | ЭтоWindows = Найти(НРег(СистемнаяИнформация.ВерсияОС), "windows") > 0; 7 | 8 | ФС.ОбеспечитьПустойКаталог("out"); 9 | ПутьКСтат = "out/stat.json"; 10 | 11 | Команда = Новый Команда; 12 | Команда.УстановитьКоманду("oscript"); 13 | Если НЕ ЭтоWindows Тогда 14 | Команда.ДобавитьПараметр("-encoding=utf-8"); 15 | КонецЕсли; 16 | Команда.ДобавитьПараметр(СтрШаблон("-codestat=%1", ПутьКСтат)); 17 | Команда.ДобавитьПараметр("tasks/test.os"); // Файла запуска тестов 18 | Команда.ПоказыватьВыводНемедленно(Истина); 19 | 20 | КодВозврата = Команда.Исполнить(); 21 | 22 | ПроцессорГенерации = Новый ГенераторОтчетаПокрытия(); 23 | 24 | ПроцессорГенерации.ОтносительныеПути() 25 | .РабочийКаталог("out") 26 | .ИмяФайлаСтатистики() 27 | .GenericCoverage() 28 | .Cobertura() 29 | .Сформировать(); 30 | 31 | ЗавершитьРаботу(КодВозврата); -------------------------------------------------------------------------------- /tasks/oscript.cfg: -------------------------------------------------------------------------------- 1 | lib.system=../oscript_modules -------------------------------------------------------------------------------- /tasks/test.os: -------------------------------------------------------------------------------- 1 | #Использовать 1testrunner 2 | #Использовать fs 3 | 4 | Функция ПрогнатьТесты() 5 | 6 | Тестер = Новый Тестер; 7 | Тестер.УстановитьФорматЛогФайла(Тестер.ФорматыЛогФайла().GenericExec); 8 | 9 | ПутьКТестам = "tests"; 10 | ПутьКОтчетуJUnit = "out"; 11 | 12 | ФС.ОбеспечитьПустойКаталог(ПутьКОтчетуJUnit); 13 | 14 | РезультатТестирования = Тестер.ТестироватьКаталог( 15 | Новый Файл(ПутьКТестам), 16 | Новый Файл(ПутьКОтчетуJUnit)); 17 | 18 | Успешно = РезультатТестирования = 0; 19 | 20 | Возврат Успешно; 21 | 22 | КонецФункции 23 | 24 | ТекКаталог = ТекущийКаталог(); 25 | 26 | Попытка 27 | ТестыПрошли = ПрогнатьТесты(); 28 | Исключение 29 | ТестыПрошли = Ложь; 30 | Сообщить(СтрШаблон("Тесты через 1testrunner выполнены неудачно 31 | |%1", ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()))); 32 | КонецПопытки; 33 | 34 | УстановитьТекущийКаталог(ТекКаталог); 35 | 36 | Если Не ТестыПрошли Тогда 37 | ВызватьИсключение "Тестирование завершилось неудачно!"; 38 | Иначе 39 | Сообщить(СтрШаблон("Результат прогона тестов <%1> 40 | |", ТестыПрошли)); 41 | КонецЕсли; -------------------------------------------------------------------------------- /tests/HttpBin_API_test.os: -------------------------------------------------------------------------------- 1 | // BSLLS:LatinAndCyrillicSymbolInWord-off 2 | 3 | #Использовать asserts 4 | #Использовать 1connector 5 | #Использовать compressor 6 | #Использовать ".." 7 | 8 | Перем HttpBin; // см. HttpBin 9 | Перем СервисЗапущен; 10 | 11 | Процедура ПередЗапускомТестов() Экспорт 12 | 13 | СервисЗапущен = Ложь; 14 | 15 | HttpBin = Новый HttpBin(); 16 | HttpBin.Запустить(); 17 | 18 | СервисЗапущен = Истина; 19 | 20 | КонецПроцедуры 21 | 22 | Процедура ПослеЗапускаТестов() Экспорт 23 | HttpBin.Остановить(); 24 | КонецПроцедуры 25 | 26 | Процедура ПередЗапускомТеста() Экспорт 27 | Если Не СервисЗапущен Тогда 28 | ВызватьИсключение "Сервис не запущен"; 29 | КонецЕсли; 30 | КонецПроцедуры 31 | 32 | &Тест 33 | Процедура Должен_ПроверитьТочкуМаршрута_Html() Экспорт 34 | 35 | РазмерДанныхБайт = 3741; 36 | 37 | Ответ = ВызватьМетодGET("html"); 38 | 39 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 40 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("text/html"); 41 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер()).Равно(РазмерДанныхБайт); 42 | 43 | КонецПроцедуры 44 | 45 | &Тест 46 | Процедура Должен_ПроверитьТочкуМаршрута_Robots() Экспорт 47 | 48 | РазмерДанныхБайт = 29; 49 | 50 | Ответ = ВызватьМетодGET("robots.txt"); 51 | 52 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 53 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("text/html"); 54 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер()).Равно(РазмерДанныхБайт); 55 | 56 | КонецПроцедуры 57 | 58 | &Тест 59 | Процедура Должен_ПроверитьТочкуМаршрута_Deny() Экспорт 60 | 61 | РазмерДанныхБайт = 238; 62 | 63 | Ответ = ВызватьМетодGET("deny"); 64 | 65 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 66 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Не_().Заполнено(); 67 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер()).Равно(РазмерДанныхБайт); 68 | 69 | КонецПроцедуры 70 | 71 | &Тест 72 | Процедура Должен_ПроверитьТочкуМаршрута_IP() Экспорт 73 | 74 | Ответ = ВызватьМетодGET("ip"); 75 | 76 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 77 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 78 | Ожидаем.Что(Ответ.Json()["origin"]).Заполнено(); 79 | 80 | КонецПроцедуры 81 | 82 | &Тест 83 | Процедура Должен_ПроверитьТочкуМаршрута_Uuid() Экспорт 84 | 85 | Ответ = ВызватьМетодGET("uuid"); 86 | 87 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 88 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 89 | Ожидаем.Что(Ответ.Json()["uuid"]).Заполнено(); 90 | 91 | КонецПроцедуры 92 | 93 | &Тест 94 | Процедура Должен_ПроверитьТочкуМаршрута_Uuid5() Экспорт 95 | 96 | КоличествоUUID = 5; 97 | 98 | Ответ = ВызватьМетодGET("uuid/" + КоличествоUUID); 99 | 100 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 101 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 102 | Ожидаем.Что(Ответ.Json()["uuid"].Количество()).Равно(КоличествоUUID); 103 | 104 | КонецПроцедуры 105 | 106 | &Тест 107 | Процедура Должен_ПроверитьТочкуМаршрута_Headers() Экспорт 108 | 109 | Ответ = ВызватьМетодGET("headers"); 110 | 111 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 112 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 113 | Ожидаем.Что(Ответ.Json()["headers"]).Заполнено(); 114 | 115 | КонецПроцедуры 116 | 117 | &Тест 118 | Процедура Должен_ПроверитьТочкуМаршрута_UserAgent() Экспорт 119 | 120 | Заголовки = Новый Соответствие(); 121 | Заголовки.Вставить("User-Agent", "WINOW"); 122 | 123 | Ответ = ВызватьМетодGET("user-agent", Заголовки); 124 | 125 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 126 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 127 | Ожидаем.Что(Ответ.Json()["user-agent"]).Равно("WINOW"); 128 | 129 | КонецПроцедуры 130 | 131 | &Тест 132 | Процедура Должен_ПроверитьТочкуМаршрута_Get() Экспорт 133 | 134 | Заголовки = Новый Соответствие(); 135 | Заголовки.Вставить("User-Agent", "WINOW"); 136 | 137 | Ответ = ВызватьМетодGET("get?param=value"); 138 | Результат = Ответ.Json(); 139 | 140 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 141 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 142 | Ожидаем.Что(Результат["args"], "args").Заполнено(); 143 | Ожидаем.Что(Результат["headers"], "headers").Заполнено(); 144 | Ожидаем.Что(Результат["origin"], "origin").Заполнено(); 145 | Ожидаем.Что(Результат["url"], "url").Заполнено(); 146 | 147 | КонецПроцедуры 148 | 149 | &Тест 150 | Процедура Должен_ПроверитьТочкуМаршрута_Anything_GET() Экспорт 151 | 152 | Ответ = ВызватьМетодGET("anything"); 153 | Результат = Ответ.Json(); 154 | 155 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 156 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 157 | Ожидаем.Что(Результат["method"]).Равно("GET"); 158 | 159 | КонецПроцедуры 160 | 161 | &Тест 162 | Процедура Должен_ПроверитьТочкуМаршрута_Anything_POST() Экспорт 163 | 164 | Ответ = ВызватьМетод("POST", "anything"); 165 | Результат = Ответ.Json(); 166 | 167 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 168 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 169 | Ожидаем.Что(Результат["method"]).Равно("POST"); 170 | 171 | КонецПроцедуры 172 | 173 | &Тест 174 | Процедура Должен_ПроверитьТочкуМаршрута_Anything_PUT() Экспорт 175 | 176 | Ответ = ВызватьМетод("PUT", "anything"); 177 | Результат = Ответ.Json(); 178 | 179 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 180 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 181 | Ожидаем.Что(Результат["method"]).Равно("PUT"); 182 | 183 | КонецПроцедуры 184 | 185 | &Тест 186 | Процедура Должен_ПроверитьТочкуМаршрута_Post_ТелоСтрока() Экспорт 187 | 188 | Тело = "name=Jack&age=20"; 189 | 190 | Заголовки = Новый Соответствие(); 191 | Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded"); 192 | 193 | ПараметрыКоннектора = ПараметрыКоннектора(Заголовки); 194 | Ответ = ВызватьМетод("POST", "post", Тело, ПараметрыКоннектора); 195 | Результат = Ответ.Json(); 196 | 197 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 198 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 199 | Ожидаем.Что(Результат["form"]).Заполнено(); 200 | Ожидаем.Что(Результат["form"]["name"]).Равно("Jack"); 201 | Ожидаем.Что(Результат["form"]["age"]).Равно("20"); 202 | 203 | КонецПроцедуры 204 | 205 | &Тест 206 | Процедура Должен_ПроверитьТочкуМаршрута_Post_ТелоСтрокаСКодировкой() Экспорт 207 | 208 | Тело = "name=Jack&age=20"; 209 | 210 | Заголовки = Новый Соответствие(); 211 | Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); 212 | 213 | ПараметрыКоннектора = ПараметрыКоннектора(Заголовки); 214 | Ответ = ВызватьМетод("POST", "post", Тело, ПараметрыКоннектора); 215 | Результат = Ответ.Json(); 216 | 217 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 218 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 219 | Ожидаем.Что(Результат["form"]).Заполнено(); 220 | Ожидаем.Что(Результат["form"]["name"]).Равно("Jack"); 221 | Ожидаем.Что(Результат["form"]["age"]).Равно("20"); 222 | 223 | КонецПроцедуры 224 | 225 | &Тест 226 | Процедура Должен_ПроверитьТочкуМаршрута_Post_ТелоДвоичныеДанные() Экспорт 227 | 228 | Тело = ПолучитьДвоичныеДанныеИзСтроки("name=Jack&age=20"); 229 | 230 | Заголовки = Новый Соответствие(); 231 | Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded"); 232 | 233 | ПараметрыКоннектора = ПараметрыКоннектора(Заголовки); 234 | Ответ = ВызватьМетод("POST", "post", Тело, ПараметрыКоннектора); 235 | Результат = Ответ.Json(); 236 | 237 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 238 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 239 | Ожидаем.Что(Результат["form"]).Заполнено(); 240 | Ожидаем.Что(Результат["form"]["name"]).Равно("Jack"); 241 | Ожидаем.Что(Результат["form"]["age"]).Равно("20"); 242 | 243 | КонецПроцедуры 244 | 245 | &Тест 246 | Процедура Должен_ПроверитьТочкуМаршрута_Post_ТелоСтрокаJson() Экспорт 247 | 248 | Тело = "{ 'name': 'Jack', 'age': 20 }"; 249 | 250 | Заголовки = Новый Соответствие(); 251 | Заголовки.Вставить("Content-Type", "application/json"); 252 | 253 | ПараметрыКоннектора = ПараметрыКоннектора(Заголовки); 254 | Ответ = ВызватьМетод("POST", "post", Тело, ПараметрыКоннектора); 255 | Результат = Ответ.Json(); 256 | 257 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 258 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 259 | Ожидаем.Что(Результат["json"]).Заполнено(); 260 | Ожидаем.Что(Результат["json"]["name"]).Равно("Jack"); 261 | Ожидаем.Что(Результат["json"]["age"]).Равно(20); 262 | 263 | КонецПроцедуры 264 | 265 | &Тест 266 | Процедура Должен_ПроверитьТочкуМаршрута_Post_ТелоСтрокаБезТипа() Экспорт 267 | 268 | Тело = "HTTPBIN"; 269 | 270 | Заголовки = Новый Соответствие(); 271 | Заголовки.Вставить("Content-Type", "text/html"); 272 | 273 | ПараметрыКоннектора = ПараметрыКоннектора(Заголовки); 274 | 275 | Ответ = ВызватьМетод("POST", "post", Тело, ПараметрыКоннектора); 276 | Результат = Ответ.Json(); 277 | 278 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 279 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 280 | Ожидаем.Что(Результат["data"]).Равно(Тело); 281 | 282 | КонецПроцедуры 283 | 284 | &Тест 285 | Процедура Должен_ПроверитьТочкуМаршрута_Post_Multipart() Экспорт 286 | 287 | Данные = Новый Соответствие(); 288 | Данные.Вставить("Name", "Jack"); 289 | 290 | Файлы = Новый Массив(); 291 | 292 | Файл = Новый Структура(); 293 | Файл.Вставить("Имя", "Text"); 294 | Файл.Вставить("Данные", ПолучитьДвоичныеДанныеИзСтроки("HTTPBIN")); 295 | Файл.Вставить("ИмяФайла", "httpbin.txt"); 296 | Файл.Вставить("Тип", "text/plain"); 297 | Файлы.Добавить(Файл); 298 | 299 | Файл = Новый Структура(); 300 | Файл.Вставить("Имя", "Binary"); 301 | Файл.Вставить("Данные", Base64Значение(Base64ZipФайла())); 302 | Файл.Вставить("ИмяФайла", "httpbin.zip"); 303 | Файл.Вставить("Тип", "application/octet-stream"); 304 | Файлы.Добавить(Файл); 305 | 306 | ПараметрыКоннектора = ПараметрыКоннектора(); 307 | ПараметрыКоннектора.Вставить("Файлы", Файлы); 308 | 309 | Ответ = ВызватьМетод("POST", "post", Данные, ПараметрыКоннектора); 310 | Результат = Ответ.Json(); 311 | 312 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 313 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 314 | Ожидаем.Что(Результат["form"]["Name"]).Равно("Jack"); 315 | Ожидаем.Что(Результат["files"]["Text"]).Равно("HTTPBIN"); 316 | 317 | ДанныеВDataURI = СтрШаблон("data:application/octet-stream;base64,%1", Base64ZipФайла()); 318 | Ожидаем.Что(Результат["files"]["Binary"]).Равно(ДанныеВDataURI); 319 | 320 | КонецПроцедуры 321 | 322 | &Тест 323 | Процедура Должен_ПроверитьТочкуМаршрута_Post_ПолеСНесколькимиЗначениямиMultipart() Экспорт 324 | 325 | РазделительMultipart = СтрЗаменить(Новый УникальныйИдентификатор, "-", ""); 326 | 327 | Заголовки = Новый Соответствие(); 328 | Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + РазделительMultipart); 329 | 330 | Соединение = Новый HTTPСоединение(HttpBin.Хост(), HttpBin.Порт()); 331 | HTTPЗапрос = Новый HTTPЗапрос("/post", Заголовки); 332 | Поток = HTTPЗапрос.ПолучитьТелоКакПоток(); 333 | 334 | РазделительСтрок = Символы.ВК + Символы.ПС; 335 | ЗаписьДанных = Новый ЗаписьДанных(Поток, , , "", ""); 336 | // Начало color 337 | ЗаписьДанных.ЗаписатьСтроку("--" + РазделительMultipart + РазделительСтрок); 338 | ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""color""" + РазделительСтрок); 339 | ЗаписьДанных.ЗаписатьСтроку(РазделительСтрок); 340 | ЗаписьДанных.ЗаписатьСтроку("Red" + РазделительСтрок); 341 | // Конец color 342 | // Начало color 343 | ЗаписьДанных.ЗаписатьСтроку("--" + РазделительMultipart + РазделительСтрок); 344 | ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""color""" + РазделительСтрок); 345 | ЗаписьДанных.ЗаписатьСтроку(РазделительСтрок); 346 | ЗаписьДанных.ЗаписатьСтроку("Green" + РазделительСтрок); 347 | // Конец color 348 | ЗаписьДанных.ЗаписатьСтроку("--" + РазделительMultipart + "--" + РазделительСтрок); 349 | ЗаписьДанных.Закрыть(); 350 | 351 | HTTPОтвет = Соединение.ВызватьHTTPМетод("POST", HTTPЗапрос); 352 | HTTPОтвет.ПолучитьТелоКакСтроку(); 353 | 354 | ЧтениеJSON = Новый ЧтениеJSON(); 355 | ЧтениеJSON.УстановитьСтроку(HTTPОтвет.ПолучитьТелоКакСтроку()); 356 | Json = ПрочитатьJSON(ЧтениеJSON, Истина); 357 | ЧтениеJSON.Закрыть(); 358 | 359 | Ожидаем.Что(HTTPОтвет.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 360 | Ожидаем.Что(HTTPОтвет.Заголовки["Content-Type"]).Равно("application/json"); 361 | Ожидаем.Что(Json["form"]["color"]).Содержит("Red"); 362 | Ожидаем.Что(Json["form"]["color"]).Содержит("Green"); 363 | 364 | КонецПроцедуры 365 | 366 | &Тест 367 | Процедура Должен_ПроверитьТочкуМаршрута_Put() Экспорт 368 | 369 | ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки("HTTPBIN"); 370 | 371 | Заголовки = Новый Соответствие(); 372 | Заголовки.Вставить("Content-Type", "application/octet-stream"); 373 | 374 | ПараметрыКоннектора = ПараметрыКоннектора(Заголовки); 375 | Ответ = ВызватьМетод("PUT", "put", ДвоичныеДанные, ПараметрыКоннектора); 376 | 377 | Результат = Ответ.Json(); 378 | 379 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 380 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 381 | Ожидаем.Что(Результат["data"]).Равно("HTTPBIN"); 382 | 383 | КонецПроцедуры 384 | 385 | &Тест 386 | Процедура Должен_ПроверитьТочкуМаршрута_Patch() Экспорт 387 | 388 | Тело = "id=123"; 389 | 390 | Ответ = ВызватьМетод("PATCH", "patch", Тело); 391 | Результат = Ответ.Json(); 392 | 393 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 394 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 395 | Ожидаем.Что(Результат["form"]["id"]).Равно("123"); 396 | 397 | КонецПроцедуры 398 | 399 | &Тест 400 | Процедура Должен_ПроверитьТочкуМаршрута_Delete() Экспорт 401 | 402 | Тело = "id=123"; 403 | 404 | Ответ = ВызватьМетод("DELETE", "delete", Тело); 405 | Результат = Ответ.Json(); 406 | 407 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 408 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 409 | Ожидаем.Что(Результат["form"]["id"]).Равно("123"); 410 | 411 | КонецПроцедуры 412 | 413 | &Тест 414 | Процедура Должен_ПроверитьТочкуМаршрута_RedirectNegative() Экспорт 415 | 416 | Ответ = ВызватьМетодGET("redirect/-3"); 417 | 418 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеНайдено_404); 419 | 420 | КонецПроцедуры 421 | 422 | &Тест 423 | Процедура Должен_ПроверитьТочкуМаршрута_Redirect() Экспорт 424 | 425 | Ответ = ВызватьМетодGET("redirect/3"); 426 | 427 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 428 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 429 | Ожидаем.Что(Ответ.Json()).Заполнено(); 430 | 431 | КонецПроцедуры 432 | 433 | &Тест 434 | Процедура Должен_ПроверитьТочкуМаршрута_RedirectTo_GET301() Экспорт 435 | 436 | ПараметрыЗапроса = Новый Структура("url, status_code", URL("get"), КодыСостоянияHTTP.ПеремещеноНавсегда_301); 437 | Ответ = ВызватьМетодGET("redirect-to", , ПараметрыЗапроса); 438 | 439 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 440 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 441 | Ожидаем.Что(Ответ.Json()).Заполнено(); 442 | 443 | КонецПроцедуры 444 | 445 | &Тест 446 | Процедура Должен_ПроверитьТочкуМаршрута_RedirectTo_GET() Экспорт 447 | 448 | ПараметрыЗапроса = Новый Структура("url", URL("get")); 449 | Ответ = ВызватьМетодGET("redirect-to", , ПараметрыЗапроса); 450 | 451 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 452 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 453 | Ожидаем.Что(Ответ.Json()).Заполнено(); 454 | 455 | КонецПроцедуры 456 | 457 | &Тест 458 | Процедура Должен_ПроверитьТочкуМаршрута_RedirectTo_POST() Экспорт 459 | 460 | Данные = Новый Структура("url, status_code", URL("get"), КодыСостоянияHTTP.ПеремещеноНавсегда_301); 461 | Ответ = ВызватьМетод("POST", "redirect-to", Данные); 462 | 463 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 464 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 465 | Ожидаем.Что(Ответ.Json()).Заполнено(); 466 | 467 | КонецПроцедуры 468 | 469 | &Тест 470 | Процедура Должен_ПроверитьТочкуМаршрута_RelativeRedirect() Экспорт 471 | 472 | Ответ = ВызватьМетодGET("relative-redirect/3"); 473 | 474 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 475 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 476 | Ожидаем.Что(Ответ.Json()).Заполнено(); 477 | 478 | КонецПроцедуры 479 | 480 | &Тест 481 | Процедура Должен_ПроверитьТочкуМаршрута_AbsoluteRedirect() Экспорт 482 | 483 | Ответ = ВызватьМетодGET("absolute-redirect/3"); 484 | 485 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 486 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 487 | Ожидаем.Что(Ответ.Json()).Заполнено(); 488 | 489 | КонецПроцедуры 490 | 491 | &Тест 492 | Процедура Должен_ПроверитьТочкуМаршрута_Status301() Экспорт 493 | 494 | Ответ = ВызватьМетодGET("status/301"); 495 | 496 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 497 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 498 | Ожидаем.Что(Ответ.Json()).Заполнено(); 499 | 500 | КонецПроцедуры 501 | 502 | &Тест 503 | Процедура Должен_ПроверитьТочкуМаршрута_Status401() Экспорт 504 | 505 | Ответ = ВызватьМетодGET("status/401"); 506 | 507 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеАвторизован_401); 508 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("text/html"); 509 | Ожидаем.Что(Ответ.Заголовки["WWW-Authenticate"]).Заполнено(); 510 | 511 | КонецПроцедуры 512 | 513 | &Тест 514 | Процедура Должен_ПроверитьТочкуМаршрута_Status402() Экспорт 515 | 516 | Ответ = ВызватьМетодGET("status/402"); 517 | 518 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеобходимаОплата_402); 519 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("text/html"); 520 | Ожидаем.Что(Ответ.Заголовки["x-more-info"]).Заполнено(); 521 | Ожидаем.Что(Ответ.Текст()).Равно("Fuck you, pay me!"); 522 | 523 | КонецПроцедуры 524 | 525 | &Тест 526 | Процедура Должен_ПроверитьТочкуМаршрута_Status406() Экспорт 527 | 528 | Ответ = ВызватьМетодGET("status/406"); 529 | Результат = Ответ.Json(); 530 | 531 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.Неприемлемо_406); 532 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 533 | Ожидаем.Что(Результат["message"]).Равно("Client did not request a supported media type."); 534 | Ожидаем.Что(Результат["accept"]).Заполнено(); 535 | 536 | КонецПроцедуры 537 | 538 | &Тест 539 | Процедура Должен_ПроверитьТочкуМаршрута_Status407() Экспорт 540 | 541 | Ответ = ВызватьМетодGET("status/407"); 542 | 543 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеобходимаАутентификацияПрокси_407); 544 | Ожидаем.Что(Ответ.Заголовки["Proxy-Authenticate"]).Заполнено(); 545 | 546 | КонецПроцедуры 547 | 548 | &Тест 549 | Процедура Должен_ПроверитьТочкуМаршрута_Status418() Экспорт 550 | 551 | Ответ = ВызватьМетодGET("status/418"); 552 | 553 | Ожидаем.Что(Ответ.КодСостояния).Равно(418); 554 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Не_().Заполнено(); 555 | Ожидаем.Что(Ответ.Заголовки["x-more-info"]).Заполнено(); 556 | Ожидаем.Что(Ответ.Текст()).Заполнено(); 557 | 558 | КонецПроцедуры 559 | 560 | &Тест 561 | Процедура Должен_ПроверитьТочкуМаршрута_Status_NaN() Экспорт 562 | 563 | Ответ = ВызватьМетодGET("status/NaN"); 564 | 565 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеверныйЗапрос_400); 566 | Ожидаем.Что(Ответ.Текст()).Равно("Invalid status code"); 567 | 568 | КонецПроцедуры 569 | 570 | &Тест 571 | Процедура Должен_ПроверитьТочкуМаршрута_Status_Random() Экспорт 572 | 573 | Ответ = ВызватьМетодGET("status/401:0.5,406:0.8,418:1,402"); 574 | 575 | Ожидаем.Что("401,406,418,402").Содержит(Формат(Ответ.КодСостояния, "ЧГ")); 576 | 577 | КонецПроцедуры 578 | 579 | &Тест 580 | Процедура Должен_ПроверитьТочкуМаршрута_Status_Random_НевалидноеСостояние() Экспорт 581 | 582 | Ответ = ВызватьМетодGET("status/401:0.5,NaN:0.8"); 583 | 584 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеверныйЗапрос_400); 585 | Ожидаем.Что(Ответ.Текст()).Равно("Invalid status code"); 586 | 587 | КонецПроцедуры 588 | 589 | &Тест 590 | Процедура Должен_ПроверитьТочкуМаршрута_Status_ОдинЭлементСВесом() Экспорт 591 | 592 | Ответ = ВызватьМетодGET("status/401:0.5"); 593 | 594 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеАвторизован_401); 595 | 596 | КонецПроцедуры 597 | 598 | &Тест 599 | Процедура Должен_ПроверитьТочкуМаршрута_ResponseHeaders() Экспорт 600 | 601 | Ответ = ВызватьМетодGET("response-headers?HeaderName=hello"); 602 | Результат = Ответ.Json(); 603 | 604 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 605 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 606 | Ожидаем.Что(Результат["Content-Type"]).Равно("application/json"); 607 | Ожидаем.Что(Результат["HeaderName"]).Равно("hello"); 608 | 609 | КонецПроцедуры 610 | 611 | &Тест 612 | Процедура Должен_ПроверитьТочкуМаршрута_Cookies() Экспорт 613 | 614 | Заголовки = Новый Соответствие(); 615 | Заголовки.Вставить("Cookie", "name=httpbin"); 616 | 617 | Ответ = ВызватьМетодGET("cookies", Заголовки); 618 | Результат = Ответ.Json(); 619 | 620 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 621 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 622 | Ожидаем.Что(Результат["cookies"]["name"]).Равно("httpbin"); 623 | 624 | КонецПроцедуры 625 | 626 | &Тест 627 | Процедура Должен_ПроверитьТочкуМаршрута_CookiesSetInPath() Экспорт 628 | 629 | Ответ = ВызватьМетодGET("cookies/set/name/httpbin"); 630 | Результат = Ответ.Json(); 631 | 632 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 633 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 634 | Ожидаем.Что(Результат["cookies"]["name"]).Равно("httpbin"); 635 | 636 | КонецПроцедуры 637 | 638 | &Тест 639 | Процедура Должен_ПроверитьТочкуМаршрута_CookiesSetInQueryString() Экспорт 640 | 641 | ПараметрыЗапроса = Новый Структура("name", "httpbin"); 642 | 643 | Ответ = ВызватьМетодGET("cookies/set", , ПараметрыЗапроса); 644 | Результат = Ответ.Json(); 645 | 646 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 647 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 648 | Ожидаем.Что(Результат["cookies"]["name"]).Равно("httpbin"); 649 | 650 | КонецПроцедуры 651 | 652 | &Тест 653 | Процедура Должен_ПроверитьТочкуМаршрута_CookiesDelete() Экспорт 654 | 655 | Ответ = ВызватьМетодGET("cookies/delete?name="); 656 | 657 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 658 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 659 | 660 | КонецПроцедуры 661 | 662 | &Тест 663 | Процедура Должен_ПроверитьТочкуМаршрута_FormsPost() Экспорт 664 | 665 | Ответ = ВызватьМетодGET("forms/post"); 666 | 667 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 668 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("text/html"); 669 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер()).Равно(1397); 670 | 671 | КонецПроцедуры 672 | 673 | &Тест 674 | Процедура Должен_ПроверитьТочкуМаршрута_BasicAuth() Экспорт 675 | 676 | Base64 = Base64Строка(ПолучитьДвоичныеДанныеИзСтроки("user:secret")); 677 | 678 | Заголовки = Новый Соответствие(); 679 | Заголовки.Вставить("Authorization", СтрШаблон("Basic %1", Base64)); 680 | 681 | Ответ = ВызватьМетодGET("basic-auth/user/secret", Заголовки); 682 | Результат = Ответ.Json(); 683 | 684 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 685 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 686 | Ожидаем.Что(Результат["authenticated"]).Равно(Истина); 687 | Ожидаем.Что(Результат["user"]).Равно("user"); 688 | 689 | КонецПроцедуры 690 | 691 | &Тест 692 | Процедура Должен_ПроверитьТочкуМаршрута_BasicAuth_401() Экспорт 693 | 694 | Base64 = Base64Строка(ПолучитьДвоичныеДанныеИзСтроки("user:wrong")); 695 | 696 | Заголовки = Новый Соответствие(); 697 | Заголовки.Вставить("Authorization", СтрШаблон("Basic %1", Base64)); 698 | 699 | Ответ = ВызватьМетодGET("basic-auth/user/secret", Заголовки); 700 | 701 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеАвторизован_401); 702 | Ожидаем.Что(Ответ.Заголовки["WWW-Authenticate"]).Равно("Basic realm=""Fake Realm"""); 703 | 704 | КонецПроцедуры 705 | 706 | &Тест 707 | Процедура Должен_ПроверитьТочкуМаршрута_HiddenBasicAuth() Экспорт 708 | 709 | Base64 = Base64Строка(ПолучитьДвоичныеДанныеИзСтроки("user:secret")); 710 | 711 | Заголовки = Новый Соответствие(); 712 | Заголовки.Вставить("Authorization", СтрШаблон("Basic %1", Base64)); 713 | 714 | Ответ = ВызватьМетодGET("hidden-basic-auth/user/secret", Заголовки); 715 | Результат = Ответ.Json(); 716 | 717 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 718 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 719 | Ожидаем.Что(Результат["authenticated"]).Равно(Истина); 720 | Ожидаем.Что(Результат["user"]).Равно("user"); 721 | 722 | КонецПроцедуры 723 | 724 | &Тест 725 | Процедура Должен_ПроверитьТочкуМаршрута_HiddenBasicAuth_404() Экспорт 726 | 727 | Base64 = Base64Строка(ПолучитьДвоичныеДанныеИзСтроки("user:wrong")); 728 | 729 | Заголовки = Новый Соответствие(); 730 | Заголовки.Вставить("Authorization", СтрШаблон("Basic %1", Base64)); 731 | 732 | Ответ = ВызватьМетодGET("hidden-basic-auth/user/secret", Заголовки); 733 | 734 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеНайдено_404); 735 | 736 | КонецПроцедуры 737 | 738 | &Тест 739 | Процедура Должен_ПроверитьТочкуМаршрута_Bearer() Экспорт 740 | 741 | Токен = "mF_9.B5f-4.1JqM"; 742 | 743 | Заголовки = Новый Соответствие(); 744 | Заголовки.Вставить("Authorization", СтрШаблон("Bearer %1", Токен)); 745 | 746 | Ответ = ВызватьМетодGET("bearer", Заголовки); 747 | 748 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 749 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 750 | Ожидаем.Что(Ответ.Json()["authenticated"]).Равно(Истина); 751 | Ожидаем.Что(Ответ.Json()["token"]).Равно(Токен); 752 | 753 | КонецПроцедуры 754 | 755 | &Тест 756 | Процедура Должен_ПроверитьТочкуМаршрута_Bearer_401() Экспорт 757 | 758 | Ответ = ВызватьМетодGET("bearer"); 759 | 760 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеАвторизован_401); 761 | Ожидаем.Что(Ответ.Заголовки["WWW-Authenticate"]).Равно("Bearer"); 762 | 763 | КонецПроцедуры 764 | 765 | &Тест 766 | Процедура Должен_ПроверитьТочкуМаршрута_Delay() Экспорт 767 | 768 | ЗадержкаСекунд = 0.5; 769 | ЗадержкаМиллисекунд = ЗадержкаСекунд * 1000; 770 | КоэффициентПогрешности = 1.9; 771 | 772 | НачалоЗамера = ТекущаяУниверсальнаяДатаВМиллисекундах(); 773 | Ответ = ВызватьМетодGET("delay/" + ЗадержкаСекунд); 774 | ПрошлоМиллисекунд = ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЗамера; 775 | 776 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 777 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 778 | Ожидаем.Что(ПрошлоМиллисекунд).Больше(ЗадержкаМиллисекунд); 779 | Ожидаем.Что(ПрошлоМиллисекунд).Меньше(ЗадержкаМиллисекунд * КоэффициентПогрешности); 780 | 781 | КонецПроцедуры 782 | 783 | &Тест 784 | Процедура Должен_ПроверитьТочкуМаршрута_Base64() Экспорт 785 | 786 | Ответ = ВызватьМетодGET("base64/SFRUUEJJTiBpcyBhd2Vzb21l"); 787 | 788 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 789 | Ожидаем.Что(Ответ.Текст()).Равно("HTTPBIN is awesome"); 790 | 791 | КонецПроцедуры 792 | 793 | &Тест 794 | Процедура Должен_ПроверитьТочкуМаршрута_Base64_Incorrect() Экспорт 795 | 796 | Ответ = ВызватьМетодGET("base64/123"); 797 | 798 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 799 | Ожидаем.Что(Ответ.Текст()).Содержит("Incorrect Base64 data try"); 800 | 801 | КонецПроцедуры 802 | 803 | &Тест 804 | Процедура Должен_ПроверитьТочкуМаршрута_Cache() Экспорт 805 | 806 | Ответ = ВызватьМетодGET("cache"); 807 | 808 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 809 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 810 | Ожидаем.Что(Ответ.Заголовки["Last-Modified"], "Last-Modified").Заполнено(); 811 | Ожидаем.Что(Ответ.Заголовки["ETag"], "ETag").Заполнено(); 812 | 813 | КонецПроцедуры 814 | 815 | &Тест 816 | Процедура Должен_ПроверитьТочкуМаршрута_Cache_IfModifiedSince() Экспорт 817 | 818 | Заголовки = Новый Соответствие(); 819 | Заголовки.Вставить("If-Modified-Since", "Wed, 21 Oct 2015 07:28:00 GMT"); 820 | 821 | Ответ = ВызватьМетодGET("cache", Заголовки); 822 | 823 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеИзменялось_304); 824 | 825 | КонецПроцедуры 826 | 827 | &Тест 828 | Процедура Должен_ПроверитьТочкуМаршрута_Cache_IfNoneMatch() Экспорт 829 | 830 | Заголовки = Новый Соответствие(); 831 | Заголовки.Вставить("If-None-Match", "*"); 832 | 833 | Ответ = ВызватьМетодGET("cache", Заголовки); 834 | 835 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.НеИзменялось_304); 836 | 837 | КонецПроцедуры 838 | 839 | &Тест 840 | Процедура Должен_ПроверитьТочкуМаршрута_CacheControl() Экспорт 841 | 842 | Секунд = 3600; 843 | СекундТекст = Формат(Секунд, "ЧГ="); 844 | 845 | Ответ = ВызватьМетодGET("cache/" + СекундТекст); 846 | 847 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 848 | Ожидаем.Что(Ответ.Заголовки["Cache-Control"]).Равно(СтрШаблон("public, max-age=%1", СекундТекст)); 849 | 850 | КонецПроцедуры 851 | 852 | &Тест 853 | Процедура Должен_ПроверитьТочкуМаршрута_ETag_IfNoneMatch() Экспорт 854 | 855 | ETag = СтрЗаменить(Новый УникальныйИдентификатор(), "-", ""); 856 | 857 | ТестовыеЗначения = Новый Соответствие(); 858 | ТестовыеЗначения.Вставить(КодыСостоянияHTTP.НеИзменялось_304, Новый Массив()); 859 | ТестовыеЗначения.Вставить(КодыСостоянияHTTP.ОК_200, Новый Массив()); 860 | 861 | ТестовыеЗначения[КодыСостоянияHTTP.НеИзменялось_304].Добавить(ETag); 862 | ТестовыеЗначения[КодыСостоянияHTTP.НеИзменялось_304].Добавить(СтрШаблон("W/""67ab43"", ""54ed21"", ""%1""", ETag)); 863 | ТестовыеЗначения[КодыСостоянияHTTP.НеИзменялось_304].Добавить("*"); 864 | ТестовыеЗначения[КодыСостоянияHTTP.ОК_200].Добавить(Неопределено); 865 | ТестовыеЗначения[КодыСостоянияHTTP.ОК_200].Добавить("bfc13a64729c4290ef5b2c2730249c88ca92d82d"); 866 | ТестовыеЗначения[КодыСостоянияHTTP.ОК_200].Добавить("W/""67ab43"", ""54ed21"", ""7892dd"""); 867 | 868 | Для Каждого КлючИЗначение Из ТестовыеЗначения Цикл 869 | КодСостояния = КлючИЗначение.Ключ; 870 | 871 | Для Каждого ТестовоеЗначение Из КлючИЗначение.Значение Цикл 872 | 873 | Заголовки = Новый Соответствие(); 874 | 875 | Если Не ТестовоеЗначение = Неопределено Тогда 876 | Заголовки.Вставить("If-None-Match", ТестовоеЗначение); 877 | КонецЕсли; 878 | 879 | Ответ = ВызватьМетодGET("etag/" + ETag, Заголовки); 880 | 881 | Ожидаем.Что(Ответ.КодСостояния, ТестовоеЗначение).Равно(КодСостояния); 882 | Ожидаем.Что(Ответ.Заголовки["ETag"]).Равно(ETag); 883 | 884 | Если КодСостояния = КодыСостоянияHTTP.ОК_200 Тогда 885 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 886 | Иначе 887 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("text/html"); 888 | КонецЕсли; 889 | 890 | КонецЦикла; 891 | КонецЦикла; 892 | 893 | КонецПроцедуры 894 | 895 | &Тест 896 | Процедура Должен_ПроверитьТочкуМаршрута_ETag_IfMatch() Экспорт 897 | 898 | ETag = СтрЗаменить(Новый УникальныйИдентификатор(), "-", ""); 899 | 900 | ТестовыеЗначения = Новый Соответствие(); 901 | ТестовыеЗначения.Вставить(КодыСостоянияHTTP.УсловиеЛожно_412, Новый Массив()); 902 | ТестовыеЗначения.Вставить(КодыСостоянияHTTP.ОК_200, Новый Массив()); 903 | 904 | ТестовыеЗначения[КодыСостоянияHTTP.ОК_200].Добавить(ETag); 905 | ТестовыеЗначения[КодыСостоянияHTTP.ОК_200].Добавить(СтрШаблон("W/""67ab43"", ""54ed21"", ""%1""", ETag)); 906 | ТестовыеЗначения[КодыСостоянияHTTP.ОК_200].Добавить("*"); 907 | ТестовыеЗначения[КодыСостоянияHTTP.ОК_200].Добавить(Неопределено); 908 | ТестовыеЗначения[КодыСостоянияHTTP.УсловиеЛожно_412].Добавить("bfc13a64729c4290ef5b2c2730249c88ca92d82d"); 909 | ТестовыеЗначения[КодыСостоянияHTTP.УсловиеЛожно_412].Добавить("W/""67ab43"", ""54ed21"", ""7892dd"""); 910 | 911 | Для Каждого КлючИЗначение Из ТестовыеЗначения Цикл 912 | КодСостояния = КлючИЗначение.Ключ; 913 | 914 | Для Каждого ТестовоеЗначение Из КлючИЗначение.Значение Цикл 915 | 916 | Заголовки = Новый Соответствие(); 917 | 918 | Если Не ТестовоеЗначение = Неопределено Тогда 919 | Заголовки.Вставить("If-Match", ТестовоеЗначение); 920 | КонецЕсли; 921 | 922 | Ответ = ВызватьМетодGET("etag/" + ETag, Заголовки); 923 | 924 | Ожидаем.Что(Ответ.КодСостояния, ТестовоеЗначение).Равно(КодСостояния); 925 | 926 | Если КодСостояния = КодыСостоянияHTTP.ОК_200 Тогда 927 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 928 | Ожидаем.Что(Ответ.Заголовки["ETag"]).Равно(ETag); 929 | Иначе 930 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("text/html"); 931 | КонецЕсли; 932 | 933 | КонецЦикла; 934 | КонецЦикла; 935 | 936 | КонецПроцедуры 937 | 938 | &Тест 939 | Процедура Должен_ПроверитьТочкуМаршрута_EncodingUTF8() Экспорт 940 | 941 | РазмерДанныхБайт = 14240; 942 | 943 | Ответ = ВызватьМетодGET("encoding/utf8"); 944 | 945 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 946 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("text/html"); 947 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер()).Равно(РазмерДанныхБайт); 948 | 949 | КонецПроцедуры 950 | 951 | &Тест 952 | Процедура Должен_ПроверитьТочкуМаршрута_Bytes() Экспорт 953 | 954 | РазмерДанныхБайт = 128; 955 | 956 | Ответ = ВызватьМетодGET("bytes/128"); 957 | 958 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 959 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/octet-stream"); 960 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер()).Равно(РазмерДанныхБайт); 961 | 962 | КонецПроцедуры 963 | 964 | &Тест 965 | Процедура Должен_ПроверитьТочкуМаршрута_BytesWithSeed() Экспорт 966 | 967 | РазмерДанныхБайт = 128; 968 | НачальноеЧисло = 100; 969 | 970 | ПараметрыЗапроса = Новый Структура("seed", НачальноеЧисло); 971 | Ответ = ВызватьМетодGET("bytes/128", , ПараметрыЗапроса); 972 | ДвоичныеДанные = Ответ.ДвоичныеДанные(); 973 | 974 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 975 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/octet-stream"); 976 | Ожидаем.Что(ДвоичныеДанные.Размер()).Равно(РазмерДанныхБайт); 977 | 978 | КонецПроцедуры 979 | 980 | &Тест 981 | Процедура Должен_ПроверитьТочкуМаршрута_Links_СоСмещением() Экспорт 982 | 983 | Ответ = ВызватьМетодGET("links/3/2"); 984 | 985 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 986 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("text/html"); 987 | Ожидаем.Что(Ответ.Текст()).Содержит(" 2 1"); 999 | 1000 | КонецПроцедуры 1001 | 1002 | &Тест 1003 | Процедура Должен_ПроверитьТочкуМаршрута_Image_СЗаголовкомAccept() Экспорт 1004 | 1005 | ТестовыеЗначения = Новый Массив(); 1006 | ТестовыеЗначения.Добавить("image/webp;image/webp;10568"); 1007 | ТестовыеЗначения.Добавить("image/svg+xml;image/svg+xml;0"); 1008 | ТестовыеЗначения.Добавить("image/jpeg;image/jpeg;35588"); 1009 | ТестовыеЗначения.Добавить("image/png;image/png;8090"); 1010 | ТестовыеЗначения.Добавить("image/*;image/png;8090"); 1011 | ТестовыеЗначения.Добавить("*/*;image/png;8090"); 1012 | 1013 | Для Каждого ТестовоеЗначение Из ТестовыеЗначения Цикл 1014 | 1015 | Подстроки = СтрРазделить(ТестовоеЗначение, ";", Истина); 1016 | Accept = Подстроки[0]; 1017 | ContentType = Подстроки[1]; 1018 | Размер = Число(Подстроки[2]); 1019 | 1020 | Заголовки = Новый Соответствие(); 1021 | Заголовки.Вставить("Accept", Accept); 1022 | 1023 | Ответ = ВызватьМетодGET("image", Заголовки); 1024 | 1025 | Ожидаем.Что(Ответ.КодСостояния, Accept).Равно(КодыСостоянияHTTP.ОК_200); 1026 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно(ContentType); 1027 | 1028 | Если Размер > 0 Тогда 1029 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер(), ContentType).Равно(Размер); 1030 | КонецЕсли; 1031 | 1032 | КонецЦикла; 1033 | 1034 | КонецПроцедуры 1035 | 1036 | &Тест 1037 | Процедура Должен_ПроверитьТочкуМаршрута_Image_БезЗаголовкаAccept() Экспорт 1038 | 1039 | Заголовки = Новый Соответствие(); 1040 | Заголовки.Вставить("Accept", ""); // Иначе коннектор устанавливает */* по умолчанию 1041 | 1042 | Ответ = ВызватьМетодGET("image", Заголовки); 1043 | 1044 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.Неприемлемо_406); 1045 | 1046 | КонецПроцедуры 1047 | 1048 | &Тест 1049 | Процедура Должен_ПроверитьТочкуМаршрута_ImagePng() Экспорт 1050 | 1051 | ContentType = "image/png"; 1052 | РазмерДанныхБайт = 8090; 1053 | 1054 | Ответ = ВызватьМетодGET("image/png"); 1055 | 1056 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 1057 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно(ContentType); 1058 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер(), ContentType).Равно(РазмерДанныхБайт); 1059 | 1060 | КонецПроцедуры 1061 | 1062 | &Тест 1063 | Процедура Должен_ПроверитьТочкуМаршрута_ImageJpeg() Экспорт 1064 | 1065 | ContentType = "image/jpeg"; 1066 | РазмерДанныхБайт = 35588; 1067 | 1068 | Ответ = ВызватьМетодGET("image/jpeg"); 1069 | 1070 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 1071 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно(ContentType); 1072 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер(), ContentType).Равно(РазмерДанныхБайт); 1073 | 1074 | КонецПроцедуры 1075 | 1076 | &Тест 1077 | Процедура Должен_ПроверитьТочкуМаршрута_ImageWebp() Экспорт 1078 | 1079 | ContentType = "image/webp"; 1080 | РазмерДанныхБайт = 10568; 1081 | 1082 | Ответ = ВызватьМетодGET("image/webp"); 1083 | 1084 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 1085 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно(ContentType); 1086 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер(), ContentType).Равно(РазмерДанныхБайт); 1087 | 1088 | КонецПроцедуры 1089 | 1090 | &Тест 1091 | Процедура Должен_ПроверитьТочкуМаршрута_ImageSvg() Экспорт 1092 | 1093 | ContentType = "image/svg+xml"; 1094 | 1095 | Ответ = ВызватьМетодGET("image/svg"); 1096 | 1097 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 1098 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно(ContentType); 1099 | 1100 | КонецПроцедуры 1101 | 1102 | &Тест 1103 | Процедура Должен_ПроверитьТочкуМаршрута_Xml() Экспорт 1104 | 1105 | РазмерДанныхБайт = 523; 1106 | 1107 | Ответ = ВызватьМетодGET("xml"); 1108 | 1109 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 1110 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("application/xml"); 1111 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер()).Равно(РазмерДанныхБайт); 1112 | 1113 | КонецПроцедуры 1114 | 1115 | &Тест 1116 | Процедура Должен_ПроверитьТочкуМаршрута_Json() Экспорт 1117 | 1118 | РазмерДанныхБайт = 323; 1119 | 1120 | Ответ = ВызватьМетодGET("json"); 1121 | 1122 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 1123 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Содержит("application/json"); 1124 | Ожидаем.Что(Ответ.ДвоичныеДанные().Размер()).Равно(РазмерДанныхБайт); 1125 | 1126 | КонецПроцедуры 1127 | 1128 | &Тест 1129 | Процедура Должен_ПроверитьТочкуМаршрута_GZip() Экспорт 1130 | 1131 | Ответ = ВызватьМетодGET("gzip"); 1132 | 1133 | Парсер = Новый ПарсерJSON(); 1134 | Json = Парсер.ПрочитатьJSON(Ответ.Текст()); 1135 | 1136 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 1137 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 1138 | Ожидаем.Что(Json["gzipped"]).Равно(Истина); 1139 | 1140 | КонецПроцедуры 1141 | 1142 | &Тест 1143 | Процедура Должен_ПроверитьТочкуМаршрута_Deflate() Экспорт 1144 | 1145 | Ответ = ВызватьМетодGET("deflate"); 1146 | 1147 | Компрессор = Новый DeflateКомпрессор(); 1148 | ДвоичныеДанные = Компрессор.Распаковать(Ответ.ДвоичныеДанные()); 1149 | ТестJson = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные); 1150 | Парсер = Новый ПарсерJSON(); 1151 | Json = Парсер.ПрочитатьJSON(ТестJson); 1152 | 1153 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 1154 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 1155 | Ожидаем.Что(Ответ.Заголовки["Content-Encoding"]).Равно("deflate"); 1156 | Ожидаем.Что(Json["deflated"]).Равно(Истина); 1157 | 1158 | КонецПроцедуры 1159 | 1160 | &Тест 1161 | Процедура Должен_ПроверитьТочкуМаршрута_Brotli() Экспорт 1162 | 1163 | Ответ = ВызватьМетодGET("brotli"); 1164 | 1165 | Компрессор = Новый BrotliКомпрессор(); 1166 | ДвоичныеДанные = Компрессор.Распаковать(Ответ.ДвоичныеДанные()); 1167 | ТестJson = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные); 1168 | Парсер = Новый ПарсерJSON(); 1169 | Json = Парсер.ПрочитатьJSON(ТестJson); 1170 | 1171 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 1172 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 1173 | Ожидаем.Что(Ответ.Заголовки["Content-Encoding"]).Равно("br"); 1174 | Ожидаем.Что(Json["brotli"]).Равно(Истина); 1175 | 1176 | КонецПроцедуры 1177 | 1178 | &Тест 1179 | Процедура Должен_ПроверитьТочкуМаршрута_Zstd() Экспорт 1180 | 1181 | Ответ = ВызватьМетодGET("zstd"); 1182 | 1183 | Компрессор = Новый ZStdКомпрессор(); 1184 | ДвоичныеДанные = Компрессор.Распаковать(Ответ.ДвоичныеДанные()); 1185 | ТестJson = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные); 1186 | Парсер = Новый ПарсерJSON(); 1187 | Json = Парсер.ПрочитатьJSON(ТестJson); 1188 | 1189 | Ожидаем.Что(Ответ.КодСостояния).Равно(КодыСостоянияHTTP.ОК_200); 1190 | Ожидаем.Что(Ответ.Заголовки["Content-Type"]).Равно("application/json"); 1191 | Ожидаем.Что(Ответ.Заголовки["Content-Encoding"]).Равно("zstd"); 1192 | Ожидаем.Что(Json["zstd"]).Равно(Истина); 1193 | 1194 | КонецПроцедуры 1195 | 1196 | Функция ВызватьМетодGET(АдресРсесурса, Заголовки = Неопределено, ПараметрыЗапроса = Неопределено) 1197 | 1198 | Ответ = КоннекторHTTP.Get(URL(АдресРсесурса), ПараметрыЗапроса, ПараметрыКоннектора(Заголовки)); 1199 | Возврат Ответ; 1200 | 1201 | КонецФункции 1202 | 1203 | Функция ВызватьМетод(Метод, АдресРсесурса, Данные = Неопределено, ДополнительныеПараметры = Неопределено) 1204 | 1205 | ЗадержкуПередЧтениемСокетаПоУмолчанию = 65; 1206 | ЗадержкуПередЧтениемСокетаДляЗапросаСДанными = 400; 1207 | 1208 | Если ДополнительныеПараметры = Неопределено Тогда 1209 | ДополнительныеПараметры = Новый Структура(); 1210 | КонецЕсли; 1211 | 1212 | Если Не Данные = Неопределено Тогда 1213 | ДополнительныеПараметры.Вставить("Данные", Данные); 1214 | HttpBin.УстановитьЗадержкуПередЧтениемСокета(ЗадержкуПередЧтениемСокетаДляЗапросаСДанными); 1215 | КонецЕсли; 1216 | 1217 | Ответ = КоннекторHTTP.ВызватьМетод(Метод, URL(АдресРсесурса), ДополнительныеПараметры); 1218 | HttpBin.УстановитьЗадержкуПередЧтениемСокета(ЗадержкуПередЧтениемСокетаПоУмолчанию); 1219 | 1220 | Возврат Ответ; 1221 | 1222 | КонецФункции 1223 | 1224 | Функция URL(АдресРсесурса) 1225 | Возврат СтрШаблон("%1/%2", HttpBin.URL(), АдресРсесурса); 1226 | КонецФункции 1227 | 1228 | Функция ПараметрыКоннектора(Заголовки = Неопределено) 1229 | 1230 | Таймаут = 10; 1231 | 1232 | ПараметрыКоннектора = Новый Структура(); 1233 | ПараметрыКоннектора.Вставить("Таймаут", Таймаут); 1234 | 1235 | Если Не Заголовки = Неопределено Тогда 1236 | ПараметрыКоннектора.Вставить("Заголовки", Заголовки); 1237 | КонецЕсли; 1238 | 1239 | Возврат ПараметрыКоннектора; 1240 | 1241 | КонецФункции 1242 | 1243 | Функция Base64ZipФайла() 1244 | Возврат "UEsDBAoAAAAAAEAIalpdCci4BwAAAAcAAAALAAAAaHR0cGJpbi50eHRIVFRQQklOUEsBAh8ACgAAAAAAQAhqWl0JyLgHAAAABwAAAAsAJAAAAAAAAAAgAAAAAAAAAGh0dHBiaW4udHh0CgAgAAAAAAABABgAYXX04T6R2wFhdfThPpHbAdjuOJY+kdsBUEsFBgAAAAABAAEAXQAAADAAAAAAAA=="; 1245 | КонецФункции -------------------------------------------------------------------------------- /tests/HttpBin_test.os: -------------------------------------------------------------------------------- 1 | // BSLLS:UnusedLocalVariable-off 2 | 3 | #Использовать asserts 4 | #Использовать ".." 5 | 6 | &Тест 7 | Процедура Должен_ЗапуститьСервисВФонеСОжиданиемИОстановить() Экспорт 8 | 9 | HttpBin = Новый HttpBin() 10 | .ЗапускатьВФоне() // Устанавливается по умолчанию при создании объекта 11 | .ОжидатьЗапуск() // Устанавливается по умолчанию при создании объекта 12 | .Запустить() 13 | .Остановить(); 14 | 15 | КонецПроцедуры 16 | 17 | &Тест 18 | Процедура Должен_ЗапуститьСервисВФонеБезОжиданияИОстановить() Экспорт 19 | 20 | HttpBin = Новый HttpBin() 21 | .ЗапускатьВФоне() // Устанавливается по умолчанию при создании объекта 22 | .ОжидатьЗапуск(Ложь) 23 | .Запустить(); 24 | 25 | Приостановить(500); 26 | 27 | HttpBin.Остановить(); 28 | 29 | КонецПроцедуры 30 | 31 | &Тест 32 | Процедура Должен_ЗапуститьСервисВФонеСОжиданиемИОстановитьЧерезПоделку() Экспорт 33 | 34 | Поделка = Новый Поделка(); 35 | Поделка.ЗапуститьПриложение(); 36 | 37 | HttpBin = Новый HttpBin(Поделка) 38 | .ЗапускатьВФоне() // Устанавливается по умолчанию при создании объекта 39 | .ОжидатьЗапуск() // Устанавливается по умолчанию при создании объекта 40 | .Запустить() 41 | .Остановить(); 42 | 43 | КонецПроцедуры --------------------------------------------------------------------------------