2005-07-18 23:25:58 +08:00
|
|
|
"""
|
2011-05-29 05:28:52 +08:00
|
|
|
HTTP server that implements the Python WSGI protocol (PEP 333, rev 1.21).
|
2005-07-18 23:25:58 +08:00
|
|
|
|
2011-05-29 05:28:52 +08:00
|
|
|
Based on wsgiref.simple_server which is part of the standard library since 2.5.
|
2005-07-18 23:25:58 +08:00
|
|
|
|
|
|
|
This is a simple server for use in testing or debugging Django apps. It hasn't
|
2011-05-29 05:28:52 +08:00
|
|
|
been reviewed for security issues. DON'T USE IT FOR PRODUCTION USE!
|
2005-07-18 23:25:58 +08:00
|
|
|
"""
|
|
|
|
|
2015-11-07 00:19:41 +08:00
|
|
|
import logging
|
2010-11-26 21:33:53 +08:00
|
|
|
import socket
|
2017-01-07 19:11:46 +08:00
|
|
|
import socketserver
|
2007-07-16 11:50:22 +08:00
|
|
|
import sys
|
2011-05-29 05:28:52 +08:00
|
|
|
from wsgiref import simple_server
|
2005-07-18 23:25:58 +08:00
|
|
|
|
2014-01-21 04:15:14 +08:00
|
|
|
from django.core.exceptions import ImproperlyConfigured
|
2018-12-06 23:08:01 +08:00
|
|
|
from django.core.handlers.wsgi import LimitedStream
|
2011-10-22 12:30:10 +08:00
|
|
|
from django.core.wsgi import get_wsgi_application
|
2014-01-21 04:15:14 +08:00
|
|
|
from django.utils.module_loading import import_string
|
2007-10-31 11:59:40 +08:00
|
|
|
|
2014-03-29 06:22:00 +08:00
|
|
|
__all__ = ('WSGIServer', 'WSGIRequestHandler')
|
2005-07-18 23:25:58 +08:00
|
|
|
|
2015-11-07 00:19:41 +08:00
|
|
|
logger = logging.getLogger('django.server')
|
|
|
|
|
2005-07-18 23:25:58 +08:00
|
|
|
|
2011-10-22 12:30:10 +08:00
|
|
|
def get_internal_wsgi_application():
|
|
|
|
"""
|
2017-01-26 03:02:33 +08:00
|
|
|
Load and return the WSGI application as configured by the user in
|
2011-10-22 12:30:10 +08:00
|
|
|
``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,
|
|
|
|
this will be the ``application`` object in ``projectname/wsgi.py``.
|
|
|
|
|
|
|
|
This function, and the ``WSGI_APPLICATION`` setting itself, are only useful
|
2014-11-20 01:21:49 +08:00
|
|
|
for Django's internal server (runserver); external WSGI servers should just
|
|
|
|
be configured to point to the correct application object directly.
|
2011-10-22 12:30:10 +08:00
|
|
|
|
2017-01-26 03:02:33 +08:00
|
|
|
If settings.WSGI_APPLICATION is not set (is ``None``), return
|
2011-10-22 12:30:10 +08:00
|
|
|
whatever ``django.core.wsgi.get_wsgi_application`` returns.
|
|
|
|
"""
|
|
|
|
from django.conf import settings
|
|
|
|
app_path = getattr(settings, 'WSGI_APPLICATION')
|
|
|
|
if app_path is None:
|
|
|
|
return get_wsgi_application()
|
2013-02-03 05:58:02 +08:00
|
|
|
|
2014-01-21 04:15:14 +08:00
|
|
|
try:
|
|
|
|
return import_string(app_path)
|
2017-01-08 03:13:29 +08:00
|
|
|
except ImportError as err:
|
|
|
|
raise ImproperlyConfigured(
|
|
|
|
"WSGI application '%s' could not be loaded; "
|
|
|
|
"Error importing module." % app_path
|
|
|
|
) from err
|
2011-10-22 12:30:10 +08:00
|
|
|
|
|
|
|
|
2014-08-03 07:50:55 +08:00
|
|
|
def is_broken_pipe_error():
|
2019-01-28 23:01:35 +08:00
|
|
|
exc_type, _, _ = sys.exc_info()
|
|
|
|
return issubclass(exc_type, BrokenPipeError)
|
2014-08-03 07:50:55 +08:00
|
|
|
|
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class WSGIServer(simple_server.WSGIServer):
|
2005-07-18 23:25:58 +08:00
|
|
|
"""BaseHTTPServer that implements the Python WSGI protocol"""
|
|
|
|
|
2013-04-28 16:47:07 +08:00
|
|
|
request_queue_size = 10
|
|
|
|
|
2017-02-02 00:41:56 +08:00
|
|
|
def __init__(self, *args, ipv6=False, allow_reuse_address=True, **kwargs):
|
|
|
|
if ipv6:
|
2010-11-26 21:33:53 +08:00
|
|
|
self.address_family = socket.AF_INET6
|
2017-02-02 00:41:56 +08:00
|
|
|
self.allow_reuse_address = allow_reuse_address
|
2017-01-21 21:13:44 +08:00
|
|
|
super().__init__(*args, **kwargs)
|
2010-11-26 21:33:53 +08:00
|
|
|
|
2014-08-03 07:50:55 +08:00
|
|
|
def handle_error(self, request, client_address):
|
|
|
|
if is_broken_pipe_error():
|
2015-11-07 00:19:41 +08:00
|
|
|
logger.info("- Broken pipe from %s\n", client_address)
|
2014-08-03 07:50:55 +08:00
|
|
|
else:
|
2017-01-21 21:13:44 +08:00
|
|
|
super().handle_error(request, client_address)
|
2014-08-03 07:50:55 +08:00
|
|
|
|
|
|
|
|
2016-12-07 03:38:43 +08:00
|
|
|
class ThreadedWSGIServer(socketserver.ThreadingMixIn, WSGIServer):
|
|
|
|
"""A threaded version of the WSGIServer"""
|
2018-11-05 02:03:20 +08:00
|
|
|
daemon_threads = True
|
2016-12-07 03:38:43 +08:00
|
|
|
|
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class ServerHandler(simple_server.ServerHandler):
|
2015-10-31 07:38:34 +08:00
|
|
|
http_version = '1.1'
|
|
|
|
|
2018-12-06 23:08:01 +08:00
|
|
|
def __init__(self, stdin, stdout, stderr, environ, **kwargs):
|
|
|
|
"""
|
2018-12-20 17:02:45 +08:00
|
|
|
Use a LimitedStream so that unread request data will be ignored at
|
|
|
|
the end of the request. WSGIRequest uses a LimitedStream but it
|
|
|
|
shouldn't discard the data since the upstream servers usually do this.
|
|
|
|
This fix applies only for testserver/runserver.
|
2018-12-06 23:08:01 +08:00
|
|
|
"""
|
|
|
|
try:
|
|
|
|
content_length = int(environ.get('CONTENT_LENGTH'))
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
content_length = 0
|
|
|
|
super().__init__(LimitedStream(stdin, content_length), stdout, stderr, environ, **kwargs)
|
|
|
|
|
2018-11-05 02:03:20 +08:00
|
|
|
def cleanup_headers(self):
|
|
|
|
super().cleanup_headers()
|
2018-12-20 17:02:45 +08:00
|
|
|
# HTTP/1.1 requires support for persistent connections. Send 'close' if
|
|
|
|
# the content length is unknown to prevent clients from reusing the
|
|
|
|
# connection.
|
2018-11-05 02:03:20 +08:00
|
|
|
if 'Content-Length' not in self.headers:
|
|
|
|
self.headers['Connection'] = 'close'
|
2019-07-06 23:43:59 +08:00
|
|
|
# Persistent connections require threading server.
|
|
|
|
elif not isinstance(self.request_handler.server, socketserver.ThreadingMixIn):
|
|
|
|
self.headers['Connection'] = 'close'
|
2018-12-20 17:02:45 +08:00
|
|
|
# Mark the connection for closing if it's set as such above or if the
|
|
|
|
# application sent the header.
|
2018-11-05 02:03:20 +08:00
|
|
|
if self.headers.get('Connection') == 'close':
|
|
|
|
self.request_handler.close_connection = True
|
|
|
|
|
2018-12-06 23:08:01 +08:00
|
|
|
def close(self):
|
|
|
|
self.get_stdin()._read_limited()
|
|
|
|
super().close()
|
|
|
|
|
2014-08-03 07:50:55 +08:00
|
|
|
def handle_error(self):
|
|
|
|
# Ignore broken pipe errors, otherwise pass on
|
|
|
|
if not is_broken_pipe_error():
|
2017-01-21 21:13:44 +08:00
|
|
|
super().handle_error()
|
2014-08-03 07:50:55 +08:00
|
|
|
|
2005-07-18 23:25:58 +08:00
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class WSGIRequestHandler(simple_server.WSGIRequestHandler):
|
2015-10-31 07:38:34 +08:00
|
|
|
protocol_version = 'HTTP/1.1'
|
|
|
|
|
2012-11-04 03:07:56 +08:00
|
|
|
def address_string(self):
|
|
|
|
# Short-circuit parent method to not call socket.getfqdn
|
|
|
|
return self.client_address[0]
|
|
|
|
|
2005-07-27 01:49:49 +08:00
|
|
|
def log_message(self, format, *args):
|
2015-11-07 00:19:41 +08:00
|
|
|
extra = {
|
|
|
|
'request': self.request,
|
|
|
|
'server_time': self.log_date_time_string(),
|
|
|
|
}
|
|
|
|
if args[1][0] == '4':
|
2014-09-06 04:27:26 +08:00
|
|
|
# 0x16 = Handshake, 0x03 = SSL 3.0 or TLS 1.x
|
2017-01-20 17:20:53 +08:00
|
|
|
if args[0].startswith('\x16\x03'):
|
2015-11-07 00:19:41 +08:00
|
|
|
extra['status_code'] = 500
|
|
|
|
logger.error(
|
|
|
|
"You're accessing the development server over HTTPS, but "
|
|
|
|
"it only supports HTTP.\n", extra=extra,
|
|
|
|
)
|
|
|
|
return
|
|
|
|
|
|
|
|
if args[1].isdigit() and len(args[1]) == 3:
|
|
|
|
status_code = int(args[1])
|
|
|
|
extra['status_code'] = status_code
|
|
|
|
|
|
|
|
if status_code >= 500:
|
|
|
|
level = logger.error
|
|
|
|
elif status_code >= 400:
|
|
|
|
level = logger.warning
|
|
|
|
else:
|
|
|
|
level = logger.info
|
2010-01-04 20:16:09 +08:00
|
|
|
else:
|
2015-11-07 00:19:41 +08:00
|
|
|
level = logger.info
|
2010-01-04 20:16:09 +08:00
|
|
|
|
2015-11-07 00:19:41 +08:00
|
|
|
level(format, *args, extra=extra)
|
2005-07-27 01:49:49 +08:00
|
|
|
|
2014-07-22 20:25:22 +08:00
|
|
|
def get_environ(self):
|
2014-09-11 01:06:19 +08:00
|
|
|
# Strip all headers with underscores in the name before constructing
|
|
|
|
# the WSGI environ. This prevents header-spoofing based on ambiguity
|
|
|
|
# between underscores and dashes both normalized to underscores in WSGI
|
|
|
|
# env vars. Nginx and Apache 2.4+ both do this as well.
|
2017-12-07 06:17:59 +08:00
|
|
|
for k in self.headers:
|
2014-09-11 01:06:19 +08:00
|
|
|
if '_' in k:
|
|
|
|
del self.headers[k]
|
|
|
|
|
2017-01-21 21:13:44 +08:00
|
|
|
return super().get_environ()
|
2014-07-22 20:25:22 +08:00
|
|
|
|
2014-08-03 07:50:55 +08:00
|
|
|
def handle(self):
|
2018-11-05 02:03:20 +08:00
|
|
|
self.close_connection = True
|
|
|
|
self.handle_one_request()
|
|
|
|
while not self.close_connection:
|
|
|
|
self.handle_one_request()
|
|
|
|
try:
|
|
|
|
self.connection.shutdown(socket.SHUT_WR)
|
2019-01-28 23:01:35 +08:00
|
|
|
except (AttributeError, OSError):
|
2018-11-05 02:03:20 +08:00
|
|
|
pass
|
|
|
|
|
|
|
|
def handle_one_request(self):
|
2015-10-31 07:38:34 +08:00
|
|
|
"""Copy of WSGIRequestHandler.handle() but with different ServerHandler"""
|
2014-08-03 07:50:55 +08:00
|
|
|
self.raw_requestline = self.rfile.readline(65537)
|
|
|
|
if len(self.raw_requestline) > 65536:
|
|
|
|
self.requestline = ''
|
|
|
|
self.request_version = ''
|
|
|
|
self.command = ''
|
|
|
|
self.send_error(414)
|
|
|
|
return
|
|
|
|
|
|
|
|
if not self.parse_request(): # An error code has been sent, just exit
|
|
|
|
return
|
|
|
|
|
|
|
|
handler = ServerHandler(
|
|
|
|
self.rfile, self.wfile, self.get_stderr(), self.get_environ()
|
|
|
|
)
|
2018-11-05 02:03:20 +08:00
|
|
|
handler.request_handler = self # backpointer for logging & connection closing
|
2014-08-03 07:50:55 +08:00
|
|
|
handler.run(self.server.get_app())
|
|
|
|
|
2010-10-20 09:33:24 +08:00
|
|
|
|
2017-01-09 23:41:33 +08:00
|
|
|
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
|
2005-08-20 05:23:56 +08:00
|
|
|
server_address = (addr, port)
|
2011-06-17 21:08:36 +08:00
|
|
|
if threading:
|
2017-01-19 22:48:01 +08:00
|
|
|
httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
|
2011-06-17 21:08:36 +08:00
|
|
|
else:
|
2017-01-09 23:41:33 +08:00
|
|
|
httpd_cls = server_cls
|
2011-06-17 21:08:36 +08:00
|
|
|
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
|
2014-05-20 21:27:12 +08:00
|
|
|
if threading:
|
|
|
|
# ThreadingMixIn.daemon_threads indicates how threads will behave on an
|
|
|
|
# abrupt shutdown; like quitting the server by the user or restarting
|
|
|
|
# by the auto-reloader. True means the server will not wait for thread
|
|
|
|
# termination before it quits. This will make auto-reloader faster
|
|
|
|
# and will prevent the need to kill the server manually if a thread
|
|
|
|
# isn't terminating correctly.
|
|
|
|
httpd.daemon_threads = True
|
2005-07-18 23:25:58 +08:00
|
|
|
httpd.set_app(wsgi_handler)
|
|
|
|
httpd.serve_forever()
|