Персональный | |
accept-фильтры
19.06.2002 Два года назад во FreeBSD появились accept-фильтры. Они позволяют не передавать в accept() пришедшее соединение до тех пор, пока не придёт первый пакет с данными (фильтр dataready) или заголовок HTTP-запроса (фильтр httpready). Использование фильтров в Apache (а в нём они поддерживаются, начиная с версии 1.3.14) позволяет уменьшить число процессов. В серверах, использующих select(), poll() или kqueue(), например, в thttpd-2.22, фильтры уменьшают число открытых файлов. И наконец, в обоих случаях accept-фильтры уменьшают число переключений контекста процесса. Идея accept-фильтра принадлежит одному из основателей Yahoo! Дэвиду Фило (David Filo). В Yahoo! использовался фильтр для запросов HTTP. 15.06.2000 Alfred Perlstein внёс во FreeBSD 5.0-CURRENT код этого фильтра и добавил фильтр для первого пакета с данными. Для включения фильтра нужно было с помощью setsockopt(2) установить опцию SO_DELAYACCEPT или SO_HTTPACCEPT для сокета, который принимал соединения. Но этот вариант просуществовал не больше трёх суток уже 18.06.2002 он был удалён и 20.06.2000 во FreeBSD 5.0-CURRENT появилась новая, более общая реализация accept-фильтров. Теперь фильтры включались с помощью опции SO_ACCEPTFILTER, вместе с которой передавался указатель на структуру, описывающую фильтр. Для современных фильтров в этой структуре нужно передать только имя фильтра dataready или httpready. В отличие от первой реализации, каждый фильтр нужно явно указать в конфигурации ядра: или же загрузить как ядерный модуль. Именно это вариант появился 28.07.2000 во FreeBSD 4.1-STABLE. Подробнее об использовании accept-фильтрах можно прочитать в setsockopt(2), accf_data(9) и accf_http(9).options ACCEPT_FILTER_DATA options ACCEPT_FILTER_HTTP Надо заметить, что первоначальная реализация фильтра httpready была далека от суровой реальности, так как поддерживала только протоколы HTTP/1.x и не поддерживала протокол HTTP/0.9 если первый пришедший пакет начинался со строки "GET ", то фильтр не передавал соединение в очередь завершённых соединений до тех пор, пока не находил во входном потоке пустую строку. Однако 6.09.2000 во FreeBSD 5.0-CURRENT и 20.09.2000 во FreeBSD 4.1-STABLE (практически перед самым 4.1.1-RELEASE) фильтр httpready был переписан при получении запроса, непохожего на "^(GET|HEAD) .+ HTTP/1.x", соединение сразу же помещается в очередь завершённых соединений. Оба фильтра dataready и httpready не предполагали никаких таймаутов, но при переполнении очереди входящих соединений вызывалась функция sodropablereq(), которая удаляла старые незавершённые соединения или же соединения, завершённые с точки зрения TCP/IP, но незавершённые с точки зрения фильтра. После того, как во FreeBSD 5.0-CURRENT и 4.4-STABLE появился syncache, с accept-фильтрами стали возникать некоторые проблемы. Первая из них, по-видимому, наблюдалась только с httpready фильтром после того, как соединение было передано accept()'у, следующий за ним read() ничего не мог прочитать. Эта ошибка была исправлена 21.12.2001 во FreeBSD 5.0-CURRENT и 27.12.2001 во FreeBSD 4.5-PRERELEASE. Недавно разработчики обнаружили, что сервер, использующий accept-фильтры, может быть легко подвержен DoS-атаке. Как уже говорилось, до syncache избытком незавершённых соединений занималась функция sodropablereq(), но в syncache эта функция не используется и в результате очередь легко переполняется соединениями, незавершёнными с точки зрения фильтра. 26.04.2002 ошибка была исправлена во FreeBSD 5.0-CURRENT, а 1.05.2002 во FreeBSD 4.5-STABLE. Была идея на основе accept-фильтров реализовать ядерный http-сервер, который бы возвращал в accept() те запросы, которые не мог обработать сам, но она, по-видимому, так и осталась нереализованной. Надо заметить, что accept-фильтры не единственное решение отложить accept() до прихода данных. В Linux 2.4.x можно установить опцию TCP_DEFER_ACCEPT, а в Windows NT 3.51 появилась функция AcceptEx(). (C) Игорь Сысоев |