commit afb81d68a00be1873ebd098c3e154f11ee053263 from: Aleksey Ryndin date: Mon Aug 28 11:18:50 2023 UTC Move russian blog to `gemini://any-key.press/vostok/reports/` commit - 937f9f836fb50f91a011ee5e4e3be4fea1e47123 commit + afb81d68a00be1873ebd098c3e154f11ee053263 blob - cbb9c374a977cd3826fc68f58ad1f0e086251542 blob + 4fe03b83b3ee6833d3520cb017e3ada510d1a871 --- README.gmi +++ README.gmi @@ -1,40 +1,29 @@ # vostok: simple C++11 Gemini server -* Generate self-signed certificate: +Git repository: ``` -$ mkdir cert/ -$ openssl req -newkey rsa:4096 -nodes -keyout cert/server.key -x509 -days 36500 -out cert/server.crt +ssh://anonymous@got.any-key.press/vostok ``` +=> https://got.any-key.press/?action=summary&path=vostok.git vostok repository web frontend -* Build and run server: -``` -$ make run_server -``` - -## Development blog (in Russian) - -=> /reports/0.0.2.gmi v0.0.2 (2023-08-23) -=> /reports/0.0.1.gmi v0.0.1 (2023-08-17) - ## Tasks * [?] add test * [v] redirect "[.../]" to "[...]/index.gmi" -* [ ] Gemini redirect by symbolic link on the file system -``` -$ ln -s "gemini://vostok.any-key.press/index.gmi" index.gmi -$ ls -l index.gmi -lrwxr-xr-x 1 ar ar 39 Aug 25 13:25 index.gmi -> gemini://vostok.any-key.press/index.gmi -``` * [ ] syslog(3) * [ ] mime.types * [ ] clang-tydi (?) * [ ] make install * [ ] man pages +* [ ] Gemini redirect by symbolic link on the file system -## See also +## How to -=> ssh://anonymous@got.any-key.press/vostok vostok source code (git) -=> https://got.any-key.press/?action=summary&path=vostok.git vostok source code (web) +=> /capsule/how_to_run_locally.gmi How to run vostok locally -Send your patches, questions and suggestions to e-mail vostok@to.any-key.press +## Feedback + +Send your patches, questions and suggestions to e-mail +``` +vostok@to.any-key.press +``` blob - b699bb55d3831b9df19822df427525f2390f9d10 (mode 644) blob + /dev/null --- reports/0.0.1.gmi +++ /dev/null @@ -1,100 +0,0 @@ -# vostok: простой сервер Gemini на C++11 - -Столкнувшись в очередной раз с протоколом Gemini я перед сном решил почитать его спецификацию. -=> gemini://gemini.circumlunar.space/docs/specification.gmi Project Gemini (Speculative specification) - -В целом мне очень понравилось то, что автор называет протокол простым и на самом деле протокол оказался несложным. Хотя и тут стоит отметить, что использование прослойки TLS в качестве транспортного уровня делает эту самую простоту весьма условной. В противовес этому, например, протокол Spartan работает поверх обычного TCP. Наверное, если бы я сейчас сел писать реализацию заново, то выбрал бы spartan. А может быть и до него дойдут когда-нибудь руки. -=> spartan://spartan.mozz.us The Spartan Protocol Homepage - -Что-то в спецификации протокола меня зацепило и я написал свою реализацию сервера Gemini на C++11 - vostok (Восток). Название отсылает к имени советского космического корабля "Восток-1", на котором Юрий Алексеевич Гагарин совершил первый полёт человека в космос. - -## Сборка проекта - -Итак, собственная реализация сервера Gemini. Язык выбран C++, точнее С++11. Причина выбора языка и его редакции - моя личная "вкусовщина". Целевая платформа: Unix-like, без привязки к какой-то конкретной реализации. Сам собираю-запускаю-проверяю на двух платформах: -* OpenBSD 7.3: сборка clang'ом с помощью BSD make -* Alpine Linux 3.18: сборка g++ с помощью GNU make - -Кстати: g++ не входит в базовую систему Alpine Linux, поэтому нужно установить его из одноименного пакета. - -Единственная внешняя зависимость - libtls, так как я сторонник не городить свои "велосипеды" в области криптографии. libtls это библиотека из состава LibreSSL: -=> https://www.libressl.org/ LibreSSL - -На OpenBSD, соответственно, libtls есть "из коробки". А, например, в Alpine Linux требуется установить пакет libressl-dev. - -Для запуска серверу нужен ключ и сертификат, которые можно сгенерировать в под-директории cert/ командой openssl: -``` -$ mkdir cert -$ openssl req -newkey rsa:4096 -nodes -keyout cert/server.key -x509 -days 36500 -out cert/server.crt -``` - -После этого всё готово к тому, что бы собрать-запустить сервер командой "make run_server". Если при сборке и запуске не произойдёт ошибок, то на экране появится строка, обозначающая, что сервер готов принимать входящие запросы: -``` -$ make run_server -make -C vostok -c++ -Wall -Wextra -std=c++11 -I../shared transport.cc error.cc gemini.cc args.cc parse_url.cc vostok.cc -ltls -o vostok -./vostok/vostok -c cert/server.crt -k cert/server.key -f ./ -🚀 Vostok server listening... -``` - -## Возможности сервера - -Первая версия сервера vostok обладает весьма скромными возможностями. Это сервер, который отдает содержимое статических файлов с файловой системы. Все файлы отдаются с пустой строкой META, что подразумевает содержимое в формате "text/gemini; charset=utf-8". - -Запускаемый исполняемый файл принимает следующие параметры командной строки: -* -a ADDR: IPv4 адрес интерфейса, на котором сервер будет слушать входящие соединения. Значение по умолчанию - "127.0.0.1". -* -p PORT: номер порта, на котором сервер будет слушать входящие соединения. Значение по умолчанию - "1965". -* -c FILE: путь к файлу сертификата. -* -k FILE: путь к файлу ключа сертификата. -* -f PATH: путь к директории на файловой системе, откуда будут отдаваться файлы. - -На моем OpenBSD сервере я оформил демон запуска следующим образом: -``` -# cat /etc/rc.d/vostok -#!/bin/ksh - -daemon_args="-a 5.23.49.151 -k /var/vostok/cert/server.key -c /var/vostok/cert/server.crt -f /var/vostok/static" -daemon_user="_vostok" -daemon="/home/_vostok/src/vostok/vostok ${daemon_args}" - -. /etc/rc.d/rc.subr - -pexp="${daemon}.*" -rc_reload=NO -rc_bg=YES - -rc_cmd $1 -``` - -## Gemini клиент - -Изначально я планировал проект vostok, как пару: сервер статики и собственный клиент (простой браузер). Но потом я понял, что лично для меня утилиты openssl в паре с less вполне хватает, что бы смотреть Gemini страницы. Что бы отобразить Gemini содержимое я пользуюсь примерно такой командной строкой: -``` -openssl s_client -crlf -quiet -connect -no_ssl3 -no_tls1 -no_tls1_1 -connect vostok.any-key.press:1965 <<< "gemini://vostok.any-key.press/reports/0.0.1.gmi" | less -``` - -Если вам хочется более функциональный браузер, то я рекомендую присмотреться к консольному amfora. -=> https://github.com/makew0rld/amfora - -Браузеры для Gemini с графическим пользовательским интерфейсом я никогда не пробовал, поэтому посоветовать ничего не могу. - -## Планы на будущее - -Для начала я хочу немного привести код в порядок: -* покрыть сервер тестами -* заменить вывод логов в std::сerr на работу с syslog(3) - -Затем немного нарастить функционал самого сервера: -* сделать поддержку MIME-типов при отдаче статики -* сделать возможность задавать редиректы (что, например, позволит редиректить открытие корневой и не только директории на файл index.gmi) - -## Исходный код - -=> ssh://anonymous@got.any-key.press/vostok git репозиторий с исходным кодом -=> https://got.any-key.press/?action=summary&path=vostok.git WEB интерфейс git репозитория с исходным кодом - -## Обратная связь - -Ваши патчи, вопросы и предложения присылайте на электронную почту vostok@to.any-key.press - -Если вы не знакомы с практикой отсылки git-патчей через элетронную почту, то рекомендую к ознакомлению ресурс: -=> https://git-send-email.io/ Learn to use email with git blob - 22680a86b8c4e5b70209ac079b7a420405fbe82c (mode 644) blob + /dev/null --- reports/0.0.2.gmi +++ /dev/null @@ -1,70 +0,0 @@ -# vostok: сервер Gemini, версия 0.0.2 - -Ого, энтузиазма хватила на вторую версию сервера? Это уже что-то значит... наверное) - -Что нового в версии 0.0.2: -* открытие директории перенаправляется на открытие файла index.gmi в этой диреткории; -* в URL запроса схема (протокол) теперь регистро независимы; -* код начал покрываться тестами. - -## Тесты - -Начал покрывать код тестами. Для тестов выделена новая поддиректория tests/, со своим Makefile. В Makefile корневой директории проекта добавлена новая цель tests, которая собирает исполняемые файлы тестов и запускает их: - -``` -$ make tests -make -C tests -./test_parse_url -test_parse_url successfully passed! -./test_open_file -test_open_file successfully passed! -``` - -Для начала тестами покрыл наиболее чувствительный "велосипед" - разбор URL. Соответственно, файл tests/test_parse_url.cc тестирует vostok/parse_url.cc. В файле tests/tests.h набросал условный "фреймворк" для тестирования с тремя макросами: TEST_START(name), IS_TRUE(...) и TEST_END(). В результате тест выглядит как-то так: -``` -#include "parse_url.h" -#include -#include - -#include "tests.h" - -namespace vostok -{ -TEST_START(test_parse_url) - zs_url_path_t zs_url_path; - - IS_TRUE(parse_url(cut_null("gemini://host"), zs_url_path) == url_ok - && strcmp(zs_url_path.begin(), "") == 0); - - // <...> - - IS_TRUE(parse_url(cut_null("GeMiNi://host"), zs_url_path) == url_ok - && strcmp(zs_url_path.begin(), "") == 0); -TEST_END() -} // namespace vostok - -extern "C" int main(int, char **){ return vostok::test_parse_url(); } -``` - -## Чувствительность к регистру схемы в URI - -Сделан небольшой фикс, который позволяет работать серверу в соответствии с RFC 3986 (Uniform Resource Identifier). В разделе 3.1. Scheme недвусмысленно сказано, что сервера должны воспринимать схему (он же "протокол", а в нашем случае начало строки - "gemini://") в любом регистре: - -> An implementation should accept uppercase letters as equivalent to lowercase in scheme names (e.g., allow "HTTP" as well as "http") for the sake of robustness but should only produce lowercase scheme names for consistency. - -Сказано - сделано. Благо именно этот код был покрыт тестами и вносить изменения в него теперь намного спокойнее. - -## Редирект на index.gmi - -Перечитал документ "Best practices for Gemini implementations" и обнаружил там прямое указание на то, что попытка открытия директории должна перенаправляться на открытие файла "index.gmi", если он присутствует в этой самой директории. - -=> gemini://gemini.circumlunar.space/docs/best-practices.gmi Best practices for Gemini implementations - -> Following the convention for webservers, if a request is received for a path which maps to a directory in the server's filesystem and a file named index.gmi or index.gemini exists in that directory, it is served up for that path. - -Раз такое дело, то решил добавить такую логику в vostok. Попутно код, открывающий файл по пути из URL, вынес в отдельный модуль. А для отдельного модуля уже грех и тесты не написать. В итоге: редирект добавил, новый модуль тестами покрыл. - -## Спасибо! - -Отдельная большая благодарность всем, кто присылает патчи. В частности: -* la-ninpre , Makefile: compile files separately blob - 2741426f19320b6b2998b33cef55bda253c294b5 (mode 644) blob + /dev/null --- reports/index.gmi +++ /dev/null @@ -1,5 +0,0 @@ -# Development blog (in Russian) - -=> /reports/0.0.2.gmi v0.0.2 (2023-08-23) -=> /reports/0.0.1.gmi v0.0.1 (2023-08-17) -