Синтаксис и применение HTTPСоединение.По указанному серверу в платформе 1С:Предприятие. Описание и примеры кода.
Синтаксис
Новый HTTPСоединение(<Сервер>, <Порт>, <Пользователь>, <Пароль>, <Прокси>, <ЗащищенноеСоединение>)Примеры платформы
ПроксиСервер = Новый ИнтернетПрокси;
ПроксиСервер.Пользователь = ИмяПользователя;
ПроксиСервер.Пароль = ПарольПользователя;
Попытка
HTTP = Новый HTTPСоединение(СерверИсточник,,,, ПроксиСервер);
Исключение
КонецПопытки;Полное описание, параметры и ограничения
HTTPСоединение.По указанному серверу
HTTPСоединение (HTTPConnection)
По указанному серверу
Не рекомендуется использовать, начиная с версии 8.3.7.
Доступен, начиная с версии 8.0.
Синтаксис:
Новый HTTPСоединение(<Сервер>, <Порт>, <Пользователь>, <Пароль>, <Прокси>, <ЗащищенноеСоединение>)
Параметры:
<Сервер> (обязательный)
Тип: Строка.
Хост сервера, с которым осуществляется соединение.
Примечание: Имя хоста не должно содержать указание протокола. Например, example.com.
<Порт> (необязательный)
Тип: Число.
Порт сервера, с которым осуществляется соединение.
Значение по умолчанию для HTTP соединений равно 80, для защищенных HTTPS соединений - 443.
Значение по умолчанию: порт по умолчанию для используемого протокола.
<Пользователь> (необязательный)
Тип: Строка.
Имя пользователя на указанном сервере.
<Пароль> (необязательный)
Тип: Строка.
Пароль пользователя на указанном сервере.
<Прокси> (необязательный)
Тип: ИнтернетПрокси.
Прокси, используемый для соединения с сервером.
<ЗащищенноеСоединение> (необязательный)
Тип: Булево.
Определяет используемый протокол:
Истина - HTTPs,
Ложь - HTTP.
Значение по умолчанию: Ложь.
Пример:
ПроксиСервер = Новый ИнтернетПрокси;
ПроксиСервер.Пользователь = ИмяПользователя;
ПроксиСервер.Пароль = ПарольПользователя;
Попытка
HTTP = Новый HTTPСоединение(СерверИсточник,,,, ПроксиСервер);
Исключение
КонецПопытки;
Использование в версии:
Доступен, начиная с версии 8.0.
Не рекомендуется использовать, начиная с версии 8.3.7.
Описание изменено в версии 8.3.21.
Методическая информация
Практические примеры
Пройти семь HTTP-методов через Ждать на своем loopback-сервисе с настоящей RS256-проверкой
Полный собственный fixture хранит одну учебную карточку только в памяти. Клиент проверяет Basic-паспорт соединения, подписывает собственный JWT RSA-ключом fixture, передает настоящий ТокенДоступа и проверяет каждую мутацию/HTTP-статус/UUID/содержимое.
Где и как запускать
Полный модуль управляемой формы своей внешней обработки в новом пустом УчебныйHTTP85. Положительный thick-client JWT/HTTP-путь и серверный guard включены; Python fixture ниже — отдельный supporting source, не BSL.
Подготовка
Только отдельная собственная Windows VM без рабочего интернета/прокси/персональных ключей и новая пустая1С8.5.1.1343. Имя конфигурации УчебныйHTTP85, русский язык, совместимость Не использовать, управляемый интерфейс. Нет предметных объектов/расширений/фона. Автор не запускал fixture, RSA-генерацию, подпись, HTTP или файлы; это полные тексты для вашего отдельного ручного учебного опыта.
Положительный путь — толстый управляемый клиент. Семь HTTP-promise методов доступны и вthin по native, но этот полный опыт также использует enum АлгоритмПодписиТокенаДоступа и RS256-подпись, поэтому его positive path сознательно проверяется только thick. Для web/mobile/CORS/HTTPS этот пример не выдается за готовый.
Создайте внешнюю обработку УчебныйHTTP85 с одной управляемой формой. Строки Метка85 длиной36, СвойКлючPEMBase6485 длиной0, Подтверждение85 длиной100, Статус85 длиной0; булево НачатОпыт85. Метка/статус/флаг только для просмотра, у поля ключа защищенное отображение. Команда Выполнить85 имеет действие Выполнить85; назначьте ПриСозданииНаСервере и вставьте весь основной BSL-модуль. Ничего не запускается автоматически.
В своей VM подготовьте Python с cryptography: используемые generate_private_key/private_bytes/verify проверяются по установленному API, не по mock-подписи. Создайте вручную отдельную пустую родительскую папку только своего опыта с NTFS-доступом только своего пользователя/администратора VM, не общий/temp/workspace-root каталог. Сохраните поддерживающий Python-код как own_http85.py. Права/пакеты/папки здесь не устанавливались.
Откройте свою форму1С и скопируйте ее свежую Метка85. В учебном терминале VM выполните python own_http85.py --session ВАШ_ТОЧНЫЙ_UUID_ИЗ_ФОРМЫ --own-directory ПОЛНЫЙ_ПУТЬ_ВАШЕЙ_НОВОЙ_РОДИТЕЛЬСКОЙ_ПАПКИ. Аргументы — реально созданные вами метка/папка, не текст этих пояснений. Fixture отказывает при неконаническом UUID или существующей папке http85-UUID; не перезаписывает прежние файлы.
При вашем ручном старте Python создаст собственную RSA2048-пару и только локальный Base64(PKCS8 PEM) private-key файл внутри своей новой UUID-папки. Не публикуйте и не используйте этот ключ в production: он намеренно не зашифрован и нужен только внутри выделенной VM. Скопируйте локальный Base64 в защищенное поле своей формы; stdout содержит только UUID/путь/host/port, не ключ. Запросы/Authorization Python не логирует.
Fixture слушает только127.0.0.1:18085; закрытому серверу не задается0.0.0.0, внешнее имя или пользовательский порт. Одна карточка отсутствует при старте. Basic lab85/UUID — только учебная проверка параметров/handshake, не production-secret. RS256 проверяет настоящую RSA-подпись, алгоритм, iss/aud/sub/session/jti/временные claims, а не просто декодирует JWT.
В форме сначала оставьте фразу пустой: никаких HTTP-вызовов. Для единственного опыта введите СВОЯ VM И LOOPBACK FIXTURE. Код сам создает собственные относительные пути/тела. После нажатия НачатОпыт ставится до первого HTTP вызова, повтор запрещен при любом результате. Не вводите действительные пароли, чужие токены или имена.
После опыта остановите только свой Python-процесс вручную. Учебная карточка уже удалена только при всех подтвержденных проверках; при исключении она может остаться в памяти своего процесса и не удаляется automatically. Свой ключ/UUID-каталог удаляйте вручную только после проверки точного собственного пути, не очищайте родительские каталоги рекурсивно. Копий/архивов автор не создавал.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ПроверитьСвойПустойСтенд();
Метка85 = Строка(Новый УникальныйИдентификатор);
СвойКлючPEMBase6485 = "";
Подтверждение85 = "";
НачатОпыт85 = Ложь;
Статус85 = "Новый UUID только для собственного loopback fixture. Ключей и соединений пока нет";
КонецПроцедуры
&НаКлиенте
Асинх Процедура Выполнить85(Команда)
#Если ТолстыйКлиентУправляемоеПриложение Тогда
Если НачатОпыт85 Тогда
ВызватьИсключение "Этот опыт уже мог измениться. Повтор/автоматическая очистка не выполняются";
КонецЕсли;
Если Подтверждение85 <> "СВОЯ VM И LOOPBACK FIXTURE" Или Не ЗначениеЗаполнено(СвойКлючPEMBase6485) Тогда
ВызватьИсключение "Нужны собственный fixture и его собственный RSA-ключ, не готовый чужой токен";
КонецЕсли;
ПроверитьСеансНаСервере85();
Если Строка(Новый УникальныйИдентификатор(Метка85)) <> Метка85 Тогда
ВызватьИсключение "Собственная метка должна быть каноническим UUID";
КонецЕсли;
Прокси = Новый ИнтернетПрокси(Ложь);
СтарыйПрофиль = Новый HTTPСоединение("127.0.0.1", 18085, "lab85", Метка85, Прокси, Ложь);
Соединение = Новый HTTPСоединение("127.0.0.1", 18085, "lab85", Метка85, Прокси, 5, Неопределено, Ложь);
Если СтарыйПрофиль.Пользователь <> "lab85" Или СтарыйПрофиль.Пароль <> Метка85
Или Соединение.Пользователь <> "lab85" Или Соединение.Пароль <> Метка85
Или ТипЗнч(Соединение.Прокси) <> Тип("ИнтернетПрокси")
Или Соединение.ЗащищенноеСоединение <> Неопределено
Или Соединение.Сервер <> "127.0.0.1" Или Соединение.Порт <> 18085
Или Соединение.Таймаут <> 5 Или Соединение.ИспользоватьАутентификациюОС Тогда
ВызватьИсключение "Паспорт своего современного соединения не совпал";
КонецЕсли;
НачатОпыт85 = Истина;
Попытка
Путь = "/lab/" + Метка85 + "/book";
ПрофильныйЗапрос = Новый HTTPЗапрос("/lab/" + Метка85 + "/profile");
ПрофильныйОтвет = Ждать Соединение.ПолучитьАсинх(ПрофильныйЗапрос);
ПроверитьСвойОтвет85(ПрофильныйОтвет, 200, Метка85 + "|Basic", Истина);
Соединение = Новый HTTPСоединение("127.0.0.1", 18085, "", "", Прокси, 5, Неопределено, Ложь);
Если Соединение.Пользователь <> "" Или Соединение.Пароль <> "" Тогда
ВызватьИсключение "Для Bearer используется отдельное своё соединение без Basic учетных данных";
КонецЕсли;
Токен = Новый ТокенДоступа;
Токен.ВремяСоздания = ТекущаяУниверсальнаяДата() - Дата(1970, 1, 1);
Токен.ВремяЖизни = 300;
Токен.Идентификатор = Строка(Новый УникальныйИдентификатор);
Токен.КлючСопоставленияПользователя = "lab85";
Токен.Эмитент = "urn:guide-http85";
Токен.Получатели.Добавить("urn:guide-http85");
Токен.ПолезнаяНагрузка.Вставить("session", Метка85);
Ждать Токен.ПодписатьАсинх(АлгоритмПодписиТокенаДоступа.RS256, СвойКлючPEMBase6485);
СвойКлючPEMBase6485 = "";
Запрос = СобратьСвойЗапрос85(Путь, Токен);
Ответ = Ждать Соединение.ПолучитьАсинх(Запрос);
ПроверитьСвойОтвет85(Ответ, 404, Метка85 + "|Нет", Истина);
Запрос = СобратьСвойЗапрос85(Путь, Токен);
Ответ = Ждать Соединение.ВызватьHTTPМетодАсинх("OPTIONS", Запрос);
ПроверитьСвойОтвет85(Ответ, 204, "", Ложь);
Если Ответ.Заголовки.Получить("Allow") <> "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS" Тогда
ВызватьИсключение "Свой fixture не подтвердил контракт методов";
КонецЕсли;
Запрос = СобратьСвойЗапрос85(Путь, Токен);
Запрос.УстановитьТелоИзСтроки(Метка85 + "|Книга|3|250", "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
Ответ = Ждать Соединение.ЗаписатьАсинх(Запрос);
ПроверитьСвойОтвет85(Ответ, 201, Метка85 + "|Книга|3|250", Истина);
Запрос = СобратьСвойЗапрос85(Путь, Токен);
Ответ = Ждать Соединение.ПолучитьЗаголовкиАсинх(Запрос);
ПроверитьСвойОтвет85(Ответ, 200, "", Ложь);
Если Ответ.Заголовки.Получить("X-Guide-Quantity") <> "3" Тогда
ВызватьИсключение "HEAD не подтвердил заголовок созданной своей карточки";
КонецЕсли;
Запрос = СобратьСвойЗапрос85(Путь, Токен);
Запрос.УстановитьТелоИзСтроки(Метка85 + "|Количество|4", "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
Ответ = Ждать Соединение.ИзменитьАсинх(Запрос);
ПроверитьСвойОтвет85(Ответ, 200, Метка85 + "|Книга|4|250", Истина);
Запрос = СобратьСвойЗапрос85(Путь, Токен);
Запрос.УстановитьТелоИзСтроки(Метка85 + "|Считать", "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
Ответ = Ждать Соединение.ОтправитьДляОбработкиАсинх(Запрос);
ПроверитьСвойОтвет85(Ответ, 200, Метка85 + "|Сумма|1000", Истина);
Запрос = СобратьСвойЗапрос85(Путь, Токен);
Ответ = Ждать Соединение.ПолучитьАсинх(Запрос);
ПроверитьСвойОтвет85(Ответ, 200, Метка85 + "|Книга|4|250", Истина);
Запрос = СобратьСвойЗапрос85(Путь, Токен);
Ответ = Ждать Соединение.УдалитьАсинх(Запрос);
ПроверитьСвойОтвет85(Ответ, 204, "", Ложь);
Запрос = СобратьСвойЗапрос85(Путь, Токен);
Ответ = Ждать Соединение.ПолучитьАсинх(Запрос);
ПроверитьСвойОтвет85(Ответ, 404, Метка85 + "|Нет", Истина);
Статус85 = "Собственная карточка создана3×250, изменена4×250, сумма1000, удалена и отсутствие отдельно прочитано";
Исключение
СвойКлючPEMBase6485 = "";
Статус85 = "Исход не подтвержден. Не повторяйте записи и не удаляйте автоматически; свой fixture проверьте вручную";
ВызватьИсключение "Опыт прерван без вывода ключа, пароля, Authorization и ответа неизвестного сервера";
КонецПопытки;
#Иначе
ВызватьИсключение "Положительный путь полного RS256-опыта ограничен толстым управляемым клиентом8.5";
#КонецЕсли
КонецПроцедуры
&НаКлиенте
Функция СобратьСвойЗапрос85(Путь, Токен)
Если Путь <> "/lab/" + Метка85 + "/book" Или ТипЗнч(Токен) <> Тип("ТокенДоступа") Тогда
ВызватьИсключение "Не свой ресурс или не настоящий подписанный токен";
КонецЕсли;
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "text/plain; charset=utf-8");
Заголовки.Вставить("Accept", "text/plain");
Запрос = Новый HTTPЗапрос(Путь, Заголовки);
Запрос.ДобавитьТокенДоступа(Токен);
Возврат Запрос;
КонецФункции
&НаКлиенте
Процедура ПроверитьСвойОтвет85(Ответ, Код, Текст, ЕстьТело)
Если ТипЗнч(Ответ) <> Тип("HTTPОтвет") Или Ответ.КодСостояния <> Код
Или Ответ.Заголовки.Получить("X-Guide-Session") <> Метка85 Тогда
ВызватьИсключение "Native-ответ, HTTP-статус или собственный серверный UUID не совпал";
КонецЕсли;
Если ЕстьТело Тогда
Данные = Ответ.ПолучитьТелоКакДвоичныеДанные();
Если ТипЗнч(Данные) <> Тип("ДвоичныеДанные") Или Данные.Размер() > 4096
Или Ответ.ПолучитьТелоКакСтроку("UTF-8") <> Текст Тогда
ВызватьИсключение "Полное небольшое собственное тело ответа не совпало";
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ПроверитьСеансНаСервере85()
ПроверитьСвойПустойСтенд();
КонецПроцедуры
&НаСервере
Процедура ПроверитьСвойПустойСтенд()
Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
КонецЕсли;
Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
КонецЕсли;
Источники = Новый Массив;
Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
Для Каждого Источник Из Источники Цикл
Расширения = РасширенияКонфигурации.Получить(, Источник);
Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
ВызватьИсключение "Native список расширений должен иметь тип Массив";
КонецЕсли;
Если Расширения.Количество() <> 0 Тогда
ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
КонецЕсли;
КонецПроцедурыPython: собственный loopback fixture с RSA verification, Basic и одной карточкой в памяти
Своя новая Windows VM: собственный Python-скрипт fixture
Полный ручной localhost fixture, свой UUID и новая собственная папка; при авторстве не запускается, сокеты и RSA-ключи не создаются.
import argparse
import base64
import json
import time
import uuid
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding, rsa
def decode_url64(value):
return base64.urlsafe_b64decode(value + "=" * (-len(value) % 4))
class OwnHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def log_message(self, *args):
return
def authorized(self, profile):
authorization = self.headers.get("Authorization", "")
if profile:
expected = "Basic " + base64.b64encode(("lab85:" + self.server.session).encode("ascii")).decode("ascii")
return authorization == expected
if not authorization.startswith("Bearer ") or len(authorization) > 8192:
return False
try:
header_part, payload_part, signature_part = authorization[7:].split(".")
header = json.loads(decode_url64(header_part))
payload = json.loads(decode_url64(payload_part))
if header.get("alg") != "RS256":
return False
self.server.public_key.verify(decode_url64(signature_part), (header_part + "." + payload_part).encode("ascii"), padding.PKCS1v15(), hashes.SHA256())
now = int(time.time())
return (payload.get("iss") == "urn:guide-http85" and payload.get("sub") == "lab85"
and payload.get("aud") == ["urn:guide-http85"] and payload.get("session") == self.server.session
and str(uuid.UUID(payload["jti"])) == payload["jti"]
and type(payload.get("iat")) in (int, float) and type(payload.get("nbf")) in (int, float)
and type(payload.get("exp")) in (int, float) and payload["iat"] <= now + 10
and payload["nbf"] <= now + 10 and now < payload["exp"] <= now + 310)
except Exception:
return False
def respond(self, status, text, extra=None):
payload = text.encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(payload)))
self.send_header("X-Guide-Session", self.server.session)
self.send_header("Connection", "close")
for key, value in (extra or {}).items():
self.send_header(key, value)
self.end_headers()
if self.command != "HEAD" and status not in (204, 304):
self.wfile.write(payload)
self.close_connection = True
def handle_operation(self):
self.connection.settimeout(5)
prefix = "/lab/" + self.server.session
profile = self.path == prefix + "/profile"
if self.path not in (prefix + "/book", prefix + "/profile"):
self.respond(404, "")
return
if not self.authorized(profile):
self.respond(401, "", {"WWW-Authenticate": 'Basic realm="own-guide85"' if profile else 'Bearer realm="own-guide85"'})
return
if profile:
self.respond(200, self.server.session + "|Basic")
return
if self.headers.get("Transfer-Encoding") is not None:
self.respond(400, "")
return
length_text = self.headers.get("Content-Length", "0")
if not length_text.isascii() or not length_text.isdecimal() or not 0 <= int(length_text) <= 4096:
self.respond(413, "")
return
length = int(length_text)
try:
payload = self.rfile.read(length)
if len(payload) != length:
raise ValueError("Incomplete own body")
text = payload.decode("utf-8")
except Exception:
self.respond(400, "")
return
session = self.server.session
card = self.server.card
if self.command == "OPTIONS":
self.respond(204, "", {"Allow": "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS"})
elif self.command == "PUT":
if card is not None or text != session + "|Книга|3|250":
self.respond(409, "")
return
self.server.card = {"quantity": 3, "price": 250}
self.respond(201, session + "|Книга|3|250")
elif card is None:
self.respond(404, session + "|Нет")
elif self.command in ("GET", "HEAD"):
self.respond(200, session + "|Книга|" + str(card["quantity"]) + "|250", {"X-Guide-Quantity": str(card["quantity"])})
elif self.command == "PATCH":
if text != session + "|Количество|4" or card["quantity"] != 3:
self.respond(409, "")
return
card["quantity"] = 4
self.respond(200, session + "|Книга|4|250")
elif self.command == "POST":
if text != session + "|Считать":
self.respond(400, "")
return
self.respond(200, session + "|Сумма|" + str(card["quantity"] * card["price"]))
elif self.command == "DELETE":
if card != {"quantity": 4, "price": 250}:
self.respond(409, "")
return
self.server.card = None
self.respond(204, "")
else:
self.respond(405, "")
do_GET = handle_operation
do_HEAD = handle_operation
do_PUT = handle_operation
do_PATCH = handle_operation
do_POST = handle_operation
do_DELETE = handle_operation
do_OPTIONS = handle_operation
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--session", required=True)
parser.add_argument("--own-directory", type=Path, required=True)
args = parser.parse_args()
if str(uuid.UUID(args.session)) != args.session:
raise ValueError("Use exact own canonical UUID from the 1C form")
directory = args.own_directory.resolve() / ("http85-" + args.session)
directory.mkdir(mode=448, parents=False, exist_ok=False)
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
private_pem = key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption())
with (directory / "own-private-pem-base64.txt").open("x", encoding="ascii") as file:
file.write(base64.b64encode(private_pem).decode("ascii"))
with HTTPServer(("127.0.0.1", 18085), OwnHandler) as server:
server.session = args.session
server.public_key = key.public_key()
server.card = None
print(json.dumps({"session": args.session, "key_file": str(directory / "own-private-pem-base64.txt"), "host": "127.0.0.1", "port": 18085}), flush=True)
server.serve_forever()
if __name__ == "__main__":
main()
Разбор по шагам
Legacy-конструктор с шестым Булево=false строит только паспорт для миграционного сравнения; начиная8.3.7 он не рекомендуется. Современный конструктор имеет шестым параметром5-секундный таймаут, седьмым SSL-object/Неопределено и восьмым OS-auth=false. Сеть использует только современное соединение.
Readonly Пользователь/Пароль/Прокси/ЗащищенноеСоединение только читаются для проверки собственного профиля. Прокси создан с false без system defaults. Современный host/port/timeouts/OSauth проверены до запроса. Пароль/ключ/Authorization не выводятся в статус/журнал.
Первый GET/profile проверяет реальный Basic challenge/credentials своего fixture и возвращает свою UUID|Basic. Если платформа/сервер не подтвердили паспорт, опыт останавливается, не переключается незаметно на другой сервер или на fake-успех.
Настоящий ТокенДоступа получает UTC UnixTime, lifetime300, уникальный jti, issuer/audience и свою session. Ждать ПодписатьАсинх(RS256,Base64 PEM) меняет сам токен, не принимается за готовый токен-ответ. Private field очищается после подписи. Нет algNone/неподписанного черновика для аутентификации.
СобратьСвойЗапрос принимает только свой точный путь и настоящую типизированную ТокенДоступа. ДобавитьТокенДоступа устанавливает Authorization, а не принимает произвольную строку. Fixture проверяет RSA-подпись и согласованные claims; JWT не является шифрованием данных.
GET до записи возвращает404 и только собственное UUID|Нет. Generic ВызватьHTTPМетодАсинх(OPTIONS,Request) проверяет204 и exactAllow семи методов. Оба ответа подтверждают X-Guide-Session, чтобы не принять ответ другой программы на том же порту.
PUT создает только пустую собственную карточку3×250; тело UTF-8 без BOM и Content-Type задаются отдельно. PATCH передает только изменение количества4; неизменная цена250 сохраняется. Свой fixture отказывает при повторном PUT/неподготовленном PATCH, не перезаписывает произвольные записи.
HEAD через ПолучитьЗаголовкиАсинх подтверждает X-Guide-Quantity=3 без требования тела. POST через ОтправитьДляОбработкиАсинх считает4×250=1000. Fresh GET отдельно проверяет собственную карточку4×250. Обещания всегда ожидаются Ждать, HTTP-коды и тела проверяются после результата.
DELETE вызывается только после собственного fresh GET карточки4×250 и exact-path guard; отвечает204. Затем отдельный GET404 с собственной меткой подтверждает отсутствие. DELETE не запускается в catch при неизвестном результате предыдущих операций.
Каждый непустой ответ имеет тип HTTPОтвет, точный ожидаемый код/UUID и небольшое точное тело. Неизвестный код/тип/маркер/данные/ошибка RSA останавливают опыт. Полученный HTML/ошибка сторонней программы не исполняются и не выводятся целиком.
Свой Basic-профиль проверяется отдельным GET. Затем для Bearer создается новое своё loopback соединение с пустыми Пользователь/Пароль: эти два способа аутентификации намеренно не смешиваются в одном профиле.
Как проверить результат
Без фразы/ownkey или в неподдержанном клиенте ни сеть, ни создание карточки не выполняются. Пароли/ключ/Authorization никогда не выводятся.
Basic профиль должен подтвердиться только своим UUID|Basic. JWT с измененным payload/подписью/algNone/другойsession или истекшим exp должен дать401, а не создать карточку.
Порядок:404→OPTIONS204→PUT201→HEAD200(quantity3)→PATCH200(quantity4)→POST200(1000)→GET200→DELETE204→GET404. Каждый результат отдельно проверяется, в том числе body и UUID.
Повторная запись/повторный запуск формы/неверный ключ/занятый ресурс должны отказать. При timeout после PUT/PATCH/DELETE изменения могли выполниться; НачатОпыт остаетсяtrue и автоматического повтора/cleanup нет.
Никакие исходные конфигурации/рабочая база/произвольный сайт не используются. Python не хранит карточки в файлах/БД, только в памяти этого собственного процесса.
Ожидаемый результат
Все семь реальных HTTP-promise методов показаны в содержательном собственном цикле с подписью/проверкой настоящего JWT, точными статусами и подтверждением данных/удаления. Никакая сеть или криптография автором не выполнялись.
На что обратить внимание
Положительный полный опыт только thick managed: классы HTTP доступны шире, но enum/подпись имеют собственные контексты. Не путайте HTTP API capability с подготовленным running server/client environment.
Старый six-argument constructor считает6-й параметр БулевоSSL; современный — числовой timeout. SSL-object в современном7-м параметре, неБулево. Legacy рассматривается для миграции, новые реальные запросы им не отправляются.
ИнтернетПрокси(false) не берет системные настройки/inetcfg.xml. Это не доказательство отсутствия внешнего маршрута компьютера; выделенная VM и loopback обязательны. Plain HTTP/Basic не допустимы для production-секретов, в примере используются только собственная VM и учебный nonce.
JWT подписан RS256, но не зашифрован. Содержимое claims можно прочитать; private key нельзя передавать/публиковать. cryptography public-key verification не превращает UUID-метку в доказательство владения реальным чужим сервером.
Получить/POST/Generic могут записывать тело в выходной файл, если передан filename. Здесь этот параметр не задан: тело берется из HTTPОтвет. HEAD/204 не требуют body, Content-Length у HEAD может описывать ресурс при отсутствии передачи тела.
PUT/PATCH/POST/DELETE могут изменить ресурс при потерянном ответе. Нет атомарного read-then-write или автоматического retry. НачатОпыт выставлен заранее; catch не удаляет/восстанавливает ресурс.
RSA-генерация/private-key файл/server.serve_forever предназначены только для вашего осознанного ручного старта своего fixture. Автор и статические тесты их не выполняют, не создают backups/archives и не отправляют запросы. RuntimeVerifiedScenarios=0.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.