Не совсем тривиальный HTTP-прокси на Erlang

24 декабря 2012

Написал тут HTTP-проксю на Erlang’е. Зачем? Ну, например, мне захотелось попробовать Cowboy и написать на Erlang относительно серьезное приложение. Кода получилось довольно много, целых 300 строк! Разбирать его здесь я смысла не вижу, желающие сами ознакомятся и разберутся. Лучше я расскажу, что прокся умеет и как ею пользоваться.

Итак, что прокся умеет:

  • Скрывать ваш IP, при этом вы сможете нормально загружать странички, отправлять формочки и даже закачивать небольшие файлики;
  • Скачивать большие объемы данных;
  • Сжимать данные gzip’ом, если они не были сжаты;
  • Работать в режиме reverse proxy с буфферизацией данных и рерайтами;
  • Игнорировать рекурсивные запросы;
  • Читать конфигурационный файл;

Что прокся не умеет:

  • Ограничивать доступ к самой себе по логину-паролю;
  • Кэшировать страницы;
  • Самостоятельно раздавать статику;
  • Понимать запросы, отличные от GET, POST и HEAD;
  • Хорошо обрабатывать большие POST-запросы;
  • Работать с SSL;
  • И многое другое;

Собираем:

sudo apt-get install erlang
git clone git://github.com/afiskon/erlang-http-proxy.git
cd erlang-http-proxy
make build

Прогоняем простенький тест:

make test

В этом тесте прокся поднимается на 8080-м порту и через нее загружается ya.ru. Если приходит код 200, тест пройден. Если вы располагаете большим количеством времени, то можете прогнать все-все-все тесты:

make test_full

Настраивается прокси через app.config. Основные параметры следующие:

  {http_proxy, [
    { port, 8080 },

Номер порта, на котором поднимается прокси.

    { workers, 256 },

Количество воркеров.

    { timeout, 10000 },

Сколько миллисекунд дается клиенту на отправку запроса.

    { sync_stream, true },

Если false, данные буферизируются, если true — не буферизируются.

    { stream_chunk_size, 4096 },

Какими кусками отдавать данные клиенту. Имеет смысл только при sync_stream = false. Например, если вы решили использовать сервер в качестве reverse proxy для того, чтобы Apache не занимался передачей данных по медленным соединениям, нужно присвоить sync_stream значение false, а stream_chunk_size — максимальный размер веб-страницы.

    { enable_gzip, true },

Если данные никак не закодированы, сжимать их gzip’ом.

    { rewrite_rules, [
        { "^http://[^/]+/mail/(.*)$", "http://mail.ru/\\1" }
      ]}
  ]}

Если запрашиваемый URL соответствует регулярному выражению (первый элемент пары), заменить его в соответствии с правилом (второй элемент пары). Если не соответствует, идем дальше по списку пар. Если в списке больше не осталось элементов, оставляем URL без изменений.

Запускаем:

./start.sh

Проверяем:

http_proxy=localhost:8080 wget http://google.com -S -O -

Сказав браузеру работать через прокси, я:

  • Скачал ISO образ размером 4 Гб на скорости около 200 Кб/сек при включенном сжатии gzip’ом, проверил значение MD5;
  • Почитал бложики, послушал музыку во Вконтакте, посмотрел видео на YouTube;
  • Открыл десяток вкладок с «тяжелыми» сайтами типа lenta.ru, заставил их обновляться каждую минуту с помощью специального плагина, после чего отправился спать;
  • На утро написал этот пост;

Прокся работает шустренько и стабильненько. В худшем случае на моем слабеньком домашнем компьютере она кушала 5-7% CPU и около 18 Мб памяти. Притом с ростом количества соединений это число как-то не особо думает увеличиваться. Не падает, ошибками не сыпет.

Весь код лежит на GitHub’е. Надеюсь, кому-нибудь он пригодится. Буду рад вашим багрепортам и пуллреквестам.

Дополнение: См также заметку про получение load average в Erlang.

Метки: , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.