init here

This commit is contained in:
2025-11-26 21:32:41 +03:00
commit 33c97acade
91 changed files with 9155 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
модуль хттп
импорт "стд::вывод"
импорт "исх/строка"
импорт "исх/сеть/тцп"
фн сериализовать хттп-запрос(хост: Строка, обращение: ХттпОбращение): Строка {
пусть метод := обращение.метод
если метод = "" {
метод := "GET"
}
пусть путь := обращение.путь
если путь = "" {
путь := "/"
}
пусть первая := строка.ф("$стр $стр HTTP/1.1\r\n", метод, путь)
пусть есть длина := ложь
пусть выход := первая
цикл [номер]заглавие среди обращение.заглавия {
если заглавие.имя = "Content-Length" { есть длина := истина }
выход := строка.собрать(выход, строка.ф("$стр: $стр\r\n", заглавие.имя, заглавие.значение))
}
выход := строка.собрать(выход, строка.ф("Host: $стр\r\n", хост))
если ~ есть длина {
выход := строка.собрать(выход, строка.ф("Content-Length: $цел\r\n", длина(обращение.туловище(:Строка8))))
}
выход := строка.собрать(выход, "\r\n")
если длина(обращение.туловище) > 0 { выход := строка.собрать(выход, обращение.туловище) }
вернуть выход
}
фн послать далеко и надолго*(хост: Строка, порт: Цел64, обращение: ХттпОбращение): ХттпОтвет {
пусть соединение = тцп.подключиться(хост, порт)
пусть данные = сериализовать хттп-запрос(хост, обращение)
соединение.записать(данные)
вывод.ф("Ждем хттп ответ\n")
пусть ответ = разобрать хттп ответ(соединение)
соединение.закрыть()
вывод.ф("Дождались\n")
вернуть ответ
}
фн послать на три буквы*(хост: Строка, порт: Цел64, путь: Строка): ХттпОтвет {
вернуть послать далеко и надолго(хост, порт, ХттпОбращение{ метод: "GET", путь: путь })
}
тип Урл* = класс {
хост*: Строка := ""
порт*: Цел64 := 0
путь*: Строка := ""
}
фн парсить урл*(стр: Строка, оплошность := Строка): Урл {
оплошность := ""
пусть с = строка.обрезать пробельные символы(стр)
если с = "" {
оплошность := "пустой урл"
вернуть Урл{}
}
пусть схема := "http"
пусть ост := с
если строка.разделить(с, "://", схема, ост) {
схема := строка.обрезать пробельные символы(схема)
} иначе {
// без схемы считаем http
схема := "http"
ост := с
}
// отделяем authority и путь
пусть № слеша = строка.индекс(ост, 0, "/")
пусть авторитет := ""
пусть путь := ""
если № слеша >= 0 {
пусть ост8 = ост(:Строка8)
авторитет := строка.извлечь(ост, 0, № слеша)
путь := строка.извлечь(ост, № слеша, длина(ост8) - № слеша)
} иначе {
авторитет := ост
путь := ""
}
авторитет := строка.обрезать пробельные символы(авторитет)
если авторитет = "" {
оплошность := "пустой хост в урл"
вернуть Урл{}
}
// парсим хост и порт
пусть хост := авторитет
пусть порт: Цел64 := 0
пусть № двоеточия = строка.индекс(авторитет, 0, ":")
если № двоеточия >= 0 {
пусть ав8 = авторитет(:Строка8)
хост := строка.извлечь(авторитет, 0, № двоеточия)
пусть порт строка := строка.извлечь(авторитет, № двоеточия + 1, длина(ав8) - (№ двоеточия + 1))
порт строка := строка.обрезать пробельные символы(порт строка)
пусть порт значение := 0
пусть № байта := 0
если ~ строка.извлечь цел(порт строка, № байта, порт значение) {
оплошность := "некорректный порт в урл"
вернуть Урл{}
}
если порт значение <= 0 | порт значение > 65535 {
оплошность := "порт вне диапазона в урл"
вернуть Урл{}
}
порт := порт значение
} иначе {
если схема = "https" {
порт := 443
} иначе {
порт := 80
}
}
хост := строка.обрезать пробельные символы(хост)
если хост = "" {
оплошность := "пустой хост в урл"
вернуть Урл{}
}
если путь = "" { путь := "/" }
вернуть Урл{ хост: хост, порт: порт, путь: путь }
}

