A little wsgi_test refactoring, added log_format argument to wsgi.server.

This commit is contained in:
Ryan Williams
2010-01-08 17:44:24 -08:00
parent 7003ded07c
commit adc1d7bb8a
2 changed files with 56 additions and 46 deletions

View File

@@ -15,6 +15,7 @@ DEFAULT_MAX_SIMULTANEOUS_REQUESTS = 1024
DEFAULT_MAX_HTTP_VERSION = 'HTTP/1.1' DEFAULT_MAX_HTTP_VERSION = 'HTTP/1.1'
MAX_REQUEST_LINE = 8192 MAX_REQUEST_LINE = 8192
MINIMUM_CHUNK_SIZE = 4096 MINIMUM_CHUNK_SIZE = 4096
DEFAULT_LOG_FORMAT='%(client_ip)s - - [%(date_time)s] "%(request_line)s" %(status_code)s %(body_length)s %(wall_seconds).6f'
__all__ = ['server', 'format_date_time'] __all__ = ['server', 'format_date_time']
@@ -303,13 +304,13 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
pass pass
finish = time.time() finish = time.time()
self.server.log_message('%s - - [%s] "%s" %s %s %.6f' % ( self.server.log_message(self.server.log_format % dict(
self.get_client_ip(), client_ip=self.get_client_ip(),
self.log_date_time_string(), date_time=self.log_date_time_string(),
self.requestline, request_line=self.requestline,
status_code[0], status_code=status_code[0],
length[0], body_length=length[0],
finish - start)) wall_seconds=finish - start))
def get_client_ip(self): def get_client_ip(self):
client_ip = self.client_address[0] client_ip = self.client_address[0]
@@ -388,8 +389,9 @@ class Server(BaseHTTPServer.HTTPServer):
max_http_version=None, max_http_version=None,
protocol=HttpProtocol, protocol=HttpProtocol,
minimum_chunk_size=None, minimum_chunk_size=None,
log_x_forwarded_for=True): log_x_forwarded_for=True,
log_format=DEFAULT_LOG_FORMAT):
self.outstanding_requests = 0 self.outstanding_requests = 0
self.socket = socket self.socket = socket
self.address = address self.address = address
@@ -405,6 +407,7 @@ class Server(BaseHTTPServer.HTTPServer):
if minimum_chunk_size is not None: if minimum_chunk_size is not None:
protocol.minimum_chunk_size = minimum_chunk_size protocol.minimum_chunk_size = minimum_chunk_size
self.log_x_forwarded_for = log_x_forwarded_for self.log_x_forwarded_for = log_x_forwarded_for
self.log_format = log_format
def get_environ(self): def get_environ(self):
socket = self.socket socket = self.socket
@@ -437,7 +440,8 @@ def server(sock, site,
server_event=None, server_event=None,
minimum_chunk_size=None, minimum_chunk_size=None,
log_x_forwarded_for=True, log_x_forwarded_for=True,
custom_pool=None): custom_pool=None,
log_format=DEFAULT_LOG_FORMAT):
""" Start up a `WSGI <http://wsgi.org/wsgi/>`_ server handling requests from the supplied server """ Start up a `WSGI <http://wsgi.org/wsgi/>`_ server handling requests from the supplied server
socket. This function loops forever. socket. This function loops forever.
@@ -449,17 +453,18 @@ def server(sock, site,
:param protocol: Protocol class. Deprecated. :param protocol: Protocol class. Deprecated.
:param server_event: Used to collect the Server object. 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. :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.
: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. :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 Pool instance which is used to spawn client green threads. If this is supplied, max_size is ignored. :param custom_pool: A custom Pool instance which is used to spawn client green threads. If this is supplied, max_size is ignored.
:param log_formar: 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. Look at DEFAULT_LOG_FORMAT for an example of how to use this.
""" """
serv = Server(sock, sock.getsockname(), serv = Server(sock, sock.getsockname(),
site, log, site, log,
environ=None, environ=None,
max_http_version=max_http_version, max_http_version=max_http_version,
protocol=protocol, protocol=protocol,
minimum_chunk_size=minimum_chunk_size, minimum_chunk_size=minimum_chunk_size,
log_x_forwarded_for=log_x_forwarded_for) log_x_forwarded_for=log_x_forwarded_for,
log_format=log_format)
if server_event is not None: if server_event is not None:
server_event.send(serv) server_event.send(serv)
if max_size is None: if max_size is None:

View File

@@ -124,20 +124,36 @@ class TestHttpd(LimitedTestCase):
super(TestHttpd, self).setUp() super(TestHttpd, self).setUp()
self.logfile = StringIO() self.logfile = StringIO()
self.site = Site() self.site = Site()
listener = api.tcp_listener(('localhost', 0)) self.killer = None
self.port = listener.getsockname()[1] self.spawn_server()
self.killer = api.spawn(
wsgi.server,
listener,
self.site,
max_size=128,
log=self.logfile)
def tearDown(self): def tearDown(self):
super(TestHttpd, self).tearDown() super(TestHttpd, self).tearDown()
api.kill(self.killer) api.kill(self.killer)
api.sleep(0) api.sleep(0)
def spawn_server(self, **kwargs):
"""Spawns a new wsgi server with the given arguments.
Sets self.port to the port of the server, and self.killer is the greenlet
running it.
Kills any previously-running server."""
if self.killer:
api.kill(self.killer)
new_kwargs = dict(max_size=128,
log=self.logfile,
site=self.site)
new_kwargs.update(kwargs)
if 'sock' not in new_kwargs:
new_kwargs['sock'] = api.tcp_listener(('localhost', 0))
self.port = new_kwargs['sock'].getsockname()[1]
self.killer = api.spawn(
wsgi.server,
**new_kwargs)
def test_001_server(self): def test_001_server(self):
sock = api.connect_tcp( sock = api.connect_tcp(
('localhost', self.port)) ('localhost', self.port))
@@ -317,10 +333,9 @@ class TestHttpd(LimitedTestCase):
private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key') private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key')
server_sock = api.ssl_listener(('localhost', 0), certificate_file, private_key_file) server_sock = api.ssl_listener(('localhost', 0), certificate_file, private_key_file)
self.spawn_server(sock=server_sock, site=wsgi_app)
api.spawn(wsgi.server, server_sock, wsgi_app, log=StringIO())
sock = api.connect_tcp(('localhost', server_sock.getsockname()[1])) sock = api.connect_tcp(('localhost', self.port))
sock = util.wrap_ssl(sock) sock = util.wrap_ssl(sock)
sock.write('POST /foo HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nContent-length:3\r\n\r\nabc') sock.write('POST /foo HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nContent-length:3\r\n\r\nabc')
result = sock.read(8192) result = sock.read(8192)
@@ -334,7 +349,7 @@ class TestHttpd(LimitedTestCase):
certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt') 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') private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key')
server_sock = api.ssl_listener(('localhost', 0), certificate_file, private_key_file) server_sock = api.ssl_listener(('localhost', 0), certificate_file, private_key_file)
api.spawn(wsgi.server, server_sock, wsgi_app, log=StringIO()) self.spawn_server(sock=server_sock, site=wsgi_app)
sock = api.connect_tcp(('localhost', server_sock.getsockname()[1])) sock = api.connect_tcp(('localhost', server_sock.getsockname()[1]))
sock = util.wrap_ssl(sock) sock = util.wrap_ssl(sock)
@@ -505,16 +520,7 @@ class TestHttpd(LimitedTestCase):
# turning off the option should work too # turning off the option should work too
self.logfile = StringIO() self.logfile = StringIO()
api.kill(self.killer) self.spawn_server(log_x_forwarded_for=False)
listener = api.tcp_listener(('localhost', 0))
self.port = listener.getsockname()[1]
self.killer = api.spawn(
wsgi.server,
listener,
self.site,
max_size=128,
log=self.logfile,
log_x_forwarded_for=False)
sock = api.connect_tcp(('localhost', self.port)) sock = api.connect_tcp(('localhost', self.port))
sock.sendall('GET / HTTP/1.1\r\nHost: localhost\r\nX-Forwarded-For: 1.2.3.4, 5.6.7.8\r\n\r\n') sock.sendall('GET / HTTP/1.1\r\nHost: localhost\r\nX-Forwarded-For: 1.2.3.4, 5.6.7.8\r\n\r\n')
@@ -551,16 +557,7 @@ class TestHttpd(LimitedTestCase):
# ensure that all clients finished # ensure that all clients finished
from eventlet import pool from eventlet import pool
p = pool.Pool(max_size=5) p = pool.Pool(max_size=5)
api.kill(self.killer) self.spawn_server(custom_pool=p)
listener = api.tcp_listener(('localhost', 0))
self.port = listener.getsockname()[1]
self.killer = api.spawn(
wsgi.server,
listener,
self.site,
max_size=128,
log=self.logfile,
custom_pool=p)
# this stuff is copied from test_001_server, could be better factored # this stuff is copied from test_001_server, could be better factored
sock = api.connect_tcp( sock = api.connect_tcp(
@@ -623,6 +620,14 @@ class TestHttpd(LimitedTestCase):
self.assertEquals(fd.read(7), 'testing') self.assertEquals(fd.read(7), 'testing')
fd.close() fd.close()
def test_025_log_format(self):
self.spawn_server(log_format="HI %(request_line)s HI")
sock = api.connect_tcp(('localhost', self.port))
sock.sendall('GET /yo! HTTP/1.1\r\nHost: localhost\r\n\r\n')
sock.recv(1024)
sock.close()
self.assert_('\nHI GET /yo! HTTP/1.1 HI\n' in self.logfile.getvalue(), self.logfile.getvalue())
if __name__ == '__main__': if __name__ == '__main__':
main() main()