Ни одна библиотека не может сделать это без использования какого-либо асинхронного таймера через потоки или иным образом.Причина в том, что параметр timeout
, используемый в httplib
, urllib2
и других библиотеках, устанавливает timeout
в базовом socket
.И что это на самом деле делает, объясняется в документации .
SO_RCVTIMEO
Устанавливает значение времени ожидания, которое определяет максимальное количество времени, которое функция ввода ожидает дозавершается.Он принимает временную структуру с количеством секунд и микросекунд, определяющих ограничение времени ожидания завершения операции ввода.Если операция получения заблокирована на это время без получения дополнительных данных , она должна вернуться с частичным счетчиком или ошибкой, установленной в [EAGAIN] или [EWOULDBLOCK], если данные не получены.
полужирная часть является ключевой.socket.timeout
повышается только в том случае, если не было получено ни одного байта в течение окна timeout
.Другими словами, это timeout
между полученными байтами.
Простая функция, использующая threading.Timer
, может выглядеть следующим образом.
import httplib
import socket
import threading
def download(host, path, timeout = 10):
content = None
http = httplib.HTTPConnection(host)
http.request('GET', path)
response = http.getresponse()
timer = threading.Timer(timeout, http.sock.shutdown, [socket.SHUT_RD])
timer.start()
try:
content = response.read()
except httplib.IncompleteRead:
pass
timer.cancel() # cancel on triggered Timer is safe
http.close()
return content
>>> host = 'releases.ubuntu.com'
>>> content = download(host, '/15.04/ubuntu-15.04-desktop-amd64.iso', 1)
>>> print content is None
True
>>> content = download(host, '/15.04/MD5SUMS', 1)
>>> print content is None
False
Кроме проверки на None
, этотакже возможно перехватить исключение httplib.IncompleteRead
не внутри функции, а вне ее.Последний случай не будет работать, хотя HTTP-запрос не имеет заголовка Content-Length
.