View File

@@ -0,0 +1,120 @@
модуль маршрутизатор
импорт "стд::контейнеры/словарь/стр-стр"
импорт "исх/строка"
импорт "исх/сеть/хттп"
импорт "исх/массивы"
тип Обращение* = класс (хттп.ХттпОбращение) {
запрос-в-пути*: стр-стр.Словарь := стр-стр.Словарь{}
}
тип ОбработчикМаршрута = фн (путь: Строка, параметры: массивы.Строки, обращение: Обращение): хттп.ХттпОтвет
тип Маршрут = класс {
путь: Строка := "/"
обработчик: ОбработчикМаршрута := позже
методы: массивы.Строки := массивы.Строки[]
}
фн (м: Маршрут) подходит по пути(обращение: хттп.ХттпОбращение, параметры := массивы.Строки): Лог {
пусть части пути по запросу = строка.разобрать(обращение.путь, "?")
пусть части пути обращения = строка.разобрать(части пути по запросу[0], "/")
пусть части пути маршрута = строка.разобрать(м.путь, "/")
цикл [номер]часть пути маршрута среди части пути маршрута {
если часть пути маршрута = "*" {
пусть ай := номер
пока ай < длина(части пути обращения) {
параметры.добавить(части пути обращения[ай])
ай++
}
вернуть истина
}
если номер >= длина(части пути обращения) {
вернуть ложь
}
пусть часть пути обращения = части пути обращения[номер]
если часть пути маршрута = "$" {
параметры.добавить(часть пути обращения)
} иначе если часть пути маршрута # часть пути обращения {
вернуть ложь
}
}
вернуть длина(части пути маршрута) = длина(части пути обращения)
}
фн (м: Маршрут) подходит по методу(обращение: хттп.ХттпОбращение): Лог {
цикл [номер]метод среди м.методы {
если метод = обращение.метод {
вернуть истина
}
}
вернуть ложь
}
фн (м: Маршрут) подходит для*(обращение: хттп.ХттпОбращение, параметры := массивы.Строки): Лог {
вернуть м.подходит по пути(обращение, параметры) & м.подходит по методу(обращение)
}
тип Маршруты = []Маршрут
тип Маршрутизатор* = класс {
маршруты: Маршруты := Маршруты[]
обработчик_404: ОбработчикМаршрута := обработчик_404
}
фн (м: Маршрутизатор) добавить маршрут*(путь: Строка, методы: массивы.Строки, обработчик: ОбработчикМаршрута) {
м.маршруты.добавить(Маршрут{путь: путь, обработчик: обработчик, методы: методы})
}
фн (м: Маршрутизатор) обработать обращение*(обращение: хттп.ХттпОбращение): хттп.ХттпОтвет {
пусть обращение маршрутизатора = Обращение{
метод: обращение.метод,
путь: обращение.путь,
версия: обращение.версия,
заглавия: обращение.заглавия,
туловище: обращение.туловище,
запрос-в-пути: разобрать-запрос-в-пути(обращение.путь)
}
цикл [номер]маршрут среди м.маршруты {
пусть параметры := массивы.Строки[]
если маршрут.подходит для(обращение, параметры) {
вернуть маршрут.обработчик(обращение.путь, параметры, обращение маршрутизатора)
}
}
вернуть м.обработчик_404(обращение.путь, массивы.Строки[], обращение маршрутизатора)
}
фн разобрать-запрос-в-пути(путь: Строка): стр-стр.Словарь {
пусть части = строка.разобрать(путь, "?")
если длина(части) < 2 {
вернуть стр-стр.Словарь{}
}
пусть параметры = строка.разобрать(части[1], "&")
пусть словарь = стр-стр.Словарь{}
цикл [номер]параметр среди параметры {
пусть пара = строка.разобрать(параметр, "=")
если длина(пара) = 2 {
словарь.добавить(пара[0], пара[1])
} иначе если длина(пара) = 1 {
словарь.добавить(пара[0], "")
}
}
вернуть словарь
}

