New in Tornado 4.0.
:arg bool use_gzip: Deprecated alias for ``decompress_response``
since Tornado 4.0.
- :arg str network_interface: Network interface to use for request.
- ``curl_httpclient`` only; see note below.
+ :arg str network_interface: Network interface or source IP to use for request.
+ See ``curl_httpclient`` note below.
:arg collections.abc.Callable streaming_callback: If set, ``streaming_callback`` will
be run with each chunk of data as it is received, and
``HTTPResponse.body`` and ``HTTPResponse.buffer`` will be empty in
from tornado.http1connection import HTTP1Connection, HTTP1ConnectionParameters
from tornado.ioloop import IOLoop
from tornado.iostream import StreamClosedError, IOStream
-from tornado.netutil import Resolver, OverrideResolver, _client_ssl_defaults
+from tornado.netutil import (
+ Resolver,
+ OverrideResolver,
+ _client_ssl_defaults,
+ is_valid_ip,
+)
from tornado.log import gen_log
from tornado.tcpclient import TCPClient
ssl_options = self._get_ssl_options(self.parsed.scheme)
+ source_ip = None
+ if self.request.network_interface:
+ if is_valid_ip(self.request.network_interface):
+ source_ip = self.request.network_interface
+ else:
+ raise ValueError(
+ "Unrecognized IPv4 or IPv6 address for network_interface, got %r"
+ % (self.request.network_interface,)
+ )
+
timeout = min(self.request.connect_timeout, self.request.request_timeout)
if timeout:
self._timeout = self.io_loop.add_timeout(
af=af,
ssl_options=ssl_options,
max_buffer_size=self.max_buffer_size,
+ source_ip=source_ip,
)
if self.final_callback is None:
):
raise KeyError("unknown method %s" % self.request.method)
for key in (
- "network_interface",
"proxy_host",
"proxy_port",
"proxy_username",
response2 = yield self.http_client.fetch(response.request)
self.assertEqual(response2.body, b"Hello world!")
+ @gen_test
+ def test_bind_source_ip(self):
+ url = self.get_url("/hello")
+ request = HTTPRequest(url, network_interface="127.0.0.1")
+ response = yield self.http_client.fetch(request)
+ self.assertEqual(response.code, 200)
+
+ with self.assertRaises((ValueError, HTTPError)) as context:
+ request = HTTPRequest(url, network_interface="not-interface-or-ip")
+ yield self.http_client.fetch(request)
+ self.assertIn("not-interface-or-ip", str(context.exception))
+
def test_all_methods(self):
for method in ["GET", "DELETE", "OPTIONS"]:
response = self.fetch("/all_methods", method=method)