basehttp.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. """
  2. HTTP server that implements the Python WSGI protocol (PEP 333, rev 1.21).
  3. Based on wsgiref.simple_server which is part of the standard library since 2.5.
  4. This is a simple server for use in testing or debugging Django apps. It hasn't
  5. been reviewed for security issues. DON'T USE IT FOR PRODUCTION USE!
  6. """
  7. from __future__ import unicode_literals
  8. from io import BytesIO
  9. import socket
  10. import sys
  11. import traceback
  12. from wsgiref import simple_server
  13. from wsgiref.util import FileWrapper # NOQA: for backwards compatibility
  14. from django.core.exceptions import ImproperlyConfigured
  15. from django.core.management.color import color_style
  16. from django.core.wsgi import get_wsgi_application
  17. from django.utils import six
  18. from django.utils.module_loading import import_string
  19. from django.utils.six.moves import socketserver
  20. __all__ = ('WSGIServer', 'WSGIRequestHandler', 'MAX_SOCKET_CHUNK_SIZE')
  21. # If data is too large, socket will choke, so write chunks no larger than 32MB
  22. # at a time. The rationale behind the 32MB can be found on Django's Trac:
  23. # https://code.djangoproject.com/ticket/5596#comment:4
  24. MAX_SOCKET_CHUNK_SIZE = 32 * 1024 * 1024 # 32 MB
  25. def get_internal_wsgi_application():
  26. """
  27. Loads and returns the WSGI application as configured by the user in
  28. ``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,
  29. this will be the ``application`` object in ``projectname/wsgi.py``.
  30. This function, and the ``WSGI_APPLICATION`` setting itself, are only useful
  31. for Django's internal servers (runserver, runfcgi); external WSGI servers
  32. should just be configured to point to the correct application object
  33. directly.
  34. If settings.WSGI_APPLICATION is not set (is ``None``), we just return
  35. whatever ``django.core.wsgi.get_wsgi_application`` returns.
  36. """
  37. from django.conf import settings
  38. app_path = getattr(settings, 'WSGI_APPLICATION')
  39. if app_path is None:
  40. return get_wsgi_application()
  41. try:
  42. return import_string(app_path)
  43. except ImportError as e:
  44. msg = (
  45. "WSGI application '%(app_path)s' could not be loaded; "
  46. "Error importing module: '%(exception)s'" % ({
  47. 'app_path': app_path,
  48. 'exception': e,
  49. })
  50. )
  51. six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
  52. sys.exc_info()[2])
  53. class ServerHandler(simple_server.ServerHandler, object):
  54. error_status = str("500 INTERNAL SERVER ERROR")
  55. def write(self, data):
  56. """'write()' callable as specified by PEP 3333"""
  57. assert isinstance(data, bytes), "write() argument must be bytestring"
  58. if not self.status:
  59. raise AssertionError("write() before start_response()")
  60. elif not self.headers_sent:
  61. # Before the first output, send the stored headers
  62. self.bytes_sent = len(data) # make sure we know content-length
  63. self.send_headers()
  64. else:
  65. self.bytes_sent += len(data)
  66. # XXX check Content-Length and truncate if too many bytes written?
  67. data = BytesIO(data)
  68. for chunk in iter(lambda: data.read(MAX_SOCKET_CHUNK_SIZE), b''):
  69. self._write(chunk)
  70. self._flush()
  71. def error_output(self, environ, start_response):
  72. super(ServerHandler, self).error_output(environ, start_response)
  73. return ['\n'.join(traceback.format_exception(*sys.exc_info()))]
  74. # Backport of http://hg.python.org/cpython/rev/d5af1b235dab. See #16241.
  75. # This can be removed when support for Python <= 2.7.3 is deprecated.
  76. def finish_response(self):
  77. try:
  78. if not self.result_is_file() or not self.sendfile():
  79. for data in self.result:
  80. self.write(data)
  81. self.finish_content()
  82. finally:
  83. self.close()
  84. class WSGIServer(simple_server.WSGIServer, object):
  85. """BaseHTTPServer that implements the Python WSGI protocol"""
  86. request_queue_size = 10
  87. def __init__(self, *args, **kwargs):
  88. if kwargs.pop('ipv6', False):
  89. self.address_family = socket.AF_INET6
  90. super(WSGIServer, self).__init__(*args, **kwargs)
  91. def server_bind(self):
  92. """Override server_bind to store the server name."""
  93. super(WSGIServer, self).server_bind()
  94. self.setup_environ()
  95. class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
  96. def __init__(self, *args, **kwargs):
  97. self.style = color_style()
  98. super(WSGIRequestHandler, self).__init__(*args, **kwargs)
  99. def address_string(self):
  100. # Short-circuit parent method to not call socket.getfqdn
  101. return self.client_address[0]
  102. def log_message(self, format, *args):
  103. msg = "[%s] %s\n" % (self.log_date_time_string(), format % args)
  104. # Utilize terminal colors, if available
  105. if args[1][0] == '2':
  106. # Put 2XX first, since it should be the common case
  107. msg = self.style.HTTP_SUCCESS(msg)
  108. elif args[1][0] == '1':
  109. msg = self.style.HTTP_INFO(msg)
  110. elif args[1] == '304':
  111. msg = self.style.HTTP_NOT_MODIFIED(msg)
  112. elif args[1][0] == '3':
  113. msg = self.style.HTTP_REDIRECT(msg)
  114. elif args[1] == '404':
  115. msg = self.style.HTTP_NOT_FOUND(msg)
  116. elif args[1][0] == '4':
  117. msg = self.style.HTTP_BAD_REQUEST(msg)
  118. else:
  119. # Any 5XX, or any other response
  120. msg = self.style.HTTP_SERVER_ERROR(msg)
  121. sys.stderr.write(msg)
  122. def run(addr, port, wsgi_handler, ipv6=False, threading=False):
  123. server_address = (addr, port)
  124. if threading:
  125. httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})
  126. else:
  127. httpd_cls = WSGIServer
  128. httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
  129. httpd.set_app(wsgi_handler)
  130. httpd.serve_forever()