266 lines
22 KiB
Plaintext
266 lines
22 KiB
Plaintext
#import "head.typ" as head : *
|
||
|
||
#show: MyStyle
|
||
|
||
|
||
= KMTP Specification
|
||
|
||
== Область применения
|
||
|
||
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
|
||
|
||
Ключевым элементом данного протокола является структура байт под названием
|
||
"сообщение". Они бывают двух видов: "запросы" (отправляются клиентом) и
|
||
"ответы" (отправляются сервером). Но есть ещё одно деление: на "мастер-сообщение"
|
||
и "суб-сообщение" (применимое к обоим типам адресанта сообщения).
|
||
Контент каждого канала начинается с мастер-сообщения своего типа, а все
|
||
последующие сообщения, отправленные по каналу, (если таковые есть)
|
||
записаны в формате "суб-сообщений".
|
||
У каждого типа есть свой формат, форматы различаются набором обязатльных "полей".
|
||
|
||
Каждый kmtp-канал содержит последовательность kmtp-сообщений. Первое сообщение -
|
||
мастер-сообщение, остальные - суб-сообщения.
|
||
При этом все суб-сообщения в канале как-бы считаются телом мастер-сообщения.
|
||
Поэтому если есть поток суб-сообщений, приложенный к мастер-сообщению, то другого
|
||
тела у мастер сообщения нет. Есть специальное поле заголовка мастер-сообщения,
|
||
сообщающее, идёт ли после мастер-сообщения поток суб-сообщений. О поляъ расскажу позже.
|
||
Поток суб-сообщений оканчивается когда вместо следующего сообщения отправитель передаёт
|
||
по каналу LF (line feed). Опять же, если потока нет, о передавать этот LF
|
||
в конце не нужно.
|
||
|
||
Сообщения содержат заголовок, чей формат полность определён и тело, называемое "кладом".
|
||
Клад бывает 4 типов: отсутствующий (none) - это когда клад отсутствует, "письмо" (letter) -
|
||
это когда клад имеет заранее известный (из заголовка сообщения) конечный размер,
|
||
"вкид" (vkid) - это когда клад продолжается до самого конца канала без возможности
|
||
вернуться обратно к kmtp-протоколу, при чем бесконечность вкида известна заранее. А ещё есть
|
||
"отвал" (otval) - это когда размер клада заранее неизвестен и должен быть на ходу
|
||
определён тем, кто принимает отвал. После перехода на отвал канал ещё может обратно
|
||
вернуться к протоколу kmtp, однако определить этот момент возврата не понимая
|
||
протокола, который использует отвал - невозможно. Потому при появлении в канале отвала
|
||
любой прокси, что не понимает протокол отвала сразу потеряет возможность распознавать весь
|
||
дальнейший трафик.
|
||
Поток суб-сообщений - это вклад. А именно "вкид". Чтобы указать принимающему
|
||
что вкид нашего мастер-сообщения это имеенно поток суб-сообщений, надо в специальное
|
||
поле для типа клада записать "sub-kmtp". При этом если клиент отправит поле типа клада
|
||
"sub-kmtp", все понимают, что он сейчас начнёт отправлять именно поток суб-запросов,
|
||
а у сервера значение значения "sub-kmtp" в этои поле будет означать отправку потока
|
||
суб-ответов.
|
||
|
||
Клиент посылает серверу запросы и на каждый запрос приходит ровно один ответ
|
||
(в том же порядке, в котором соответствующие запросы были заданы). На
|
||
мастер-запрос (master-request) приходит мастер-ответ (master-response),
|
||
на суб-запрос (sub-request) приходит суб-ответ (sub-response).
|
||
|
||
Но сервер также может в любой момент времени отправить в выходной канал особый вид
|
||
сообщения, называемое "событие" (event). Событие адресовано мастер-запросу, а не
|
||
суб-запросам. События имеют такую же структуру, что и суб-ответы, отличаются они только тем,
|
||
что в заголовке события "поле Status" обязано быть равно "Event (DC)".
|
||
О том что такое "поле Status" будет сказано позже.
|
||
|
||
== Формат сообщения
|
||
|
||
Любое сообщение начинается с заголовка.
|
||
Заголовок начинается с нескольких обязательных полей. Их число, назначение и формат заранее
|
||
определены для каждого типа сообщений.
|
||
После блока обязательных полей следует множество именованных полей. Их число не определено,
|
||
пользователи kmtp вольны сами придумывать эти поля.
|
||
Оканчивается заголовок символом LF (Перевод строки). После заголовка идёт клад.
|
||
Если клад не none и клад окончился возвращением к 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[Client-Klad-Seek-Pos]: Формат тот же что и у Client-Klad-Sek-Pos
|
||
(#math.penta.filled). Но здесь указывается сдвиг относительно ресурса на сервере.
|
||
Так, к примеру, клиент может указать с какого момента продолжить скачивание большого
|
||
файла с сервера.
|
||
+ #field-name-decl[Body-Type]: если за сообщением следует "тело",
|
||
то здесь указывается как
|
||
его прочитать. Это может быть пустая строка, если тела нет, число в 16-ричной
|
||
системе счисления, если произошел отнюх, "otval" если произошел отвал,
|
||
"vkid", если произошел вкид (как можешь убедиться, эти 4 случая невозможно
|
||
перепутать) #math.penta.filled. Важно! Если Body-Type пуст, то клада нет, сообщение
|
||
оканчивается там, где оканчивается его заголовок. Но если Body-Type это "0" или
|
||
"00 ... 0", то клад есть, го тип - письмо, и после пустого письма должен стоять
|
||
обязательный перевод строки. Да, как бы это не было странно, но у суб-сообщений
|
||
может быть свой клад, получается что клад суб-сообщения это клад в кладе
|
||
мастер-сообщения.
|
||
+ #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. Нет ограничения на размер этого числа.
|
||
|
||
Мастер-ответ отличается от суб-ответа тем, что в начале присутствует одно мастер поле:
|
||
+ #master-field-name-decl[Auth-Password]: Это то, что ты подумал.
|
||
|
||
Status мастер-ответа не может быть #roman-status[DC] (Event).
|
||
|
||
Получается, что список обязательных полей, использующих синтаксис `=HEX>str or 'str'`,
|
||
который дефолтен для именованных полей, довольно мал: это все мастер-поля + поле
|
||
Location. Нужно учесть, что значение поля Location не должно содержать октета 0.
|
||
|
||
Этот документ сам определяет ряд именованных полей сообщения.
|
||
Есть целый ряд незапланированных полей, вида `"Proxy-" + n + "-" + Name`,
|
||
где n - натуральное число, а Name - имя для поля. Это будет поле, переданное
|
||
n-ым с конца прокси (отсчет начинается от стороны получателя). От одного прокси может
|
||
передаваться много разных полей, поэтому можно дополнительно указать суб-имя `Name`.
|
||
|
||
+ #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)\
|
||
Запрашиваемый ресурс / запрашиваямая операция над ресурсом недопустима в стране
|
||
клиента. Отличие от DCCLII в том, что другим клиентам из других стран этот же
|
||
ресурс может быть доступен.
|
||
|
||
== Широко известные функции. Поле Function.
|
||
|
||
=== Предисловие
|
||
|
||
Множество кодов статуса ответа зафиксировано и не расширяемо, а вот свои функции серверу
|
||
придумывать не запрещено. Есть некоторые принятые значения для определённых
|
||
имён функций. Здесь представлен неисчерпывающий список таких имён функций.
|
||
|
||
=== read
|
||
|
||
Читает весь ресурс (файл) или его часть. Для указания интересующего нас постфикса
|
||
клиент заполняет своё поле Resource-Klad-Seek-Pos. |