diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index 23310ee6cb..94a2405655 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -6,12 +6,13 @@ import types import warnings -from eventlet.green import urllib -from eventlet.green import socket from eventlet.green import BaseHTTPServer -from eventlet import greenpool +from eventlet.green import socket +from eventlet.green import urllib from eventlet import greenio -from eventlet.support import get_errno, six +from eventlet import greenpool +from eventlet import support +from eventlet.support import six DEFAULT_MAX_SIMULTANEOUS_REQUESTS = 1024 @@ -273,7 +274,7 @@ def handle_one_request(self): except greenio.SSL.ZeroReturnError: self.raw_requestline = '' except socket.error as e: - if get_errno(e) not in BAD_SOCK: + if support.get_errno(e) not in BAD_SOCK: raise self.raw_requestline = '' @@ -320,7 +321,7 @@ def handle_one_request(self): self.handle_one_response() except socket.error as e: # Broken pipe, connection reset by peer - if get_errno(e) not in BROKEN_SOCK: + if support.get_errno(e) not in BROKEN_SOCK: raise finally: self.server.outstanding_requests -= 1 @@ -390,7 +391,9 @@ def write(data, _writelines=wfile.writelines): length[0] = length[0] + sum(map(len, towrite)) except UnicodeEncodeError: self.server.log_message( - "Encountered non-ascii unicode while attempting to write wsgi response: %r" % [x for x in towrite if isinstance(x, six.text_type)]) + "Encountered non-ascii unicode while attempting to write" + "wsgi response: %r" % + [x for x in towrite if isinstance(x, six.text_type)]) self.server.log_message(traceback.format_exc()) _writelines( ["HTTP/1.1 500 Internal Server Error\r\n", @@ -573,7 +576,7 @@ def finish(self): BaseHTTPServer.BaseHTTPRequestHandler.finish(self) except socket.error as e: # Broken pipe, connection reset by peer - if get_errno(e) not in BROKEN_SOCK: + if support.get_errno(e) not in BROKEN_SOCK: raise greenio.shutdown_safe(self.connection) self.connection.close() @@ -701,28 +704,44 @@ def server(sock, site, socket_timeout=None, capitalize_response_headers=True): """Start up a WSGI server handling requests from the supplied server - socket. This function loops forever. The *sock* object will be closed after server exits, - but the underlying file descriptor will remain open, so if you have a dup() of *sock*, - it will remain usable. + socket. This function loops forever. The *sock* object will be + closed after server exits, but the underlying file descriptor will + remain open, so if you have a dup() of *sock*, it will remain usable. :param sock: Server socket, must be already bound to a port and listening. :param site: WSGI application function. - :param log: File-like object that logs should be written to. If not specified, sys.stderr is used. + :param log: File-like object that logs should be written to. + If not specified, sys.stderr is used. :param environ: Additional parameters that go into the environ dictionary of every request. :param max_size: Maximum number of client connections opened at any time by this server. - :param max_http_version: Set to "HTTP/1.0" to make the server pretend it only supports HTTP 1.0. This can help with applications or clients that don't behave properly using HTTP 1.1. + :param max_http_version: Set to "HTTP/1.0" to make the server pretend it only supports HTTP 1.0. + This can help with applications or clients that don't behave properly using HTTP 1.1. :param protocol: Protocol class. Deprecated. :param server_event: Used to collect the Server object. Deprecated. - :param minimum_chunk_size: Minimum size in bytes for http chunks. This can be used to improve performance of applications which yield many small strings, though using it technically violates the WSGI spec. This can be overridden on a per request basis by setting environ['eventlet.minimum_write_chunk_size']. - :param log_x_forwarded_for: If True (the default), logs the contents of the x-forwarded-for header in addition to the actual client ip address in the 'client_ip' field of the log line. - :param custom_pool: A custom GreenPool instance which is used to spawn client green threads. If this is supplied, max_size is ignored. - :param keepalive: If set to False, disables keepalives on the server; all connections will be closed after serving one request. + :param minimum_chunk_size: Minimum size in bytes for http chunks. This can be used to improve + performance of applications which yield many small strings, though + using it technically violates the WSGI spec. This can be overridden + on a per request basis by setting environ['eventlet.minimum_write_chunk_size']. + :param log_x_forwarded_for: If True (the default), logs the contents of the x-forwarded-for + header in addition to the actual client ip address in the 'client_ip' field of the + log line. + :param custom_pool: A custom GreenPool instance which is used to spawn client green threads. + If this is supplied, max_size is ignored. + :param keepalive: If set to False, disables keepalives on the server; all connections will be + closed after serving one request. :param log_output: A Boolean indicating if the server will log data or not. - :param log_format: A python format string that is used as the template to generate log lines. The following values can be formatted into it: client_ip, date_time, request_line, status_code, body_length, wall_seconds. The default is a good example of how to use it. - :param url_length_limit: A maximum allowed length of the request url. If exceeded, 414 error is returned. - :param debug: True if the server should send exception tracebacks to the clients on 500 errors. If False, the server will respond with empty bodies. - :param socket_timeout: Timeout for client connections' socket operations. Default None means wait forever. - :param capitalize_response_headers: Normalize response headers' names to Foo-Bar. Default is True. + :param log_format: A python format string that is used as the template to generate log lines. + The following values can be formatted into it: client_ip, date_time, request_line, + status_code, body_length, wall_seconds. The default is a good example of how to + use it. + :param url_length_limit: A maximum allowed length of the request url. If exceeded, 414 error + is returned. + :param debug: True if the server should send exception tracebacks to the clients on 500 errors. + If False, the server will respond with empty bodies. + :param socket_timeout: Timeout for client connections' socket operations. Default None means + wait forever. + :param capitalize_response_headers: Normalize response headers' names to Foo-Bar. + Default is True. """ serv = Server(sock, sock.getsockname(), site, log, @@ -777,7 +796,7 @@ def server(sock, site, DeprecationWarning, stacklevel=2) pool.execute_async(serv.process_request, client_socket) except ACCEPT_EXCEPTIONS as e: - if get_errno(e) not in ACCEPT_ERRNO: + if support.get_errno(e) not in ACCEPT_ERRNO: raise except (KeyboardInterrupt, SystemExit): serv.log.write("wsgi exiting\n") @@ -791,5 +810,5 @@ def server(sock, site, # all. sock.close() except socket.error as e: - if get_errno(e) not in BROKEN_SOCK: + if support.get_errno(e) not in BROKEN_SOCK: traceback.print_exc() diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py index 0d6ce39064..4ebf13602b 100644 --- a/tests/wsgi_test.py +++ b/tests/wsgi_test.py @@ -11,20 +11,18 @@ import eventlet from eventlet import debug from eventlet import event +from eventlet.green import socket as greensocket +from eventlet.green import ssl +from eventlet.green import subprocess from eventlet import greenio from eventlet import greenthread +from eventlet import support +from eventlet.support import six from eventlet import tpool from eventlet import wsgi -from eventlet.green import socket as greensocket -from eventlet.green import ssl -from eventlet.green import subprocess -from eventlet.support import get_errno, six -from tests import ( - LimitedTestCase, - skipped, skip_with_pyevent, skip_if_no_ssl, - find_command, run_python, -) +import tests + certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt') private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key') @@ -156,7 +154,7 @@ def read_http(sock): try: response_line = fd.readline().rstrip('\r\n') except socket.error as exc: - if get_errno(exc) == 10053: + if support.get_errno(exc) == 10053: raise ConnectionClosed raise if not response_line: @@ -203,7 +201,7 @@ def read_http(sock): return result -class _TestBase(LimitedTestCase): +class _TestBase(tests.LimitedTestCase): def setUp(self): super(_TestBase, self).setUp() self.logfile = six.StringIO() @@ -221,7 +219,8 @@ def spawn_server(self, **kwargs): """Spawns a new wsgi server with the given arguments using :meth:`spawn_thread`. - Sets self.port to the port of the server""" + Sets self.port to the port of the server + """ new_kwargs = dict(max_size=128, log=self.logfile, site=self.site) @@ -309,12 +308,12 @@ def test_004_close_keepalive(self): self.assertRaises(ConnectionClosed, read_http, sock) fd.close() - @skipped + @tests.skipped def test_005_run_apachebench(self): url = 'http://localhost:12346/' # ab is apachebench subprocess.call( - [find_command('ab'), '-c', '64', '-n', '1024', '-k', url], + [tests.find_command('ab'), '-c', '64', '-n', '1024', '-k', url], stdout=subprocess.PIPE) def test_006_reject_long_urls(self): @@ -428,7 +427,7 @@ def test_011_multiple_chunks(self): # Require a CRLF to close the message body self.assertEqual(response, '\r\n') - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_012_ssl_server(self): def wsgi_app(environ, start_response): start_response('200 OK', {}) @@ -446,11 +445,12 @@ def wsgi_app(environ, start_response): sock = eventlet.connect(('localhost', self.port)) sock = eventlet.wrap_ssl(sock) sock.write( - b'POST /foo HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nContent-length:3\r\n\r\nabc') + b'POST /foo HTTP/1.1\r\nHost: localhost\r\n' + b'Connection: close\r\nContent-length:3\r\n\r\nabc') result = sock.read(8192) self.assertEqual(result[-3:], 'abc') - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_013_empty_return(self): def wsgi_app(environ, start_response): start_response("200 OK", []) @@ -526,8 +526,7 @@ def test_015_write(self): assert result2.headers_lower['transfer-encoding'] == 'chunked' def test_016_repeated_content_length(self): - """ - content-length header was being doubled up if it was set in + """content-length header was being doubled up if it was set in start_response and could also be inferred from the iterator """ def wsgi_app(environ, start_response): @@ -548,7 +547,7 @@ def wsgi_app(environ, start_response): self.assertEqual(1, len( [l for l in header_lines if l.lower().startswith('content-length')])) - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_017_ssl_zeroreturnerror(self): def server(sock, site, log): @@ -557,7 +556,7 @@ def server(sock, site, log): client_socket = sock.accept() serv.process_request(client_socket) return True - except: + except Exception: traceback.print_exc() return False @@ -671,7 +670,7 @@ def test_socket_remains_open(self): server_sock_2.accept() # shouldn't be able to use this one anymore except socket.error as exc: - self.assertEqual(get_errno(exc), errno.EBADF) + self.assertEqual(support.get_errno(exc), errno.EBADF) self.spawn_server(sock=server_sock) sock = eventlet.connect(('localhost', self.port)) fd = sock.makefile('rw') @@ -706,7 +705,7 @@ def clobberin_time(environ, start_response): def test_022_custom_pool(self): # just test that it accepts the parameter for now - # TODO: test that it uses the pool and that you can waitall() to + # TODO(waitall): test that it uses the pool and that you can waitall() to # ensure that all clients finished p = eventlet.GreenPool(5) self.spawn_server(custom_pool=p) @@ -746,13 +745,15 @@ def wsgi_app(environ, start_response): self.site.application = wsgi_app sock = eventlet.connect(('localhost', self.port)) fd = sock.makefile('rw') - fd.write(b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 1025\r\nExpect: 100-continue\r\n\r\n') + fd.write(b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 1025\r\n' + b'Expect: 100-continue\r\n\r\n') fd.flush() result = read_http(sock) self.assertEqual(result.status, 'HTTP/1.1 417 Expectation Failed') self.assertEqual(result.body, 'failure') fd.write( - b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 7\r\nExpect: 100-continue\r\n\r\ntesting') + b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 7\r\n' + b'Expect: 100-continue\r\n\r\ntesting') fd.flush() header_lines = [] while True: @@ -790,13 +791,15 @@ def wsgi_app(environ, start_response): self.site.application = wsgi_app sock = eventlet.connect(('localhost', self.port)) fd = sock.makefile('rw') - fd.write(b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 1025\r\nExpect: 100-continue\r\n\r\n') + fd.write(b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 1025\r\n' + b'Expect: 100-continue\r\n\r\n') fd.flush() result = read_http(sock) self.assertEqual(result.status, 'HTTP/1.1 417 Expectation Failed') self.assertEqual(result.body, 'failure') fd.write( - b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 7\r\nExpect: 100-continue\r\n\r\ntesting') + b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 7\r\n' + b'Expect: 100-continue\r\n\r\ntesting') fd.flush() header_lines = [] while True: @@ -840,7 +843,7 @@ def test_025_accept_errors(self): eventlet.connect(('localhost', self.port)) self.fail("Didn't expect to connect") except socket.error as exc: - self.assertEqual(get_errno(exc), errno.ECONNREFUSED) + self.assertEqual(support.get_errno(exc), errno.ECONNREFUSED) log_content = self.logfile.getvalue() assert 'Invalid argument' in log_content, log_content @@ -933,7 +936,7 @@ def test_027_keepalive_chunked(self): read_http(sock) sock.close() - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_028_ssl_handshake_errors(self): errored = [False] @@ -968,7 +971,7 @@ def server(sock): assert result.startswith('HTTP'), result assert result.endswith('hello world') except ImportError: - pass # TODO: should test with OpenSSL + pass # TODO(openssl): should test with OpenSSL greenthread.kill(g) def test_029_posthooks(self): @@ -1276,7 +1279,7 @@ def test_server_connection_timeout_exception(self): testcode_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'wsgi_test_conntimeout.py') - output = run_python(testcode_path) + output = tests.run_python(testcode_path) sections = output.split(b"SEPERATOR_SENTINEL") # first section is empty self.assertEqual(3, len(sections), output) @@ -1327,7 +1330,7 @@ def read_headers(sock): try: response_line = fd.readline() except socket.error as exc: - if get_errno(exc) == 10053: + if support.get_errno(exc) == 10053: raise ConnectionClosed raise if not response_line: @@ -1381,7 +1384,7 @@ def test_iterable_app_keeps_socket_open_unless_connection_close_sent(self): class ProxiedIterableAlreadyHandledTest(IterableAlreadyHandledTest): # same thing as the previous test but ensuring that it works with tpooled # results as well as regular ones - @skip_with_pyevent + @tests.skip_with_pyevent def get_app(self): return tpool.Proxy(super(ProxiedIterableAlreadyHandledTest, self).get_app()) @@ -1450,7 +1453,8 @@ def chunk_encode(self, chunks, dirt=None): return b def body(self, dirt=None): - return self.chunk_encode(["this", " is ", "chunked", "\nline", " 2", "\n", "line3", ""], dirt=dirt) + return self.chunk_encode(["this", " is ", "chunked", "\nline", + " 2", "\n", "line3", ""], dirt=dirt) def ping(self, fd): fd.sendall(b"GET /ping HTTP/1.1\r\n\r\n") @@ -1458,7 +1462,8 @@ def ping(self, fd): def test_short_read_with_content_length(self): body = self.body() - req = "POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\nContent-Length:1000\r\n\r\n" + body + req = "POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\n" \ + "Content-Length:1000\r\n\r\n" + body fd = self.connect() fd.sendall(req.encode()) @@ -1469,7 +1474,8 @@ def test_short_read_with_content_length(self): def test_short_read_with_zero_content_length(self): body = self.body() - req = "POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\nContent-Length:0\r\n\r\n" + body + req = "POST /short-read HTTP/1.1\r\ntransfer-encoding: Chunked\r\n" \ + "Content-Length:0\r\n\r\n" + body fd = self.connect() fd.sendall(req.encode()) self.assertEqual(read_http(fd).body, "this is ch") @@ -1501,8 +1507,8 @@ def test_dirt(self): def test_chunked_readline(self): body = self.body() - req = "POST /lines HTTP/1.1\r\nContent-Length: %s\r\ntransfer-encoding: Chunked\r\n\r\n%s" % ( - len(body), body) + req = "POST /lines HTTP/1.1\r\nContent-Length: %s\r\n" \ + "transfer-encoding: Chunked\r\n\r\n%s" % (len(body), body) fd = self.connect() fd.sendall(req.encode())