View File

@@ -0,0 +1,8 @@
модуль маршрутизатор
импорт "исх/массивы"
импорт "исх/сеть/хттп"
фн обработчик_404*(путь: Строка, параметры: массивы.Строки, обращение: Обращение): хттп.ХттпОтвет {
вернуть хттп.ответ_404()
}

View File

@@ -0,0 +1,118 @@
модуль хттп
фн ответ_201*(): ХттпОтвет {
вернуть ХттпОтвет{
код: 201,
состояние: "Created",
туловище: "Тварь успешно создана.",
}
}
фн ответ_204*(): ХттпОтвет {
вернуть ХттпОтвет{
код: 204,
состояние: "No Content",
туловище: "Пожалуйста, оставайтесь на месте. За вами уже выехали.",
}
}
фн ответ_400*(): ХттпОтвет {
вернуть ХттпОтвет{
код: 400,
состояние: "Bad Request",
туловище: "Некорректный запрос. Пожалуйста, проверьте правильность запроса и повторите попытку.",
}
}
фн ответ_401*(): ХттпОтвет {
вернуть ХттпОтвет{
код: 401,
состояние: "Unauthorized",
туловище: "Требуется аутентификация. Пожалуйста, предоставьте свои паспортные данные и повторите запрос.",
}
}
фн ответ_402*(): ХттпОтвет {
вернуть ХттпОтвет{
код: 402,
состояние: "Payment Required",
туловище: "Доступ к запрашиваемому ресурсу требует оплаты. Пожалуйста, свяжитесь с администратором для получения дополнительной информации.",
}
}
фн ответ_403*(): ХттпОтвет {
вернуть ХттпОтвет{
код: 403,
состояние: "Forbidden",
туловище: "Вы были репрессированы. Пожалуйста, перейдите по ссылке: http://сибирь.рф",
}
}
фн ответ_422*(): ХттпОтвет {
вернуть ХттпОтвет{
код: 422,
состояние: "Unprocessable Entity",
туловище: "Неперевариваемая тварь.",
}
}
фн ответ_404*(): ХттпОтвет {
вернуть ХттпОтвет{
код: 404,
состояние: "Not Found",
туловище: "Запрашиваемый ресурс не найден на сервере.",
}
}
фн ответ_500*(): ХттпОтвет {
вернуть ХттпОтвет{
код: 500,
состояние: "Internal Server Error",
туловище: "Просим быть внимательными и бдительными. Оглядывайтесь вверх и по сторонам. Что-то произошло непонятное.",
}
}
фн создать ответ*(база: ХттпОтвет, расширение: ХттпОтвет): ХттпОтвет {
пусть пустой ответ = ХттпОтвет{}
пусть ответ = ХттпОтвет{}
если расширение.код # пустой ответ.код {
ответ.код := расширение.код
} иначе {
ответ.код := база.код
}
если расширение.состояние # пустой ответ.состояние {
ответ.состояние := расширение.состояние
} иначе {
ответ.состояние := база.состояние
}
цикл [номер]заглавие среди база.заглавия {
ответ.заглавия.добавить(заглавие)
}
цикл [номер]заглавие среди расширение.заглавия {
пусть нашлось := ложь
цикл [уемер]существующее среди ответ.заглавия {
если заглавие.имя = существующее.имя {
существующее.значение := заглавие.значение
нашлось := истина
прервать
}
}
если ~нашлось {
ответ.заглавия.добавить(заглавие)
}
}
если расширение.туловище # "" {
ответ.туловище := расширение.туловище
} иначе {
ответ.туловище := база.туловище
}
вернуть ответ
}

