Персональный
сайт
Игоря
Сысоева


 
english
обо мне
 
sysoev.ru
 
nginx
 
mod_accel
mod_realip
mod_deflate
программирование
всякая всячина
windows
freebsd
apache
pppd
unix
web
 
 

Почему невозможно корректно ограничить размер закачиваемого файла

 

11.01.2007

Иногда нужно ограничить размер закачиваемого файла. Казалось бы, сделать это достаточно просто — в заголовке "Content-Length" браузер сообщает размер тела запроса. Если этот размер больше нашего ограничения, то возвращаем браузеру ошибку "413 Request Entity Too Large" и закрываем соединение. Однако всё осложняется тем фактом, что MSIE 4.x-6.x и Netscape 4.x считают, что ответ от сервера может быть получен только после того, как они полностью передадут запрос. Если соединение закрывается во время передачи запроса, то вместо переданного сервером сообщения об ошибке MSIE показывает стандартное сообщение "This page cannot be displayed", а Netscape 4.x — "A network error occurred while Netscape was sending data (Network Error: Broken pipe)", и в результате понять, что именно послужило причиной ошибки, невозможно. Точно так же ведут себя Opera, Konqueror и современные версии браузеров, основанных на Mozilla. Браузеры, созданные на основе Mozilla времён 2001 года, в такой ситуации работали корректно и показывали переданное сообщение об ошибке.

В Apache эта проблема частично решается с помощью медленного закрытия (lingering close) соединения. Когда соединение необходимо закрыть, Apache вызывает shutdown() для сокета с параметром SHUT_WR, тем самым сообщая браузеру о том, что больше он ничего передавать не будет. После этого Apache в течение 2 секунд ждёт, не передаст ли ему что-нибудь браузер. Если что-то передано, Apache считывает это, никуда не сохраняя, и снова в цикле ждёт 2 секунды. Если в течение 2 секунд ничего не передавалось, то Apache закрывает соединение. Максимальное время ожидания задаётся при сборке Apache с помощью переменной MAX_SECS_TO_LINGER, по умолчанию равной 30 секундам. По истечении этого времени Apache закрывает соединение, даже если браузер не завершил передачу. Таким образом, если установлено ограничение на размер закачиваемого файла, то браузер покажет сообщение о 413 ошибке только в том случае, если он успеет в течение 30 секунд передать полностью запрос. Например, модемный пользователь, для которого наиболее критично ограничивать размеры, со скоростью 3K/s за 30 секунд успеет передать около 100K.

nginx так же, как и Apache, использует медленное закрытие соединения с двумя отличиями — во-первых, медленное закрытие делается только для запросов с телом, тогда как Apache закрывает так все соединения, и, во-вторых, времена таймаутов настраиваются (по умолчанию они равны 5 и 30 секундам).

Необходимо заметить, что ограничение размера числом больше 1M будет работать не всегда. Дело в том, что около 20% всех запросов выполняется через прокси-сервера Squid, в которых по умолчанию стоит ограничение на 1M (директива request_body_max_size) и его очень редко меняют. В отличие от Apache Squid после передачи сообщения о 413 ошибке сразу же закрывает соединение.

В стандарте HTTP/1.1 для закачивания файлов предусмотрен заголовок браузера "Expect: continue", в ответ на который сервер может вернуть сообщение об ошибке или же ответить кодом "HTTP/1.1 100 Continue", после чего браузер может передавать тело запроса. Однако ни один из современных браузеров это не поддерживает.

(C) Игорь Сысоев
http://sysoev.ru