Я уже давно подумываю, что мне на сервере не хватает веб-прокси для Gemini. Иногда хочется иметь возможность дать ссылку на Gemini https URL'ом.
Всё, конечно, уже написано в этом мире за нас, поэтому для начала я обратился к списку известных веб-прокси для Gemini.
Список оказался немного не актуальным: часть сервисов из списка уже не работают. Wobbly и построенные на его основе прокси требуют Javascript в веб браузере. (JS для просмотра простого текстового протокола? Извините, но это уже перебор). Самый симпатичным (мне) показался вариант Mozz.us portal:
Но и он обладает двумя критическими недостатками:
Насчёт последнего, конечно, шутка. Дело вот в чём: я подумал, что нехорошо, что такой годный проект поднят в сети только в одном экземпляре, хорошо бы запустить копию у себя на сервере. Я даже был готов ради него в своём сервере vostok заменить пустые MIME на что-то вроде "text/gemini". Но потом я посмотрел на его исходный код:
https://github.com/michael-lazar/gemini-portal
И в репозитории я обнаружил, что с одной стороны там много всего, что мне вообще не нужно. А с другой - ради всего этого используется внушительный набор внешних зависимостей. А мы же все любим раскидистые зависимости в пайтон? И да, тут не самые широкие зависимости из тех, что я видел. Но ведь можно реализовать такой прокси сервер только на стандартной библиотеке пайтон? Я подумал, что можно, да и задача, вроде как, не сложная.
Значит, решено: пишем на пайтон в рамках стандартной библиотеки.
Для реализации HTTP сервера используем модули http и http.server
https://docs.python.org/3/library/http.html
https://docs.python.org/3/library/http.server.html
Для реализации gemini клиента используем модули socket и ssl
https://docs.python.org/3/library/socket.html
https://docs.python.org/3/library/ssl.html
И для клиента, и для сервера потребуются манипуляции с URL, а значит нужен модуль urllib.parse
https://docs.python.org/3/library/urllib.parse.html
А для формирования тела ответа (HTML) пригодится модуль xml.etree.ElementTree
https://docs.python.org/3/library/xml.etree.elementtree.html
Из всего этого я и собрал проект yah2g (yet another http-to-gemini). Git репозиторий с исходным кодом сервера можно клонировать по следующей ссылке:
ssh://anonymous@got.any-key.press/yah2g
Web интерфейс к git репозиторию
Мой экземпляр сервера запущен на OpenBSD. За SSL/TLS отвечает relayd, который перенаправляет HTTP запросы к запущенному python3, исполняющему скрипт yah2g.py.
По реализации стоит сделать несколько замечаний.
Прокси сервер, опираясь на MIME из ответа, различает два вида полученного из Gemini содержимого: text/gemini и всё остальное. Gemini страницы первого вида из text/gemini преобразуются в HTML. Конвертер получился несложный. К примеру текущая Gemini-страница доступна по следующей ссылке:
https://gem.any-key.press/?url=gemini%3A%2F%2Fany-key.press%2Fyah2g%2Freport_0.1.gmi
А все остальные виды содержимого отдаются HTTP ответом без преобразования. Это позволяет, например, смотреть картинки в сыром виде:
gemini://vostok.any-key.press/capsule/vostok.png
Или, в качестве другого примера, подписаться на atom.xml:
gemini://any-key.press/atom.xml
Второе замечание по реализации касается запроса пользовательского ввода в Gemini (статусы 1x). Сервер их на текущий момент не поддерживает. Честно говоря, и дорабатывать их поддержку я пока не планирую. Поэтому, например, заслать новую ссылку в geddit с помощью прокси сервера не получится (может это даже и хорошо).
Кстати, неплохой тестовой базой для проверки конвертации Gemini-разметки в HTML служила страница со спецификацией gemtext:
gemini://geminiprotocol.net/docs/gemtext-specification.gmi
Но стоит учитывать, что, например, строка цитаты там описана, но не представлена примером.
Для самопроверки я так же сделал несколько страниц, которые покрывают некоторые крайние случаи конвертации Gemini-разметки в HTML:
Тестовая страница, содержащая все типы строк
Тестовая страница, содержащая экранируемые в HTML символы
Тестовая страница, содержащая в последней строке элемент списка
Тестовая страница, содержащая в конце незакрытый блок преформатированного текста
Комментарии через ActivityPub (Fediverse) можно оставить здесь: