263 lines
21 KiB
Plaintext
263 lines
21 KiB
Plaintext
#import "head.typ" as head : *
|
||
|
||
#show: MyStyle
|
||
|
||
|
||
= KMTP, Sub-KMTP Спецификация
|
||
|
||
== Область применения
|
||
|
||
KMTP это протокол, созданный специально для будущей видеоигры KM (Используется
|
||
в рамках KM project). Он нацелен на замену чрезвычайно раздутого
|
||
протокола http+websocket. KMTP (KM Transport Protocol) это его официальное название,
|
||
однако в самом мире игры KM он известен как STP (Suckless
|
||
Transport Protocol).
|
||
|
||
KMTP должен использоваться поверх надёжного транспортного протокола,
|
||
создающего двунаправленный поток байт,
|
||
такого как TCP, так как даже малейшая ошибка может привести к непредсказуемым последствиям.
|
||
|
||
KMTP применяется когда между двумя общающимися процессами
|
||
выделено отношение "клиент-сервер". KMTP предполагает, что на сервере расположено
|
||
множество файлов (ресурсов) в древовидной структуре, а клиент обращается к ним,
|
||
сообщая какую функцию он хочет произвести над тем или иным ресурсом. После этого
|
||
возможны передача серверу
|
||
потока байт (аргумент функции) и приём от сервера потока байт (результат функции).
|
||
|
||
Очень часто kmtp будет применяться как обёртка для протокола более высокого уровня,
|
||
хотя под это понятие попадает даже содержимое jpeg картинки, скачанной с файлового
|
||
сервера.
|
||
|
||
В начале соединения клиент может предствиться передав "логин" и "пароль".
|
||
|
||
Каналы связи называются
|
||
- client's output / server's output / request channel
|
||
- client's input / servers's output / response channel
|
||
|
||
Ключевым элементом данного протокола является структура байт под названием
|
||
"сообщение". Они бывают двух видов: "запросы" (отправляются клиентом) и
|
||
"ответы" (отправляются сервером). Но есть ещё одно деление: на "мастер-сообщение"
|
||
и "суб-сообщение" (применимое к обоим типам адресанта сообщения).
|
||
Контент каждого канала начинается с мастер-сообщения своего типа.
|
||
Если клиент и сервер договорились сделать "апгрейд" до sub-kmtp, то все дальнейшие
|
||
сообщения будут записаны в формате "суб-сообщений".
|
||
У каждого типа есть свой формат, форматы различаются набором обязательных "полей".
|
||
|
||
Каждый канал содержит только мастер-сообщение (соответсвующего типа) и ничего более.
|
||
Поток суб-сообений это часть местер сообщения. Естесственно, поток суб-сообщений
|
||
будет присутсвовать только когда был произведён апгрейд до sub-kmtp.
|
||
В этом случае сначала идёт мастер-заголовок сообщения, а всё остальное (та часть
|
||
сообщения, называющаяся кладом) - это sub-kmtp. Вместо него, конечно же, мог бы быть
|
||
совсем другой клад мастер сообщения.
|
||
|
||
Сообщения содержат заголовок, чей формат полность определён и тело, называемое "кладом".
|
||
Клад бывает 4 типов: "письмо" (letter) -
|
||
это когда клад имеет заранее известный (из заголовка сообщения) конечный размер,
|
||
"вкид" (vkid) - это когда клад продолжается до самого конца канала без возможности
|
||
вернуться обратно к kmtp-протоколу, при чем бесконечность вкида известна заранее. А ещё есть
|
||
"отвал" (otval) - это когда размер клада заранее неизвестен и должен быть на ходу
|
||
определён тем, кто принимает отвал. После перехода на отвал канал ещё может обратно
|
||
вернуться к протоколу sub-kmtp, однако определить этот момент возврата не понимая
|
||
протокола, который использует отвал - невозможно. Потому при появлении в канале отвала
|
||
любой прокси, что не понимает протокол отвала сразу потеряет возможность распознавать весь
|
||
дальнейший трафик. Как можно заметить практического смысла использовать отвал
|
||
вместо вкида в мастер-kmtp сообщении нет.
|
||
Поток суб-сообщений - это клад. А именно "отвал". Чтобы указать принимающему
|
||
что вкид нашего мастер-сообщения это имеенно поток суб-сообщений, надо в специальное
|
||
поле для типа клада записать "sub-kmtp". При этом если клиент отправит поле типа клада
|
||
"sub-kmtp", все понимают, что он сейчас начнёт отправлять именно поток суб-запросов,
|
||
а у сервера значение значения "sub-kmtp" в этои поле будет означать отправку потока
|
||
суб-ответов.
|
||
|
||
Клиент посылает серверу запросы и на каждый запрос приходит ровно один ответ
|
||
(в том же порядке, в котором соответствующие запросы были заданы). На
|
||
мастер-запрос (master-request) приходит мастер-ответ (master-response),
|
||
на суб-запрос (sub-request) приходит суб-ответ (sub-response).
|
||
Но! Сервер также может в любой момент времени отправить в выходной канал особый вид
|
||
сообщения, называемое "событие" (event). Событие адресовано мастер-запросу, а не
|
||
суб-запросам. События имеют такую же структуру, что и суб-ответы, отличаются они только тем,
|
||
что в заголовке события "поле Status" обязано быть равно "Event (DC)".
|
||
О том что такое "поле Status" будет сказано позже.
|
||
|
||
== Формат сообщения
|
||
|
||
Любое сообщение начинается с заголовка.
|
||
Заголовок начинается с нескольких обязательных полей. Их число, назначение и формат заранее
|
||
определены для каждого типа сообщений.
|
||
После блока обязательных полей следует множество именованных полей. Их число не определено,
|
||
пользователи kmtp вольны сами придумывать эти поля.
|
||
Оканчивается заголовок символом LF (Перевод строки). После заголовка идёт клад.
|
||
|
||
Поля имеют формат "имя" + ":"/">" + "значение" + LF. При этом есть безымянные поля, где
|
||
"имя" пусто.
|
||
"имя" это строка из символов `[a-zA-Z0-9-_+=/]` , что овпадает с форматом
|
||
ключей в kmon (о kmon можно прочитать в соответствующем документе).
|
||
Имя и значение разделены символом ":", но для некоторых 3-ёх обязательных полей
|
||
":" заменяется на ">". Эти поля: "Virtual-Host", "Auth-Username", "Auth-Password".
|
||
Они отличаются от других тем, что они присутствуют только в мастер-сообщениях.
|
||
Об этом позже будет рассказано.
|
||
"Значение" несёт в себе строку произвольного формата, но важно то, как она обёрнута.
|
||
Если надо передать строку str, значение записывается как "=" + hex(len(str)) + ">" + str,
|
||
при этом если так получилось, что str не содержит символа `'` (ascii 0x27), то можно
|
||
записать значение как "'" + str + "'". Семантика сообщения от выбора способа записи
|
||
значения не меняется. Такой формат совпадает с форматом строк в kmon.
|
||
Некоторые обязательные поля, о которых известно, что они не содержат символа перевода строки,
|
||
передаются без форматирования, напрямую: "имя" + ":" + str + LF.
|
||
Поля, которые имеют особую запись будут помечены в списке полей символом #math.penta.filled .
|
||
|
||
Сейчас будут полность определены обязательне поля всех 4 типов сообщений.
|
||
Хотя им всем даны имена, в канале они опущены, ибо известны заранее.
|
||
|
||
#let field-name-decl(name) = bold(name)
|
||
#let master-field-name-decl(name) = bold(name)
|
||
|
||
Устройство блока заголовка суб-запроса:
|
||
+ #field-name-decl[Location]: Путь нужного ресурса на сервере. В мастер-запросе
|
||
не может быть относительным путём, ведь нам неотчего отталкиваться. Правила такие же,
|
||
как для юникс-путя.
|
||
+ #field-name-decl[Function]: Функция, производимая над ресурсом.
|
||
Ближайшим аналогом будет
|
||
поле method из http. KMTP определяет ряд функций с определённым значение,
|
||
однако пользователи kmtp могут сами определять свои функции, если сервер
|
||
сможет их понять. Function имеет ограничение на используемые символы (функции
|
||
именуются как ключи в kmon), что делает ненужным оборачивание в кавычки.
|
||
(#math.penta.filled)
|
||
+ #field-name-decl[Client-Klad-Seek-Pos]: В этом поле записывается неотрицательное
|
||
число (в шестнадцатиричной системе счисления) #math.penta.filled. Пустая строка
|
||
эквивалентна нулю. Значение зависит от функции, но принято использовать его для
|
||
указания сдвига данных, передаваемых в кладе клиента (например, если клиент
|
||
загружал большой файл, но соединение оборвалось, это поле можно использовать
|
||
чтобы указать с какого места продолжается закачка).
|
||
+ #field-name-decl[Resource-Klad-Seek-Pos]: Формат тот же что и у Client-Klad-Sek-Pos
|
||
(#math.penta.filled). Но здесь указывается сдвиг относительно ресурса на сервере.
|
||
Так, к примеру, клиент может указать с какого момента продолжить скачивание большого
|
||
файла с сервера.
|
||
+ #field-name-decl[Body-Type]: если за сообщением следует "тело",
|
||
то здесь указывается как
|
||
его прочитать. Это может быть число в 16-ричной
|
||
системе счисления, если передано письмо, "otval" если произошел отвал,
|
||
"vkid", если произошел вкид (как можешь убедиться, эти 3 случая невозможно
|
||
перепутать) #math.penta.filled. Да, как бы это не было странно, но у суб-сообщений
|
||
может быть свой клад, получается что клад суб-сообщения это клад в кладе
|
||
мастер-сообщения.
|
||
+ #bold[Upgrade-Name]: Здесь указывается узнаваемое имя протокола,
|
||
к которому мы переходим. На это поле наложены такие же ограничения, как
|
||
на поле Function (используются только символы, допустимые в kmon
|
||
ключе), поэтому кавычки не используются #math.penta.filled. Пустая строка допустима.
|
||
Имеет смысл только если клад не пуст.
|
||
|
||
Для удобства мастер-запрос очень похож на суб-запрос. Различие лишь в том, что
|
||
в мастер-запрос в начало добавляются 3 мастер-поля:
|
||
+ #master-field-name-decl[Virtual-Host]
|
||
+ #master-field-name-decl[Auth-Username]
|
||
+ #master-field-name-decl[Auth-Password]
|
||
Каждое из них это то что ты подумал это это. Мастер-поле в сериализованном виде
|
||
начинается не с ":", как все обязательные поля, а с ">".
|
||
|
||
В мастер-запросе поле Location не может быть относительным путём, а в суб-запросах
|
||
может, точкой отсчета для относительных путей будет путь указанный в мастер-запросе.
|
||
|
||
#let roman-status(x) = bold(x)
|
||
|
||
Усройство блока заголовка суб-ответа:
|
||
+ #field-name-decl[Status]: одна строка из определённого набора, необходимая для
|
||
правильной интерпретации ответа. #math.penta.filled.
|
||
+ #field-name-decl[Resource-Klad-Seek-Pos]: #math.penta.filled.
|
||
+ #field-name-decl[Client-Klad-Seek-Pos]: #math.penta.filled.
|
||
+ #field-name-decl[Body-Type]: #math.penta.filled.
|
||
+ #field-name-decl[Upgrade-Name]: #math.penta.filled.
|
||
+ #field-name-decl[Last-Change-Time]: число (шестнадцатеричное),
|
||
отмеряющее время последнего изменения ресурса.
|
||
Не обязано быть привязано к реальному времени. Может быть пустой строкой - это
|
||
эквивалентно нулю. #math.penta.filled. Нет ограничения на размер этого числа.
|
||
|
||
Мастер-ответ ничем не отличается от суб-ответа.
|
||
|
||
Status мастер-ответа не может быть #roman-status[DC] (Event).
|
||
|
||
Получается, что список обязательных полей, использующих синтаксис `=HEX>str or 'str'`,
|
||
который дефолтен для именованных полей, довольно мал: это все мастер-поля + поле
|
||
Location. Нужно учесть, что значение поля Location не должно содержать октета 0,
|
||
хотя это наврятле будет проверяться.
|
||
|
||
Этот документ сам определяет ряд именованных полей сообщения.
|
||
Есть целый ряд незапланированных полей, вида `"Proxy-" + n + "-" + Name`,
|
||
где n - натуральное число, а Name - имя для поля. Это будет поле, переданное
|
||
n-ым с конца прокси (отсчет начинается от стороны получателя). От одного прокси может
|
||
передаваться много разных полей, поэтому можно дополнительно указать суб-имя `Name`.
|
||
|
||
Считается, что kmtp-прокси при прохождении через него заголовка сообщения, порядок
|
||
всех полей, начинающихся с `"Proxy-"`, инкрементирует, а если хочет и свои поля
|
||
добавить, Поставит в их имена префикс `"Proxy-1-"`.
|
||
|
||
+ #field-name-decl[Proxy-n-Address]: Адрес клиента, который видит прокси, но конечный
|
||
сервер не видит. Здесь #field-name-decl[Proxy-n-Address] - это параметризованное имя
|
||
поля.
|
||
|
||
== Коды статуса. Поле Status
|
||
|
||
=== Предисловие
|
||
|
||
У каждого сообщения сервера (ответа или события) поле Status
|
||
(код статуса) принимает одно
|
||
из возможных значений. Сейчас будут перечислены все допустимые значения
|
||
статуса ответа и их смысл.
|
||
|
||
=== Самые частые коды статуса, выражающие успех (CC..., DC...)
|
||
- #roman-status[CC] (Success)
|
||
- #roman-status[DC] (Event)
|
||
|
||
=== Коды перенаправления (CCC...)
|
||
- #roman-status[CCCI] (Moved Permanently)
|
||
- #roman-status[CCCIII] (See other)
|
||
- #roman-status[CCCVII] (Temporary Redirect)
|
||
- #roman-status[CCCX] (Go Back)
|
||
|
||
#heading(level: 3)[Ошибка когда виноват клиент (CD...)]
|
||
- #roman-status[CD] (Bad request)
|
||
- #roman-status[CDI] (Unauthorized)
|
||
- #roman-status[CDIII] (Forbidden)
|
||
- #roman-status[CDIV] (Not Found)
|
||
- #roman-status[CDV] (Inadmissible Function)
|
||
- #roman-status[CDX] (Gone)
|
||
- #roman-status[CDXIII] (Klad Too Long)
|
||
- #roman-status[CDXIV] (Path Too Long)
|
||
- #roman-status[CDXV] (Unsupported Media Type)
|
||
- #roman-status[CDXXXI] (Header Too Long)
|
||
- #roman-status[CDLXI] (Profile Not Found)
|
||
- #roman-status[CDLXII] (Incorrect Seek Pos Client Side)
|
||
- #roman-status[CDLXIV] (Inadmissible Body Type)
|
||
- #roman-status[CDLXXVI] (Incorrect Seek Pos Resource Side)
|
||
|
||
#heading(level: 3)[Ошибка когда виноват сервер, прокси или машина, на которой
|
||
они работают (D...)]
|
||
- #roman-status[D] (Internal Server Error)
|
||
- #roman-status[DII] (Destination Unreachable)
|
||
- #roman-status[DIII] (Service Unavailable)
|
||
- #roman-status[DXI] (System Failure)
|
||
- #roman-status[DXII] (Undelivered)
|
||
- #sparkingGreenSunEffect(1)[DXVIII]
|
||
(Nuclear Reactor That Was Managed By This Computer Was Blown Up By Terrorists) \
|
||
Упс, это спойлеры к KM.
|
||
|
||
#heading(level: 3)[Ошибка когда виновато общество в котором вы живёте (DCC...)]
|
||
- #roman-status[DCCLI] (Unavailable For Legal Reasons) \
|
||
Запрашиваемый ресурс / запрашиваямая операция над ресурсом недопустима в стране
|
||
сервера.
|
||
- #roman-status[DCCLII] (Cancelled For Legal Reasons)\
|
||
Запрашиваемый ресурс / запрашиваямая операция над ресурсом недопустима в стране
|
||
клиента. Отличие от DCCLI в том, что другим клиентам из других стран этот же
|
||
ресурс может быть доступен.
|
||
|
||
== Широко известные функции. Поле Function.
|
||
|
||
=== Предисловие
|
||
|
||
Множество кодов статуса ответа зафиксировано и не расширяемо, а вот свои функции серверу
|
||
придумывать не запрещено. Есть некоторые принятые значения для определённых
|
||
имён функций. Здесь представлен неисчерпывающий список таких имён функций.
|
||
|
||
=== read
|
||
|
||
Читает весь ресурс (файл) или его часть. Для указания интересующего нас постфикса
|
||
клиент заполняет своё поле Resource-Klad-Seek-Pos. |