Архив метки: aiohttp

Почему я не люблю конфигурацию в django-style

Введение

Сегодня работал над добавлением в aiohttp.webсвойства scheme для request object.

Идея простая: отвечать что request.scheme "http" для HTTP запросов, иначе "https".

У меня есть правило: перед началом погляди как другие уже справились с этой задачей.

У создателей популярных библиотек есть большой опыт по преодолению неочевидных проблем, учиться у мастеров — полезно.

Так вышло что сегодня я смотрел код Django.

И было в том коде примерно такое:

@property
def scheme(self):
if settings.SECURE_PROXY_SSL_HEADER:
try:
header, value = settings.SECURE_PROXY_SSL_HEADER
except ValueError:
raise ImproperlyConfigured(
'The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.'
)
if self.META.get(header, None) == value:
return 'https'
return 'http'

В целом очень хорошо: Django показала, как работать с HTTP и что делать если сервер расположен за HTTPS Reverse Proxy (Nginx, например).

В последнем случае я сконфигурирую Nginx чтобы он добавил несколько полезных HTTP HEADERS для HTTPS connection:

  proxy_set_header        Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

По X-Forwarded-Proto я пойму что это был HTTPS.

В целом стандартная и всем (надеюсь) известная процедура.

У aiohttp свободы чуть больше: оно может понять что сокет, по которому подключились напрямую, сам уже SSL — но это пригодно только если мы готовы выставить наш aiohttp сервер прямо в веб.

Куда чаще его прячут за Nginx, HAProxy или похожим reverse proxyи там уже работают с сертификататами, проксируя обычный HTTP connection.

В общем всё прекрасно: Nginx выставит X-Forwarded-Proto HTTP HEADER который будет или "http" или "https".

Django глянет на settings.SECURE_PROXY_SSL_HEADER и если там ("X-Forwarded-Proto", "https") то scheme тоже будет "https".

Очень грамотно сделано, мне нравится.

Проблема

Так почему я этот пост написал?

А потому что settings.SECURE_PROXY_SSL_HEADER может быть чем угодно — строкой, числом или ещё какой непотребной константой.

Проверка выполняется на момент получения request.scheme.

Нам, питонщикам, плевать на производительность в данном случае — на самом деле try/except обходится дешево и никак не отразится на работе сайта.

Беда в другом — ошибка неправильной конфигурации выявится не на этапе старта приложения а тогда, когда его выкатят в production.

У тестов будет свой правильный settings.py, а на production serverадмин чуть-чуть ошибётся.

И это тоже не слишком большая проблема — при ручном тестировании команда QA, наверное, обнаружит ошибку очень быстро. Или на следующее утро — тут уж зависит от того как техроцесс построен.

А теперь представьте что вы ошиблись в другой настройке. Очень редко используемой, но при этом важной.

Решение

Проблему вроде бы можно выявить рано, перестроив процесс запуска программы.

Для начала нужно отказаться от использования общего конфига в API.

Строить код библиотеки так, чтобы она никогда не лезла в settings.py(это и Flask касается если что).

Пусть все нужные классы принимают конфигурационные параметры явно, прямо в конструкторах.

Тогда можно быстро понять, что формат параметра не тот или IP address недоступен.

Разделение на этапы:

  • чтение конфига, анализ его и подготовка приложения к работе
  • запуск и работа

помогает избежать досадных недоразумений.

Плюс, к тому же, на явном этапе подготовки к старту можно позволить себе довольно дорогостоящие проверки на корректное функционирование системы (послать PING чтобы убедится что Redis живой, например).

Автор: Andrew Svetlov

aiohttp 0.14

Выпустил новую версию библиотеки.

Из вкусного — Web Sockets для aiohttp.web серверов, оптимизация скорости работы, множество мелких улучшений.

Полный список изменений — здесь.

Автор: Andrew Svetlov

asyncio и HTTP

asyncio не умеет работать с HTTP.

Так и было задумано.

asyncio никогда не станет веб-сервером. Он делался как именно event loop для tcp, ssl, unix sockets, pipes и subprocesses. Плюс streaming API.

Веб был сознательно выпилен и теперь то что было лежит в aiohttp. Эта часть просто не дозрела до включения в стандартную библиотеку.

Идея такая:

  • WSGI — синхронный протокол, для asyncio не подходит.
  • Какой будет новый стандарт — неясно никому.
  • Пусть для asyncio люди попытаются сделать свои http либы и время покажет у кого получилось лучше.
  • Тогда, возможно, и появится новый стандарт.

Что касается меня то я пытаюсь понять какой именно должен быть API для HTTP server, что там должно быть обязательно и что нужно сознательно исключить.

Сейчас делаем это aiorest

Когда поймём, что получилось хорошо в aiorest — займемся перенесением удачных решений в aiohttp. Там HTTP serverслишком уж неудобный. А нужно что-то типа tornado.web, но более симпатичное и приятное.

Автор: Andrew Svetlov