handlers.py 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. from urllib.parse import urlparse
  2. from urllib.request import url2pathname
  3. from django.conf import settings
  4. from django.contrib.staticfiles import utils
  5. from django.contrib.staticfiles.views import serve
  6. from django.core.handlers.asgi import ASGIHandler
  7. from django.core.handlers.exception import response_for_exception
  8. from django.core.handlers.wsgi import WSGIHandler, get_path_info
  9. from django.http import Http404
  10. class StaticFilesHandlerMixin:
  11. """
  12. Common methods used by WSGI and ASGI handlers.
  13. """
  14. # May be used to differentiate between handler types (e.g. in a
  15. # request_finished signal)
  16. handles_files = True
  17. def load_middleware(self):
  18. # Middleware are already loaded for self.application; no need to reload
  19. # them for self.
  20. pass
  21. def get_base_url(self):
  22. utils.check_settings()
  23. return settings.STATIC_URL
  24. def _should_handle(self, path):
  25. """
  26. Check if the path should be handled. Ignore the path if:
  27. * the host is provided as part of the base_url
  28. * the request's path isn't under the media path (or equal)
  29. """
  30. return path.startswith(self.base_url[2]) and not self.base_url[1]
  31. def file_path(self, url):
  32. """
  33. Return the relative path to the media file on disk for the given URL.
  34. """
  35. relative_url = url[len(self.base_url[2]):]
  36. return url2pathname(relative_url)
  37. def serve(self, request):
  38. """Serve the request path."""
  39. return serve(request, self.file_path(request.path), insecure=True)
  40. def get_response(self, request):
  41. try:
  42. return self.serve(request)
  43. except Http404 as e:
  44. return response_for_exception(request, e)
  45. class StaticFilesHandler(StaticFilesHandlerMixin, WSGIHandler):
  46. """
  47. WSGI middleware that intercepts calls to the static files directory, as
  48. defined by the STATIC_URL setting, and serves those files.
  49. """
  50. def __init__(self, application):
  51. self.application = application
  52. self.base_url = urlparse(self.get_base_url())
  53. super().__init__()
  54. def __call__(self, environ, start_response):
  55. if not self._should_handle(get_path_info(environ)):
  56. return self.application(environ, start_response)
  57. return super().__call__(environ, start_response)
  58. class ASGIStaticFilesHandler(StaticFilesHandlerMixin, ASGIHandler):
  59. """
  60. ASGI application which wraps another and intercepts requests for static
  61. files, passing them off to Django's static file serving.
  62. """
  63. def __init__(self, application):
  64. self.application = application
  65. self.base_url = urlparse(self.get_base_url())
  66. async def __call__(self, scope, receive, send):
  67. # Only even look at HTTP requests
  68. if scope['type'] == 'http' and self._should_handle(scope['path']):
  69. # Serve static content
  70. # (the one thing super() doesn't do is __call__, apparently)
  71. return await super().__call__(scope, receive, send)
  72. # Hand off to the main app
  73. return await self.application(scope, receive, send)