You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

basehttp.py 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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. import logging
  8. import socket
  9. import socketserver
  10. import sys
  11. from wsgiref import simple_server
  12. from django.core.exceptions import ImproperlyConfigured
  13. from django.core.handlers.wsgi import LimitedStream
  14. from django.core.wsgi import get_wsgi_application
  15. from django.utils.module_loading import import_string
  16. __all__ = ('WSGIServer', 'WSGIRequestHandler')
  17. logger = logging.getLogger('django.server')
  18. def get_internal_wsgi_application():
  19. """
  20. Load and return the WSGI application as configured by the user in
  21. ``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,
  22. this will be the ``application`` object in ``projectname/wsgi.py``.
  23. This function, and the ``WSGI_APPLICATION`` setting itself, are only useful
  24. for Django's internal server (runserver); external WSGI servers should just
  25. be configured to point to the correct application object directly.
  26. If settings.WSGI_APPLICATION is not set (is ``None``), return
  27. whatever ``django.core.wsgi.get_wsgi_application`` returns.
  28. """
  29. from django.conf import settings
  30. app_path = getattr(settings, 'WSGI_APPLICATION')
  31. if app_path is None:
  32. return get_wsgi_application()
  33. try:
  34. return import_string(app_path)
  35. except ImportError as err:
  36. raise ImproperlyConfigured(
  37. "WSGI application '%s' could not be loaded; "
  38. "Error importing module." % app_path
  39. ) from err
  40. def is_broken_pipe_error():
  41. exc_type, exc_value = sys.exc_info()[:2]
  42. return issubclass(exc_type, socket.error) and exc_value.args[0] == 32
  43. class WSGIServer(simple_server.WSGIServer):
  44. """BaseHTTPServer that implements the Python WSGI protocol"""
  45. request_queue_size = 10
  46. def __init__(self, *args, ipv6=False, allow_reuse_address=True, **kwargs):
  47. if ipv6:
  48. self.address_family = socket.AF_INET6
  49. self.allow_reuse_address = allow_reuse_address
  50. super().__init__(*args, **kwargs)
  51. def handle_error(self, request, client_address):
  52. if is_broken_pipe_error():
  53. logger.info("- Broken pipe from %s\n", client_address)
  54. else:
  55. super().handle_error(request, client_address)
  56. class ThreadedWSGIServer(socketserver.ThreadingMixIn, WSGIServer):
  57. """A threaded version of the WSGIServer"""
  58. daemon_threads = True
  59. class ServerHandler(simple_server.ServerHandler):
  60. http_version = '1.1'
  61. def __init__(self, stdin, stdout, stderr, environ, **kwargs):
  62. """
  63. Use a LimitedStream so that unread request data will be ignored at
  64. the end of the request. WSGIRequest uses a LimitedStream but it
  65. shouldn't discard the data since the upstream servers usually do this.
  66. This fix applies only for testserver/runserver.
  67. """
  68. try:
  69. content_length = int(environ.get('CONTENT_LENGTH'))
  70. except (ValueError, TypeError):
  71. content_length = 0
  72. super().__init__(LimitedStream(stdin, content_length), stdout, stderr, environ, **kwargs)
  73. def cleanup_headers(self):
  74. super().cleanup_headers()
  75. # HTTP/1.1 requires support for persistent connections. Send 'close' if
  76. # the content length is unknown to prevent clients from reusing the
  77. # connection.
  78. if 'Content-Length' not in self.headers:
  79. self.headers['Connection'] = 'close'
  80. # Mark the connection for closing if it's set as such above or if the
  81. # application sent the header.
  82. if self.headers.get('Connection') == 'close':
  83. self.request_handler.close_connection = True
  84. def close(self):
  85. self.get_stdin()._read_limited()
  86. super().close()
  87. def handle_error(self):
  88. # Ignore broken pipe errors, otherwise pass on
  89. if not is_broken_pipe_error():
  90. super().handle_error()
  91. class WSGIRequestHandler(simple_server.WSGIRequestHandler):
  92. protocol_version = 'HTTP/1.1'
  93. def address_string(self):
  94. # Short-circuit parent method to not call socket.getfqdn
  95. return self.client_address[0]
  96. def log_message(self, format, *args):
  97. extra = {
  98. 'request': self.request,
  99. 'server_time': self.log_date_time_string(),
  100. }
  101. if args[1][0] == '4':
  102. # 0x16 = Handshake, 0x03 = SSL 3.0 or TLS 1.x
  103. if args[0].startswith('\x16\x03'):
  104. extra['status_code'] = 500
  105. logger.error(
  106. "You're accessing the development server over HTTPS, but "
  107. "it only supports HTTP.\n", extra=extra,
  108. )
  109. return
  110. if args[1].isdigit() and len(args[1]) == 3:
  111. status_code = int(args[1])
  112. extra['status_code'] = status_code
  113. if status_code >= 500:
  114. level = logger.error
  115. elif status_code >= 400:
  116. level = logger.warning
  117. else:
  118. level = logger.info
  119. else:
  120. level = logger.info
  121. level(format, *args, extra=extra)
  122. def get_environ(self):
  123. # Strip all headers with underscores in the name before constructing
  124. # the WSGI environ. This prevents header-spoofing based on ambiguity
  125. # between underscores and dashes both normalized to underscores in WSGI
  126. # env vars. Nginx and Apache 2.4+ both do this as well.
  127. for k in self.headers:
  128. if '_' in k:
  129. del self.headers[k]
  130. return super().get_environ()
  131. def handle(self):
  132. self.close_connection = True
  133. self.handle_one_request()
  134. while not self.close_connection:
  135. self.handle_one_request()
  136. try:
  137. self.connection.shutdown(socket.SHUT_WR)
  138. except (socket.error, AttributeError):
  139. pass
  140. def handle_one_request(self):
  141. """Copy of WSGIRequestHandler.handle() but with different ServerHandler"""
  142. self.raw_requestline = self.rfile.readline(65537)
  143. if len(self.raw_requestline) > 65536:
  144. self.requestline = ''
  145. self.request_version = ''
  146. self.command = ''
  147. self.send_error(414)
  148. return
  149. if not self.parse_request(): # An error code has been sent, just exit
  150. return
  151. handler = ServerHandler(
  152. self.rfile, self.wfile, self.get_stderr(), self.get_environ()
  153. )
  154. handler.request_handler = self # backpointer for logging & connection closing
  155. handler.run(self.server.get_app())
  156. def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
  157. server_address = (addr, port)
  158. if threading:
  159. httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
  160. else:
  161. httpd_cls = server_cls
  162. httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
  163. if threading:
  164. # ThreadingMixIn.daemon_threads indicates how threads will behave on an
  165. # abrupt shutdown; like quitting the server by the user or restarting
  166. # by the auto-reloader. True means the server will not wait for thread
  167. # termination before it quits. This will make auto-reloader faster
  168. # and will prevent the need to kill the server manually if a thread
  169. # isn't terminating correctly.
  170. httpd.daemon_threads = True
  171. httpd.set_app(wsgi_handler)
  172. httpd.serve_forever()