def test_multi_future_exceptions(self): with ExpectLog(app_log, "Multiple exceptions in yield list"): with self.assertRaises(RuntimeError) as cm: yield [self.async_exception(RuntimeError("error 1")), self.async_exception(RuntimeError("error 2"))] self.assertEqual(str(cm.exception), "error 1") # With only one exception, no error is logged. with self.assertRaises(RuntimeError): yield [self.async_exception(RuntimeError("error 1")), self.async_future(2)] # Exception logging may be explicitly quieted. with self.assertRaises(RuntimeError): yield gen.multi_future( [self.async_exception(RuntimeError("error 1")), self.async_exception(RuntimeError("error 2"))], quiet_exceptions=RuntimeError)
def test_body_size_override_reset(self): # The max_body_size override is reset between requests. stream = IOStream(socket.socket()) try: yield stream.connect(('127.0.0.1', self.get_http_port())) # Use a raw stream so we can make sure it's all on one connection. stream.write(b'PUT /streaming?expected_size=10240 HTTP/1.1\r\n' b'Content-Length: 10240\r\n\r\n') stream.write(b'a' * 10240) headers, response = yield gen.Task(read_stream_body, stream) self.assertEqual(response, b'10240') # Without the ?expected_size parameter, we get the old default value stream.write(b'PUT /streaming HTTP/1.1\r\n' b'Content-Length: 10240\r\n\r\n') with ExpectLog(gen_log, '.*Content-Length too long'): data = yield stream.read_until_close() self.assertEqual(data, b'') finally: stream.close()
def test_missing_headers(self, StreamingFormDataParserDelegateMock): delegate = StreamingFormDataParserDelegateMock() headers = HTTPHeaders() headers.add("Content-Type", 'multipart/form-data; boundary=1234') parser = StreamingFormDataParser(delegate, headers) data = b"""\ --1234 Foo --1234-- """.replace(b"\n", b"\r\n") with ExpectLog(gen_log, "multipart/form-data missing headers"): parser.data_received(data) self.assertFalse(delegate.start_file.called) self.assertFalse(delegate.finish_file.called) self.assertFalse(delegate.file_data_received.called)
def test_body_size_override_reset(self): # The max_body_size override is reset between requests. stream = IOStream(socket.socket()) try: yield stream.connect(("10.0.0.7", self.get_http_port())) # Use a raw stream so we can make sure it's all on one connection. stream.write(b"PUT /streaming?expected_size=10240 HTTP/1.1\r\n" b"Content-Length: 10240\r\n\r\n") stream.write(b"a" * 10240) start_line, headers, response = yield read_stream_body(stream) self.assertEqual(response, b"10240") # Without the ?expected_size parameter, we get the old default value stream.write(b"PUT /streaming HTTP/1.1\r\n" b"Content-Length: 10240\r\n\r\n") with ExpectLog(gen_log, ".*Content-Length too long"): data = yield stream.read_until_close() self.assertEqual(data, b"HTTP/1.1 400 Bad Request\r\n\r\n") finally: stream.close()
def test_connection_refused(self): cleanup_func, port = refusing_port() self.addCleanup(cleanup_func) with ExpectLog(gen_log, ".*", required=False): self.http_client.fetch("http://127.0.0.1:%d/" % port, self.stop) response = self.wait() self.assertEqual(599, response.code) if sys.platform != 'cygwin': # cygwin returns EPERM instead of ECONNREFUSED here contains_errno = str(errno.ECONNREFUSED) in str(response.error) if not contains_errno and hasattr(errno, "WSAECONNREFUSED"): contains_errno = str(errno.WSAECONNREFUSED) in str(response.error) self.assertTrue(contains_errno, response.error) # This is usually "Connection refused". # On windows, strerror is broken and returns "Unknown error". expected_message = os.strerror(errno.ECONNREFUSED) self.assertTrue(expected_message in str(response.error), response.error)
def test_exception_in_last(self): def callback2(): raise Exception('callback1 error') def finish_callback(): self.fail('finish_callback should not be called') ag = AsyncGroup(finish_callback, name='test_group') cb1 = ag.add(lambda: None) cb2 = ag.add(callback2) cb1() with ExpectLog( async_logger, r'.*aborting AsyncGroup\(name=test_group, finished=False\)'): self.assertRaises(Exception, cb2) self.assertEqual(ag._finished, True)
def test_connection_refused(self: typing.Any): cleanup_func, port = refusing_port() self.addCleanup(cleanup_func) with ExpectLog(gen_log, ".*", required=False): with self.assertRaises(socket.error) as cm: self.fetch("http://127.0.0.1:%d/" % port, raise_error=True) if sys.platform != "cygwin": # cygwin returns EPERM instead of ECONNREFUSED here contains_errno = str(errno.ECONNREFUSED) in str(cm.exception) if not contains_errno and hasattr(errno, "WSAECONNREFUSED"): contains_errno = str(errno.WSAECONNREFUSED) in str( # type: ignore cm.exception ) self.assertTrue(contains_errno, cm.exception) # This is usually "Connection refused". # On windows, strerror is broken and returns "Unknown error". expected_message = os.strerror(errno.ECONNREFUSED) self.assertTrue(expected_message in str(cm.exception), cm.exception)
def test_ipv6(self): try: self.http_server.listen(self.get_http_port(), address='::1') except socket.gaierror as e: if e.args[0] == socket.EAI_ADDRFAMILY: # python supports ipv6, but it's not configured on the network # interface, so skip this test. return raise url = self.get_url("/hello").replace("localhost", "[::1]") # ipv6 is currently disabled by default and must be explicitly requested with ExpectLog(gen_log, "uncaught exception"): self.http_client.fetch(url, self.stop) response = self.wait() self.assertEqual(response.code, 599) self.http_client.fetch(url, self.stop, allow_ipv6=True) response = self.wait() self.assertEqual(response.body, b"Hello world!")
def test_async_read_error_logging(self): # Socket errors on asynchronous reads should be logged (but only # once). server, client = self.make_iostream_pair() server.set_close_callback(self.stop) try: # Start a read that will be fullfilled asynchronously. server.read_bytes(1, lambda data: None) client.write(b('a')) # Stub out read_from_fd to make it fail. def fake_read_from_fd(): os.close(server.socket.fileno()) server.__class__.read_from_fd(server) server.read_from_fd = fake_read_from_fd # This log message is from _handle_read (not read_from_fd). with ExpectLog(gen_log, "error on read"): self.wait() finally: server.close() client.close()
def test_connection_refused(self): # When a connection is refused, the connect callback should not # be run. (The kqueue IOLoop used to behave differently from the # epoll IOLoop in this respect) server_socket, port = bind_unused_port() server_socket.close() stream = IOStream(socket.socket(), self.io_loop) self.connect_called = False def connect_callback(): self.connect_called = True stream.set_close_callback(self.stop) # log messages vary by platform and ioloop implementation with ExpectLog(gen_log, ".*", required=False): stream.connect(("localhost", port), connect_callback) self.wait() self.assertFalse(self.connect_called) self.assertTrue(isinstance(stream.error, socket.error), stream.error) if sys.platform != 'cygwin': # cygwin's errnos don't match those used on native windows python self.assertEqual(stream.error.args[0], errno.ECONNREFUSED)
def test_invalid_content_disposition(self, StreamingFormDataParserDelegateMock): delegate = StreamingFormDataParserDelegateMock() headers = HTTPHeaders() headers.add("Content-Type", 'multipart/form-data; boundary=1234') parser = StreamingFormDataParser(delegate, headers) data = b"""\ --1234 Content-Disposition: invalid; name="files"; filename="ab.txt" Foo --1234-- """.replace(b"\n", b"\r\n") with ExpectLog(gen_log, "Invalid multipart/form-data"): parser.data_received(data) self.assertFalse(delegate.start_file.called) self.assertFalse(delegate.finish_file.called) self.assertFalse(delegate.file_data_received.called)
def test_exception_in_last(self): def callback2(): raise Exception('callback1 error') def finish_callback(): self.fail('finish_callback should not be called') ag = AsyncGroup(finish_callback, name='test_group') cb1 = ag.add(lambda: None) cb2 = ag.add(callback2) cb1() with ExpectLog( async_logger, '.*test_group group: aborting async group due to unhandled exception in callback' ): self.assertRaises(Exception, cb2) self.assertEqual(ag._finish_cb_called, False) self.assertEqual(ag._aborted, True)
def test_connection_refused(self): # When a connection is refused, the connect callback should not # be run. (The kqueue IOLoop used to behave differently from the # epoll IOLoop in this respect) cleanup_func, port = refusing_port() self.addCleanup(cleanup_func) stream = IOStream(socket.socket()) stream.set_close_callback(self.stop) # log messages vary by platform and ioloop implementation with ExpectLog(gen_log, ".*", required=False): with self.assertRaises(StreamClosedError): yield stream.connect(("127.0.0.1", port)) self.assertTrue(isinstance(stream.error, socket.error), stream.error) if sys.platform != 'cygwin': _ERRNO_CONNREFUSED = (errno.ECONNREFUSED, ) if hasattr(errno, "WSAECONNREFUSED"): _ERRNO_CONNREFUSED += (errno.WSAECONNREFUSED, ) # cygwin's errnos don't match those used on native windows python self.assertTrue(stream.error.args[0] in _ERRNO_CONNREFUSED)
def test_notifications(self): f = Future() ag = AsyncGroup(partial(f.set_result, True)) not1 = ag.add_notification() not2 = ag.add_notification() self.assertEqual(ag._finished, False) not1() self.assertEqual(ag._finished, False) not2('params', are='ignored') self.assertEqual(ag._finished, True) self.assertEqual(f.result(), True) with ExpectLog( async_logger, r'.*trying to finish already finished AsyncGroup\(name=None, finished=True\)' ): ag.finish()
def test_cookie_tampering_future_timestamp(self): handler = CookieTestRequestHandler() # this string base64-encodes to '12345678' handler.set_secure_cookie('foo', binascii.a2b_hex(b'd76df8e7aefc')) cookie = handler._cookies['foo'] match = re.match(br'12345678\|([0-9]+)\|([0-9a-f]+)', cookie) self.assertTrue(match) timestamp = match.group(1) sig = match.group(2) self.assertEqual( _create_signature(handler.application.settings["cookie_secret"], 'foo', '12345678', timestamp), sig) # shifting digits from payload to timestamp doesn't alter signature # (this is not desirable behavior, just confirming that that's how it # works) self.assertEqual( _create_signature(handler.application.settings["cookie_secret"], 'foo', '1234', b'5678' + timestamp), sig) # tamper with the cookie handler._cookies['foo'] = utf8( '1234|5678%s|%s' % (to_basestring(timestamp), to_basestring(sig))) # it gets rejected with ExpectLog(gen_log, "Cookie timestamp in future"): self.assertTrue(handler.get_secure_cookie('foo') is None)
def test_exception_in_first(self): def callback1(): raise Exception('callback1 error') def callback2(): self.fail('callback2 should not be called') def finish_callback(): self.fail('finish_callback should not be called') ag = AsyncGroup(finish_callback, name='test_group') cb1 = ag.add(callback1) cb2 = ag.add(callback2) self.assertRaises(Exception, cb1) self.assertEqual(ag._finished, True) with ExpectLog( async_logger, r'.*ignoring executing callback in AsyncGroup\(name=test_group, finished=True\)' ): cb2() self.assertEqual(ag._finished, True)
def test_uncaught_exception_log(self): if IOLoop.configured_class().__name__.endswith('AsyncIOLoop'): # Install an exception handler that mirrors our # non-asyncio logging behavior. def exc_handler(loop, context): app_log.error('%s: %s', context['message'], type(context.get('exception'))) self.io_loop.asyncio_loop.set_exception_handler(exc_handler) @gen.coroutine def f(): yield gen.moment 1 / 0 g = f() with ExpectLog( app_log, "(?s)Future.* exception was never retrieved:" ".*ZeroDivisionError"): yield gen.moment yield gen.moment del g gc.collect() # for PyPy
def test_error_in_async_open(self): with ExpectLog(app_log, "Uncaught exception"): ws = yield self.ws_connect("/error_in_async_open") res = yield ws.read_message() self.assertIsNone(res)
def test_error_in_on_message(self): ws = yield self.ws_connect("/error_in_on_message") ws.write_message("hello") with ExpectLog(app_log, "Uncaught exception"): response = yield ws.read_message() self.assertIs(response, None)
def test_stack_context(self): with ExpectLog(app_log, "Uncaught exception GET /"): self.http_client.fetch(self.get_url('/'), self.handle_response) self.wait() self.assertEqual(self.response.code, 500) self.assertTrue(b'got expected exception' in self.response.body)
def test_twitter_show_user_error_legacy(self): with ExpectLog(gen_log, 'Error response HTTP 500'): response = self.fetch( '/legacy/twitter/client/show_user?name=error') self.assertEqual(response.code, 500) self.assertEqual(response.body, b'error from twitter request')
def test_coroutine_exception_handler(self): # Make sure we get an error and not a timeout with ExpectLog(app_log, "Uncaught exception GET /coroutine_exception"): response = self.fetch('/coroutine_exception') self.assertEqual(500, response.code)
def test_chunked_with_content_length(self): # Make sure the invalid headers are detected with ExpectLog(gen_log, ("Malformed HTTP message from None: Response " "with both Transfer-Encoding and Content-Length")): response = self.fetch('/chunkwithcl') self.assertEqual(response.code, 599)
def test_large_body(self): with ExpectLog(gen_log, "Malformed HTTP message from None: Content-Length too long"): response = self.fetch('/large') self.assertEqual(response.code, 599)
def test_ssl_context_handshake_fail(self): with ExpectLog(gen_log, "SSL Error|Uncaught exception"): ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) ctx.verify_mode = ssl.CERT_REQUIRED resp = self.fetch("/hello", ssl_options=ctx) self.assertRaises(ssl.SSLError, resp.rethrow)
def test_large_headers(self): with ExpectLog(gen_log, "Unsatisfiable read"): response = self.fetch('/large') self.assertEqual(response.code, 599)
def test_large_body_streaming(self): with ExpectLog(gen_log, ".*Content-Length too long"): response = self.fetch("/streaming", method="PUT", body=b"a" * 10240) self.assertEqual(response.code, 400)
def test_large_headers(self): with ExpectLog(gen_log, "Unsatisfiable read"): with self.assertRaises(UnsatisfiableReadError): self.fetch('/large', raise_error=True)
def test_large_body(self): with ExpectLog( gen_log, "Malformed HTTP message from None: Content-Length too long"): with self.assertRaises(HTTPStreamClosedError): self.fetch('/large', raise_error=True)
def test_ssl_options_handshake_fail(self): with ExpectLog(gen_log, "SSL Error|Uncaught exception", required=False): resp = self.fetch( "/hello", ssl_options=dict(cert_reqs=ssl.CERT_REQUIRED)) self.assertRaises(ssl.SSLError, resp.rethrow)