View File

@@ -0,0 +1,235 @@
модуль хттп
импорт "исх/строка"
импорт "исх/вперед-назад"
импорт "исх/сеть/тцп"
фн сериализовать хттп ответ*(р: ХттпОтвет): Строка {
пусть ответ := строка.собрать(строка.ф("$стр $цел $стр\r\n", р.версия, р.код, р.состояние))
пусть есть размер контента := ложь
цикл [номер]заглавие среди р.заглавия {
если заглавие.имя = "Content-Length" { есть размер контента := истина }
ответ := строка.собрать(ответ, строка.ф("$стр: $стр\r\n", заглавие.имя, заглавие.значение))
}
если ~ есть размер контента {
ответ := строка.собрать(ответ, строка.ф("Content-Length: $цел\r\n", длина(р.туловище(:Строка8))))
}
ответ := строка.собрать(ответ, "\r\n")
если длина(р.туловище) > 0 {
ответ := строка.собрать(ответ, р.туловище)
}
вернуть ответ
}
фн отправить хттп ответ*(с: тцп.ТцпСоединение, р: ХттпОтвет) {
пусть данные := сериализовать хттп ответ(р)
с.записать(данные)
}
фн разобрать хттп обращение*(с: вперед-назад.Крипипаста): ХттпОбращение {
пусть сколько читаем = 1024
пусть прочитано := 0
пусть обращение = ХттпОбращение{}
пусть данные := ""
пусть первая линия := ""
пусть добрались до тела := ложь
пусть размер контента: Цел64 := -1
пока истина {
пусть сколько прочитать: Цел64 := -1
если размер контента > 0 & добрались до тела {
сколько прочитать := размер контента - прочитано
}
если сколько прочитать = 0 {
вернуть обращение
}
пусть новые данные = с.прочитать(сколько читаем)
прочитано := прочитано + длина(новые данные)
если длина(новые данные) = 0 {
вернуть обращение
}
данные := строка.собрать(данные, новые данные)
если ~ добрались до тела {
пока длина(данные) > 0 {
пусть конец строки = строка.индекс(данные, 0, "\n")
если конец строки = -1 {
прервать
}
пусть линия = строка.обрезать пробельные символы(строка.извлечь(данные, 0, конец строки))
данные := строка.извлечь(данные, конец строки + 1, длина(данные(:Строка8)) - конец строки)
если линия = "" {
если размер контента > 0 {
пусть индекс переноса = строка.индекс(данные, 0, "\n")
добрались до тела := истина
данные := строка.извлечь(данные, индекс переноса + 1, длина(данные(:Строка8)) - индекс переноса)
прочитано := длина(данные(:Строка8))
обращение.туловище := данные
данные := ""
прервать
} иначе {
вернуть обращение
}
}
если первая линия = "" {
первая линия := линия
пусть части = строка.разобрать(первая линия, " ")
если длина(части) >= 1 { обращение.метод := части[0] }
если длина(части) >= 2 {
цикл [номер]часть среди части {
если номер > 0 & номер < длина(части) - 1 {
если номер > 1 {
обращение.путь := строка.собрать(обращение.путь, " ")
}
обращение.путь := строка.собрать(обращение.путь, часть)
}
}
}
если длина(части) >= 3 { обращение.версия := части[длина(части) - 1] }
} иначе {
пусть заглавие = ХттпЗаглавие{}
строка.разделить(линия, ":", заглавие.имя, заглавие.значение)
заглавие.имя := строка.обрезать пробельные символы(заглавие.имя)
заглавие.значение := строка.обрезать пробельные символы(заглавие.значение)
если размер контента < 0 & заглавие.имя = "Content-Length" {
пусть новый размер контента := 0
пусть номер байта := 0
если строка.извлечь цел(заглавие.значение, номер байта, новый размер контента) {
размер контента := новый размер контента
}
}
обращение.заглавия.добавить(заглавие)
}
}
} иначе {
обращение.туловище := строка.соединить(обращение.туловище, данные)
}
}
вернуть обращение
}
фн разобрать хттп ответ*(с: тцп.ТцпСоединение): ХттпОтвет {
пусть сколько читаем = 1024
пусть прочитано := 0
пусть ответ = ХттпОтвет{}
пусть данные := ""
пусть первая линия := ""
пусть добрались до тела := ложь
пусть размер контента: Цел64 := -1
пока истина {
пусть сколько прочитать: Цел64 := -1
если размер контента > 0 & добрались до тела {
сколько прочитать := размер контента - прочитано
}
если сколько прочитать = 0 {
вернуть ответ
}
пусть новые данные = с.прочитать(сколько читаем)
прочитано := прочитано + длина(новые данные)
если длина(новые данные) = 0 {
вернуть ответ
}
данные := строка.собрать(данные, новые данные)
если ~ добрались до тела {
пока длина(данные) > 0 {
пусть конец строки = строка.индекс(данные, 0, "\n")
если конец строки = -1 {
прервать
}
пусть линия = строка.обрезать пробельные символы(строка.извлечь(данные, 0, конец строки))
данные := строка.извлечь(данные, конец строки + 1, длина(данные(:Строка8)) - конец строки)
если линия = "" {
если размер контента > 0 {
добрались до тела := истина
прочитано := длина(данные(:Строка8))
ответ.туловище := данные
данные := ""
прервать
} иначе {
вернуть ответ
}
}
если первая линия = "" {
первая линия := линия
пусть части = строка.разобрать(первая линия, " ")
если длина(части) >= 1 { ответ.версия := части[0] }
если длина(части) >= 2 {
пусть новый код := 0
пусть номер байта := 0
если строка.извлечь цел(части[1], номер байта, новый код) {
ответ.код := новый код
}
}
если длина(части) >= 3 {
цикл [номер]часть среди части {
если номер >= 2 {
если номер > 2 {
ответ.состояние := строка.собрать(ответ.состояние, " ")
}
ответ.состояние := строка.собрать(ответ.состояние, часть)
}
}
}
} иначе {
пусть заглавие = ХттпЗаглавие{}
строка.разделить(линия, ":", заглавие.имя, заглавие.значение)
заглавие.имя := строка.обрезать пробельные символы(заглавие.имя)
заглавие.значение := строка.обрезать пробельные символы(заглавие.значение)
если размер контента < 0 & заглавие.имя = "Content-Length" {
пусть новый размер контента := 0
пусть номер байта := 0
если строка.извлечь цел(заглавие.значение, номер байта, новый размер контента) {
размер контента := новый размер контента
}
}
ответ.заглавия.добавить(заглавие)
}
}
} иначе {
ответ.туловище := строка.соединить(ответ.туловище, данные)
}
}
вернуть ответ
}

View File

@@ -0,0 +1,24 @@
модуль хттп
тип ХттпЗаглавие* = класс {
имя*: Строка := ""
значение*: Строка := ""
}
тип ХттпЗаглавия* = []ХттпЗаглавие
тип ХттпОбращение* = класс {
метод*: Строка := ""
путь*: Строка := ""
версия*: Строка := ""
заглавия*: ХттпЗаглавия := ХттпЗаглавия[]
туловище*: Строка := ""
}
тип ХттпОтвет* = класс {
версия*: Строка := "HTTP/1.1"
код*: Цел64 := 200
состояние*: Строка := "OK"
заглавия*: ХттпЗаглавия := ХттпЗаглавия[]
туловище*: Строка := ""
}