class TCPClient(object): """A non-blocking TCP connection factory. .. versionchanged:: 4.1 The ``io_loop`` argument is deprecated. """ def __init__(self, resolver=None, io_loop=None): self.io_loop = io_loop or IOLoop.current() if resolver is not None: self.resolver = resolver self._own_resolver = False else: self.resolver = Resolver(io_loop=io_loop) self._own_resolver = True def close(self): if self._own_resolver: self.resolver.close() @gen.coroutine def connect(self, host, port, af=socket.AF_UNSPEC, ssl_options=None, max_buffer_size=None): """Connect to the given host and port. Asynchronously returns an `.IOStream` (or `.SSLIOStream` if ``ssl_options`` is not None). """ addrinfo = yield self.resolver.resolve(host, port, af) connector = _Connector( addrinfo, self.io_loop, functools.partial(self._create_stream, max_buffer_size)) af, addr, stream = yield connector.start() # TODO: For better performance we could cache the (af, addr) # information here and re-use it on subsequent connections to # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) if ssl_options is not None: stream = yield stream.start_tls(False, ssl_options=ssl_options, server_hostname=host) raise gen.Return(stream) def _create_stream(self, max_buffer_size, af, addr): # Always connect in plaintext; we'll convert to ssl if necessary # after one connection has completed. try: stream = IOStream(socket.socket(af), io_loop=self.io_loop, max_buffer_size=max_buffer_size) except socket.error as e: fu = Future() fu.set_exception(e) return fu else: return stream.connect(addr)
class TCPClient(object): """A non-blocking TCP connection factory. """ def __init__(self, resolver=None, io_loop=None): self.io_loop = io_loop or IOLoop.current() if resolver is not None: self.resolver = resolver self._own_resolver = False else: self.resolver = Resolver(io_loop=io_loop) self._own_resolver = True def close(self): if self._own_resolver: self.resolver.close() @gen.coroutine def connect(self, host, port, af=socket.AF_UNSPEC, ssl_options=None, max_buffer_size=None): """Connect to the given host and port. Asynchronously returns an `.IOStream` (or `.SSLIOStream` if ``ssl_options`` is not None). """ addrinfo = yield self.resolver.resolve(host, port, af) connector = _Connector( addrinfo, self.io_loop, functools.partial(self._create_stream, host, ssl_options, max_buffer_size)) af, addr, stream = yield connector.start() # TODO: For better performance we could cache the (af, addr) # information here and re-use it on sbusequent connections to # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) raise gen.Return(stream) def _create_stream(self, host, ssl_options, max_buffer_size, af, addr): # TODO: we should connect in plaintext mode and start the # ssl handshake only after stopping the _Connector. if ssl_options is None: stream = IOStream(socket.socket(af), io_loop=self.io_loop, max_buffer_size=max_buffer_size) else: stream = SSLIOStream(socket.socket(af), io_loop=self.io_loop, ssl_options=ssl_options, max_buffer_size=max_buffer_size) return stream.connect(addr, server_hostname=host)
class TCPClient(object): """A non-blocking TCP connection factory. """ def __init__(self, resolver=None, io_loop=None): self.io_loop = io_loop or IOLoop.current() if resolver is not None: self.resolver = resolver self._own_resolver = False else: self.resolver = Resolver(io_loop=io_loop) self._own_resolver = True def close(self): if self._own_resolver: self.resolver.close() @gen.coroutine def connect(self, host, port, af=socket.AF_UNSPEC, ssl_options=None, max_buffer_size=None): """Connect to the given host and port. Asynchronously returns an `.IOStream` (or `.SSLIOStream` if ``ssl_options`` is not None). """ addrinfo = yield self.resolver.resolve(host, port, af) connector = _Connector( addrinfo, self.io_loop, # 这个涉及到三个函数的调用,不说了,多是泪 functools.partial(self._create_stream, host, ssl_options, max_buffer_size)) af, addr, stream = yield connector.start() # TODO: For better performance we could cache the (af, addr) # information here and re-use it on sbusequent connections to # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) raise gen.Return(stream) # 这里gen会捕获这个错误 def _create_stream(self, host, ssl_options, max_buffer_size, af, addr): # TODO: we should connect in plaintext mode and start the # ssl handshake only after stopping the _Connector. if ssl_options is None: stream = IOStream(socket.socket(af), io_loop=self.io_loop, max_buffer_size=max_buffer_size) else: stream = SSLIOStream(socket.socket(af), io_loop=self.io_loop, ssl_options=ssl_options, max_buffer_size=max_buffer_size) return stream.connect(addr, server_hostname=host)
class TCPClient(object): """A non-blocking TCP connection factory. .. versionchanged:: 5.0 The ``io_loop`` argument (deprecated since version 4.1) has been removed. """ def __init__(self, resolver=None): if resolver is not None: self.resolver = resolver self._own_resolver = False else: self.resolver = Resolver() self._own_resolver = True def close(self): if self._own_resolver: self.resolver.close() @gen.coroutine def connect(self, host, port, af=socket.AF_UNSPEC, ssl_options=None, max_buffer_size=None, source_ip=None, source_port=None, timeout=None): """Connect to the given host and port. Asynchronously returns an `.IOStream` (or `.SSLIOStream` if ``ssl_options`` is not None). Using the ``source_ip`` kwarg, one can specify the source IP address to use when establishing the connection. In case the user needs to resolve and use a specific interface, it has to be handled outside of Tornado as this depends very much on the platform. Raises `TimeoutError` if the input future does not complete before ``timeout``, which may be specified in any form allowed by `.IOLoop.add_timeout` (i.e. a `datetime.timedelta` or an absolute time relative to `.IOLoop.time`) Similarly, when the user requires a certain source port, it can be specified using the ``source_port`` arg. .. versionchanged:: 4.5 Added the ``source_ip`` and ``source_port`` arguments. .. versionchanged:: 5.0 Added the ``timeout`` argument. """ if timeout is not None: if isinstance(timeout, numbers.Real): timeout = IOLoop.current().time() + timeout elif isinstance(timeout, datetime.timedelta): timeout = IOLoop.current().time() + timedelta_to_seconds( timeout) else: raise TypeError("Unsupported timeout %r" % timeout) if timeout is not None: addrinfo = yield gen.with_timeout( timeout, self.resolver.resolve(host, port, af)) else: addrinfo = yield self.resolver.resolve(host, port, af) connector = _Connector( addrinfo, functools.partial(self._create_stream, max_buffer_size, source_ip=source_ip, source_port=source_port)) af, addr, stream = yield connector.start(connect_timeout=timeout) # TODO: For better performance we could cache the (af, addr) # information here and re-use it on subsequent connections to # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) if ssl_options is not None: if timeout is not None: stream = yield gen.with_timeout( timeout, stream.start_tls(False, ssl_options=ssl_options, server_hostname=host)) else: stream = yield stream.start_tls(False, ssl_options=ssl_options, server_hostname=host) raise gen.Return(stream) def _create_stream(self, max_buffer_size, af, addr, source_ip=None, source_port=None): # Always connect in plaintext; we'll convert to ssl if necessary # after one connection has completed. source_port_bind = source_port if isinstance(source_port, int) else 0 source_ip_bind = source_ip if source_port_bind and not source_ip: # User required a specific port, but did not specify # a certain source IP, will bind to the default loopback. source_ip_bind = '::1' if af == socket.AF_INET6 else '127.0.0.1' # Trying to use the same address family as the requested af socket: # - 127.0.0.1 for IPv4 # - ::1 for IPv6 socket_obj = socket.socket(af) set_close_exec(socket_obj.fileno()) if source_port_bind or source_ip_bind: # If the user requires binding also to a specific IP/port. try: socket_obj.bind((source_ip_bind, source_port_bind)) except socket.error: socket_obj.close() # Fail loudly if unable to use the IP/port. raise try: stream = IOStream(socket_obj, max_buffer_size=max_buffer_size) except socket.error as e: fu = Future() fu.set_exception(e) return fu else: return stream, stream.connect(addr)
class WebSocketClientConnection(simple_httpclient._HTTPConnection): """WebSocket client connection. This class should not be instantiated directly; use the `websocket_connect` function instead. """ def __init__(self, io_loop, request): self.connect_future = TracebackFuture() self.read_future = None self.read_queue = collections.deque() self.key = base64.b64encode(os.urandom(16)) scheme, sep, rest = request.url.partition(':') scheme = {'ws': 'http', 'wss': 'https'}[scheme] request.url = scheme + sep + rest request.headers.update({ 'Upgrade': 'websocket', 'Connection': 'Upgrade', 'Sec-WebSocket-Key': self.key, 'Sec-WebSocket-Version': '13', }) self.resolver = Resolver(io_loop=io_loop) super(WebSocketClientConnection, self).__init__( io_loop, None, request, lambda: None, self._on_http_response, 104857600, self.resolver) def close(self): """Closes the websocket connection. .. versionadded:: 3.2 """ if self.protocol is not None: self.protocol.close() self.protocol = None def _on_close(self): self.on_message(None) self.resolver.close() super(WebSocketClientConnection, self)._on_close() def _on_http_response(self, response): if not self.connect_future.done(): if response.error: self.connect_future.set_exception(response.error) else: self.connect_future.set_exception(WebSocketError( "Non-websocket response")) def _handle_1xx(self, code): assert code == 101 assert self.headers['Upgrade'].lower() == 'websocket' assert self.headers['Connection'].lower() == 'upgrade' accept = WebSocketProtocol13.compute_accept_value(self.key) assert self.headers['Sec-Websocket-Accept'] == accept self.protocol = WebSocketProtocol13(self, mask_outgoing=True) self.protocol._receive_frame() if self._timeout is not None: self.io_loop.remove_timeout(self._timeout) self._timeout = None self.connect_future.set_result(self) def write_message(self, message, binary=False): """Sends a message to the WebSocket server.""" self.protocol.write_message(message, binary) def read_message(self, callback=None): """Reads a message from the WebSocket server. Returns a future whose result is the message, or None if the connection is closed. If a callback argument is given it will be called with the future when it is ready. """ assert self.read_future is None future = TracebackFuture() if self.read_queue: future.set_result(self.read_queue.popleft()) else: self.read_future = future if callback is not None: self.io_loop.add_future(future, callback) return future def on_message(self, message): if self.read_future is not None: self.read_future.set_result(message) self.read_future = None else: self.read_queue.append(message) def on_pong(self, data): pass
class UDPClient(object): """A non-blocking UDP connection factory. .. versionchanged:: 5.0 The ``io_loop`` argument (deprecated since version 4.1) has been removed. """ def __init__(self, resolver: Resolver = None) -> None: if resolver is not None: self.resolver = resolver self._own_resolver = False else: self.resolver = Resolver() self._own_resolver = True # self.io_loop = io_loop or IOLoop.current() # def __init__(self, resolver: Resolver = None) -> None: # if resolver is not None: # self.resolver = resolver # self._own_resolver = False # else: # self.resolver = Resolver() # self._own_resolver = True def close(self) -> None: if self._own_resolver: self.resolver.close() def connect( self, host: str, port: int, af: socket.AddressFamily = socket.AF_UNSPEC, ssl_options=None, max_buffer_size: int = None, source_ip: str = None, source_port: int = None, timeout: Union[float, datetime.timedelta] = None, ) -> IOStream: if timeout is not None: if isinstance(timeout, numbers.Real): timeout = IOLoop.current().time() + timeout elif isinstance(timeout, datetime.timedelta): timeout = IOLoop.current().time() + timeout.total_seconds() else: raise TypeError("Unsupported timeout %r" % timeout) if timeout is not None: addrinfo = gen.with_timeout( timeout, self.resolver.resolve(host, port, af) ) else: addrinfo = self.resolver.resolve(host, port, af) # noinspection PyTypeChecker connector = _Connector(addrinfo, functools.partial(self._create_stream, max_buffer_size, source_ip=source_ip, source_port=source_port, ), ) af, addr, stream = connector.start(connect_timeout=timeout) # TODO: For better performance we could cache the (af, addr) # information here and re-use it on subsequent connections to # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) if ssl_options is not None: if timeout is not None: stream = gen.with_timeout( timeout, stream.start_tls( False, ssl_options=ssl_options, server_hostname=host ), ) else: stream = stream.start_tls( False, ssl_options=ssl_options, server_hostname=host ) return stream # sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # # sock.bind((host, port)) # # # if broadcast: # # sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # # # # if reuse: # # sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # # if timeout is not None: # if isinstance(timeout, numbers.Real): # timeout = IOLoop.current().time() + timeout # elif isinstance(timeout, datetime.timedelta): # timeout = IOLoop.current().time() + timeout.total_seconds() # else: # raise TypeError("Unsupported timeout %r" % timeout) # if timeout is not None: # addrinfo = gen.with_timeout( # timeout, self.resolver.resolve(host, port, af) # ) # else: # addrinfo = self.resolver.resolve(host, port, af) # connector = _Connector( # # addrinfo, # functools.partial( # self._create_stream, # max_buffer_size, # source_ip=source_ip, # source_port=source_port, # ), # ) # af, addr, stream = connector.start(connect_timeout=timeout) # # # stream = UDPStream( # # socket=sock, # # max_buffer_size=max_buffer_size, # # destination=(host, port), # # ) # # # # if ssl_options is not None: # # stream = yield stream.start_tls( # # server_side=False, # # ssl_options=ssl_options, # # server_hostname=host, # # ) # # return stream # async def connect( # self, # host: str, # port: int, # af: socket.AddressFamily = socket.AF_UNSPEC, # # ssl_options: Union[Dict[str, Any], ssl.SSLContext] = None, # max_buffer_size: int = None, # source_ip: str = None, # source_port: int = None, # timeout: Union[float, datetime.timedelta] = None, # ) -> IOStream: # if timeout is not None: # if isinstance(timeout, numbers.Real): # timeout = IOLoop.current().time() + timeout # elif isinstance(timeout, datetime.timedelta): # timeout = IOLoop.current().time() + timeout.total_seconds() # else: # raise TypeError("Unsupported timeout %r" % timeout) # if timeout is not None: # addrinfo = await gen.with_timeout( # timeout, self.resolver.resolve(host, port, af) # ) # else: # addrinfo = await self.resolver.resolve(host, port, af) # connector = _Connector( # addrinfo, # functools.partial( # self._create_stream, # max_buffer_size, # source_ip=source_ip, # source_port=source_port, # ), # ) # af, addr, stream = await connector.start(connect_timeout=timeout) # # TODO: For better performance we could cache the (af, addr) # # information here and re-use it on subsequent connections to # # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) # if ssl_options is not None: # if timeout is not None: # stream = await gen.with_timeout( # timeout, # stream.start_tls( # False, ssl_options=ssl_options, server_hostname=host # ), # ) # else: # stream = await stream.start_tls( # False, ssl_options=ssl_options, server_hostname=host # ) # return stream def _create_stream( self, max_buffer_size: int, af: socket.AddressFamily, addr: Tuple, source_ip: str = None, source_port: int = None, ) -> Tuple[IOStream, "Future[IOStream]"]: # Always connect in plaintext; we'll convert to ssl if necessary # after one connection has completed. source_port_bind = source_port if isinstance(source_port, int) else 0 source_ip_bind = source_ip if source_port_bind and not source_ip: # User required a specific port, but did not specify # a certain source IP, will bind to the default loopback. source_ip_bind = "::1" if af == socket.AF_INET6 else "127.0.0.1" # Trying to use the same address family as the requested af socket: # - 127.0.0.1 for IPv4 # - ::1 for IPv6 socket_obj = socket.socket(af) set_close_exec(socket_obj.fileno()) if source_port_bind or source_ip_bind: # If the user requires binding also to a specific IP/port. try: socket_obj.bind((source_ip_bind, source_port_bind)) except socket.error: socket_obj.close() # Fail loudly if unable to use the IP/port. raise try: stream = IOStream(socket_obj, max_buffer_size=max_buffer_size) except socket.error as e: fu = Future() # type: Future[IOStream] fu.set_exception(e) return stream, fu else: return stream, stream.connect(addr)
class WebSocketClientConnection(simple_httpclient._HTTPConnection): """WebSocket client connection.""" def __init__(self, io_loop, request): self.connect_future = TracebackFuture() self.read_future = None self.read_queue = collections.deque() self.key = base64.b64encode(os.urandom(16)) scheme, sep, rest = request.url.partition(':') scheme = {'ws': 'http', 'wss': 'https'}[scheme] request.url = scheme + sep + rest request.headers.update({ 'Upgrade': 'websocket', 'Connection': 'Upgrade', 'Sec-WebSocket-Key': self.key, 'Sec-WebSocket-Version': '13', }) self.resolver = Resolver(io_loop=io_loop) super(WebSocketClientConnection, self).__init__(io_loop, None, request, lambda: None, self._on_http_response, 104857600, self.resolver) def _on_close(self): self.on_message(None) self.resolver.close() def _on_http_response(self, response): if not self.connect_future.done(): if response.error: self.connect_future.set_exception(response.error) else: self.connect_future.set_exception( WebSocketError("Non-websocket response")) def _handle_1xx(self, code): assert code == 101 assert self.headers['Upgrade'].lower() == 'websocket' assert self.headers['Connection'].lower() == 'upgrade' accept = WebSocketProtocol13.compute_accept_value(self.key) assert self.headers['Sec-Websocket-Accept'] == accept self.protocol = WebSocketProtocol13(self, mask_outgoing=True) self.protocol._receive_frame() if self._timeout is not None: self.io_loop.remove_timeout(self._timeout) self._timeout = None self.connect_future.set_result(self) def write_message(self, message, binary=False): """Sends a message to the WebSocket server.""" self.protocol.write_message(message, binary) def read_message(self, callback=None): """Reads a message from the WebSocket server. Returns a future whose result is the message, or None if the connection is closed. If a callback argument is given it will be called with the future when it is ready. """ assert self.read_future is None future = TracebackFuture() if self.read_queue: future.set_result(self.read_queue.popleft()) else: self.read_future = future if callback is not None: self.io_loop.add_future(future, callback) return future def on_message(self, message): if self.read_future is not None: self.read_future.set_result(message) self.read_future = None else: self.read_queue.append(message) def on_pong(self, data): pass
class TCPClient(object): """A non-blocking TCP connection factory. .. versionchanged:: 5.0 The ``io_loop`` argument (deprecated since version 4.1) has been removed. """ def __init__(self, resolver=None): if resolver is not None: self.resolver = resolver self._own_resolver = False else: self.resolver = Resolver() self._own_resolver = True def close(self): if self._own_resolver: self.resolver.close() @gen.coroutine def connect(self, host, port, af=socket.AF_UNSPEC, ssl_options=None, max_buffer_size=None, source_ip=None, source_port=None, timeout=None): """Connect to the given host and port. Asynchronously returns an `.IOStream` (or `.SSLIOStream` if ``ssl_options`` is not None). Using the ``source_ip`` kwarg, one can specify the source IP address to use when establishing the connection. In case the user needs to resolve and use a specific interface, it has to be handled outside of Tornado as this depends very much on the platform. Raises `TimeoutError` if the input future does not complete before ``timeout``, which may be specified in any form allowed by `.IOLoop.add_timeout` (i.e. a `datetime.timedelta` or an absolute time relative to `.IOLoop.time`) Similarly, when the user requires a certain source port, it can be specified using the ``source_port`` arg. .. versionchanged:: 4.5 Added the ``source_ip`` and ``source_port`` arguments. """ if timeout is not None: if isinstance(timeout, numbers.Real): timeout = IOLoop.current().time() + timeout elif isinstance(timeout, datetime.timedelta): timeout = IOLoop.current().time() + timedelta_to_seconds(timeout) else: raise TypeError("Unsupported timeout %r" % timeout) if timeout is not None: addrinfo = yield gen.with_timeout( timeout, self.resolver.resolve(host, port, af)) else: addrinfo = yield self.resolver.resolve(host, port, af) connector = _Connector( addrinfo, functools.partial(self._create_stream, max_buffer_size, source_ip=source_ip, source_port=source_port) ) af, addr, stream = yield connector.start(connect_timeout=timeout) # TODO: For better performance we could cache the (af, addr) # information here and re-use it on subsequent connections to # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) if ssl_options is not None: if timeout is not None: stream = yield gen.with_timeout(timeout, stream.start_tls( False, ssl_options=ssl_options, server_hostname=host)) else: stream = yield stream.start_tls(False, ssl_options=ssl_options, server_hostname=host) raise gen.Return(stream) def _create_stream(self, max_buffer_size, af, addr, source_ip=None, source_port=None): # Always connect in plaintext; we'll convert to ssl if necessary # after one connection has completed. source_port_bind = source_port if isinstance(source_port, int) else 0 source_ip_bind = source_ip if source_port_bind and not source_ip: # User required a specific port, but did not specify # a certain source IP, will bind to the default loopback. source_ip_bind = '::1' if af == socket.AF_INET6 else '127.0.0.1' # Trying to use the same address family as the requested af socket: # - 127.0.0.1 for IPv4 # - ::1 for IPv6 socket_obj = socket.socket(af) set_close_exec(socket_obj.fileno()) if source_port_bind or source_ip_bind: # If the user requires binding also to a specific IP/port. try: socket_obj.bind((source_ip_bind, source_port_bind)) except socket.error: socket_obj.close() # Fail loudly if unable to use the IP/port. raise try: stream = IOStream(socket_obj, max_buffer_size=max_buffer_size) except socket.error as e: fu = Future() fu.set_exception(e) return fu else: return stream, stream.connect(addr)
class WebSocketClientConnection(simple_httpclient._HTTPConnection): """WebSocket client connection. This class should not be instantiated directly; use the `websocket_connect` function instead. """ def __init__(self, io_loop, request): self.connect_future = TracebackFuture() self.read_future = None self.read_queue = collections.deque() self.key = base64.b64encode(os.urandom(16)) scheme, sep, rest = request.url.partition(':') scheme = {'ws': 'http', 'wss': 'https'}[scheme] request.url = scheme + sep + rest request.headers.update({ 'Upgrade': 'websocket', 'Connection': 'Upgrade', 'Sec-WebSocket-Key': self.key, 'Sec-WebSocket-Version': '13', }) self.resolver = Resolver(io_loop=io_loop) super(WebSocketClientConnection, self).__init__(io_loop, None, request, lambda: None, self._on_http_response, 104857600, self.resolver, 65536) def close(self, code=None, reason=None): """Closes the websocket connection. ``code`` and ``reason`` are documented under `WebSocketHandler.close`. .. versionadded:: 3.2 .. versionchanged:: 3.3 Added the ``code`` and ``reason`` arguments. """ if self.protocol is not None: self.protocol.close(code, reason) self.protocol = None def _on_close(self): self.on_message(None) self.resolver.close() super(WebSocketClientConnection, self)._on_close() def _on_http_response(self, response): if not self.connect_future.done(): if response.error: self.connect_future.set_exception(response.error) else: self.connect_future.set_exception( WebSocketError("Non-websocket response")) def headers_received(self, start_line, headers): if start_line.code != 101: return super(WebSocketClientConnection, self).headers_received(start_line, headers) self.headers = headers assert self.headers['Upgrade'].lower() == 'websocket' assert self.headers['Connection'].lower() == 'upgrade' accept = WebSocketProtocol13.compute_accept_value(self.key) assert self.headers['Sec-Websocket-Accept'] == accept self.protocol = WebSocketProtocol13(self, mask_outgoing=True) self.protocol._receive_frame() if self._timeout is not None: self.io_loop.remove_timeout(self._timeout) self._timeout = None self.stream = self.connection.detach() self.stream.set_close_callback(self._on_close) self.connect_future.set_result(self) def write_message(self, message, binary=False): """Sends a message to the WebSocket server.""" self.protocol.write_message(message, binary) def read_message(self, callback=None): """Reads a message from the WebSocket server. Returns a future whose result is the message, or None if the connection is closed. If a callback argument is given it will be called with the future when it is ready. """ assert self.read_future is None future = TracebackFuture() if self.read_queue: future.set_result(self.read_queue.popleft()) else: self.read_future = future if callback is not None: self.io_loop.add_future(future, callback) return future def on_message(self, message): if self.read_future is not None: self.read_future.set_result(message) self.read_future = None else: self.read_queue.append(message) def on_pong(self, data): pass
class TCPClient(object): """A non-blocking TCP connection factory. .. versionchanged:: 4.1 The ``io_loop`` argument is deprecated. """ def __init__(self, resolver=None, io_loop=None): self.io_loop = io_loop or IOLoop.current() if resolver is not None: self.resolver = resolver self._own_resolver = False else: self.resolver = Resolver(io_loop=io_loop) self._own_resolver = True def close(self): if self._own_resolver: self.resolver.close() @gen.coroutine def connect(self, host, port, af=socket.AF_UNSPEC, ssl_options=None, max_buffer_size=None, source_ip=None, source_port=None): """Connect to the given host and port. Asynchronously returns an `.IOStream` (or `.SSLIOStream` if ``ssl_options`` is not None). Using the ``source_ip`` kwarg, one can specify the source IP address to use when establishing the connection. In case the user needs to resolve and use a specific interface, it has to be handled outside of Tornado as this depends very much on the platform. Similarly, when the user requires a certain source port, it can be specified using the ``source_port`` arg. """ addrinfo = yield self.resolver.resolve(host, port, af) connector = _Connector( addrinfo, self.io_loop, functools.partial(self._create_stream, max_buffer_size, source_ip=source_ip, source_port=source_port) ) af, addr, stream = yield connector.start() # TODO: For better performance we could cache the (af, addr) # information here and re-use it on subsequent connections to # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) if ssl_options is not None: stream = yield stream.start_tls(False, ssl_options=ssl_options, server_hostname=host) raise gen.Return(stream) def _create_stream(self, max_buffer_size, af, addr, source_ip=None, source_port=None): # Always connect in plaintext; we'll convert to ssl if necessary # after one connection has completed. source_port_bind = source_port if isinstance(source_port, int) else 0 source_ip_bind = source_ip if source_port_bind and not source_ip: # User required a specific port, but did not specify # a certain source IP, will bind to the default loopback. source_ip_bind = '::1' if af == socket.AF_INET6 else '127.0.0.1' # Trying to use the same address family as the requested af socket: # - 127.0.0.1 for IPv4 # - ::1 for IPv6 socket_obj = socket.socket(af) if source_port_bind or source_ip_bind: # If the user requires binding also to a specific IP/port. try: socket_obj.bind((source_ip_bind, source_port_bind)) except socket.error: socket_obj.close() # Fail loudly if unable to use the IP/port. raise try: stream = IOStream(socket_obj, io_loop=self.io_loop, max_buffer_size=max_buffer_size) except socket.error as e: fu = Future() fu.set_exception(e) return fu else: return stream.connect(addr)
class WebSocketClientConnection(simple_httpclient._HTTPConnection): """WebSocket client connection.""" def __init__(self, io_loop, request): self.connect_future = TracebackFuture() self.read_future = None self.read_queue = collections.deque() self.key = base64.b64encode(os.urandom(16)) scheme, sep, rest = request.url.partition(":") scheme = {"ws": "http", "wss": "https"}[scheme] request.url = scheme + sep + rest request.headers.update( { "Upgrade": "websocket", "Connection": "Upgrade", "Sec-WebSocket-Key": self.key, "Sec-WebSocket-Version": "13", } ) self.resolver = Resolver(io_loop=io_loop) super(WebSocketClientConnection, self).__init__( io_loop, None, request, lambda: None, self._on_http_response, 104857600, self.resolver ) def _on_close(self): self.on_message(None) self.resolver.close() def _on_http_response(self, response): if not self.connect_future.done(): if response.error: self.connect_future.set_exception(response.error) else: self.connect_future.set_exception(WebSocketError("Non-websocket response")) def _handle_1xx(self, code): assert code == 101 assert self.headers["Upgrade"].lower() == "websocket" assert self.headers["Connection"].lower() == "upgrade" accept = WebSocketProtocol13.compute_accept_value(self.key) assert self.headers["Sec-Websocket-Accept"] == accept self.protocol = WebSocketProtocol13(self, mask_outgoing=True) self.protocol._receive_frame() if self._timeout is not None: self.io_loop.remove_timeout(self._timeout) self._timeout = None self.connect_future.set_result(self) def write_message(self, message, binary=False): """Sends a message to the WebSocket server.""" self.protocol.write_message(message, binary) def read_message(self, callback=None): """Reads a message from the WebSocket server. Returns a future whose result is the message, or None if the connection is closed. If a callback argument is given it will be called with the future when it is ready. """ assert self.read_future is None future = TracebackFuture() if self.read_queue: future.set_result(self.read_queue.popleft()) else: self.read_future = future if callback is not None: self.io_loop.add_future(future, callback) return future def on_message(self, message): if self.read_future is not None: self.read_future.set_result(message) self.read_future = None else: self.read_queue.append(message) def on_pong(self, data): pass
class TCPClient(object): """A non-blocking TCP connection factory. .. versionchanged:: 4.1 The ``io_loop`` argument is deprecated. """ def __init__(self, resolver=None, io_loop=None): self.io_loop = io_loop or IOLoop.current() if resolver is not None: self.resolver = resolver self._own_resolver = False else: self.resolver = Resolver(io_loop=io_loop) self._own_resolver = True def close(self): if self._own_resolver: self.resolver.close() @gen.coroutine def connect(self, host, port, af=socket.AF_UNSPEC, ssl_options=None, max_buffer_size=None, source_ip=None, source_port=None): """Connect to the given host and port. Asynchronously returns an `.IOStream` (or `.SSLIOStream` if ``ssl_options`` is not None). Using the ``source_ip`` kwarg, one can specify the source IP address to use when establishing the connection. In case the user needs to resolve and use a specific interface, it has to be handled outside of Tornado as this depends very much on the platform. Similarly, when the user requires a certain source port, it can be specified using the ``source_port`` arg. """ addrinfo = yield self.resolver.resolve(host, port, af) connector = _Connector( addrinfo, self.io_loop, functools.partial(self._create_stream, max_buffer_size, source_ip=source_ip, source_port=source_port) ) af, addr, stream = yield connector.start() # TODO: For better performance we could cache the (af, addr) # information here and re-use it on subsequent connections to # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) if ssl_options is not None: stream = yield stream.start_tls(False, ssl_options=ssl_options, server_hostname=host) raise gen.Return(stream) def _create_stream(self, max_buffer_size, af, addr, source_ip=None, source_port=None): # Always connect in plaintext; we'll convert to ssl if necessary # after one connection has completed. source_port_bind = source_port if isinstance(source_port, int) else 0 source_ip_bind = source_ip if source_port_bind and not source_ip: # User required a specific port, but did not specify # a certain source IP, will bind to the default loopback. source_ip_bind = '::1' if af == socket.AF_INET6 else '127.0.0.1' # Trying to use the same address family as the requested af socket: # - 127.0.0.1 for IPv4 # - ::1 for IPv6 socket_obj = socket.socket(af) if source_port_bind or source_ip_bind: # If the user requires binding also to a specific IP/port. socket_obj.bind((source_ip_bind, source_port_bind)) # Fail loudly if unable to use the IP/port. try: stream = IOStream(socket_obj, io_loop=self.io_loop, max_buffer_size=max_buffer_size) except socket.error as e: fu = Future() fu.set_exception(e) return fu else: return stream.connect(addr)
class LBConnector(object): """ Adds support for sequential search for live LB to IOStream. Uses socket.create_connection to perform it - sequential approach. """ def __init__(self, io_loop): self.io_loop = io_loop # Default blocking resolver calling socket.getaddrinfo self.resolver = Resolver(io_loop=io_loop) self._own_resolver = True def close(self): self.resolver.close() @gen.coroutine def connect(self, host, port, timeout, af=socket.AF_UNSPEC, max_buffer_size=None): """Connect to the given host and port. Asynchronously returns an `.IOStream`. """ addrinfo = yield self.resolver.resolve(host, port, af) connector = _RandomConnector( addrinfo, self.io_loop, partial(self._create_stream, max_buffer_size, timeout)) # Use large timeout for connection search, assume that all addresses # apart from the last will timeout total_connect_timeout = (len(addrinfo) + 1) * timeout af, addr, stream = yield connector.start(total_connect_timeout) # TODO: For better performance we could cache the (af, addr) # information here and re-use it on subsequent connections to # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) # TODO: support ssl; it can be copied from tornado but we need to # read ssl opts from Connection raise gen.Return(stream) def _create_stream(self, max_buffer_size, timeout, af, addr): sock = socket.socket(af) sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) future = Future() stream = iostream.IOStream(sock, io_loop=self.io_loop, max_buffer_size=max_buffer_size) def on_stream_connect_timeout(): """ Close the stream and pass an exception to caller """ stream.set_close_callback(None) exc = iostream.StreamClosedError("Connect timeout") stream.close(exc_info=(None, exc, None)) future.set_exception(exc) def on_stream_connected(): """ On success clean after ourselves """ self.io_loop.remove_timeout(handler) stream.set_close_callback(None) future.set_result(stream) def on_stream_error(): """ Stream close while connecting means it failed Cancel the timeout and pass the error to caller """ self.io_loop.remove_timeout(handler) future.set_exception(stream.error) timeout = timedelta(seconds=timeout) handler = self.io_loop.add_timeout(timeout, on_stream_connect_timeout) stream.set_close_callback(on_stream_error) stream.connect(addr, callback=on_stream_connected) return future