import logging
import re
import socket
+import time
import urlparse
import zlib
_SUPPORTED_METHODS = set(["GET", "HEAD", "POST", "PUT", "DELETE"])
def __init__(self, io_loop, request, callback):
+ self.start_time = time.time()
self.io_loop = io_loop
self.request = request
self.callback = callback
self.headers = None
self.chunks = None
self._decompressor = None
+ # Timeout handle returned by IOLoop.add_timeout
+ self._timeout = None
with stack_context.StackContext(self.cleanup):
parsed = urlparse.urlsplit(self.request.url)
if ":" in parsed.netloc:
else:
self.stream = IOStream(socket.socket(),
io_loop=self.io_loop)
+ timeout = min(request.connect_timeout, request.request_timeout)
+ if timeout:
+ self._connect_timeout = self.io_loop.add_timeout(
+ self.start_time + timeout,
+ self._on_timeout)
self.stream.connect((host, port),
functools.partial(self._on_connect, parsed))
+ def _on_timeout(self):
+ self._timeout = None
+ self.stream.close()
+ if self.callback is not None:
+ self.callback(HTTPResponse(self.request, 599,
+ error=HTTPError(599, "Timeout")))
+ self.callback = None
+
def _on_connect(self, parsed):
+ if self._timeout is not None:
+ self.io_loop.remove_callback(self._timeout)
+ self._timeout = None
+ if self.request.request_timeout:
+ self._timeout = self.io_loop.add_timeout(
+ self.start_time + self.request.request_timeout,
+ self._on_timeout)
if (self.request.method not in self._SUPPORTED_METHODS and
not self.request.allow_nonstandard_methods):
raise KeyError("unknown method %s" % self.request.method)
"don't know how to read %s", self.request.url)
def _on_body(self, data):
+ if self._timeout is not None:
+ self.io_loop.remove_timeout(self._timeout)
+ self._timeout = None
if self._decompressor:
data = self._decompressor.decompress(data)
if self.request.streaming_callback:
import gzip
import logging
+import socket
+from contextlib import closing
from tornado.simple_httpclient import SimpleAsyncHTTPClient
-from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase
-from tornado.web import Application, RequestHandler
+from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase, get_unused_port
+from tornado.web import Application, RequestHandler, asynchronous
class HelloWorldHandler(RequestHandler):
def get(self):
def get(self):
self.finish(self.request.headers["Authorization"])
+class HangHandler(RequestHandler):
+ @asynchronous
+ def get(self):
+ pass
+
class SimpleHTTPClientTestCase(AsyncHTTPTestCase, LogTrapTestCase):
def get_app(self):
return Application([
("/post", PostHandler),
("/chunk", ChunkHandler),
("/auth", AuthHandler),
+ ("/hang", HangHandler),
], gzip=True)
def setUp(self):
self.assertEqual(len(response.body), 34)
f = gzip.GzipFile(mode="r", fileobj=response.buffer)
self.assertEqual(f.read(), "asdfqwer")
+
+ def test_connect_timeout(self):
+ # create a socket and bind it to a port, but don't
+ # call accept so the connection will timeout.
+ #get_unused_port()
+ port = get_unused_port()
+
+ with closing(socket.socket()) as sock:
+ sock.bind(('', port))
+ self.http_client.fetch("http://localhost:%d/" % port,
+ self.stop,
+ connect_timeout=0.1)
+ response = self.wait()
+ self.assertEqual(response.code, 599)
+ self.assertEqual(str(response.error), "HTTP 599: Timeout")
+
+ def test_request_timeout(self):
+ response = self.fetch('/hang', request_timeout=0.1)
+ self.assertEqual(response.code, 599)
+ self.assertEqual(str(response.error), "HTTP 599: Timeout")