Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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.

runserver.py 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import datetime
  2. import importlib
  3. import logging
  4. import sys
  5. from django.apps import apps
  6. from django.conf import settings
  7. from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler
  8. from django.core.exceptions import ImproperlyConfigured
  9. from django.core.management import CommandError
  10. from django.core.management.commands.runserver import Command as RunserverCommand
  11. from daphne import __version__
  12. from daphne.endpoints import build_endpoint_description_strings
  13. from daphne.server import Server
  14. logger = logging.getLogger("django.channels.server")
  15. def get_default_application():
  16. """
  17. Gets the default application, set in the ASGI_APPLICATION setting.
  18. """
  19. try:
  20. path, name = settings.ASGI_APPLICATION.rsplit(".", 1)
  21. except (ValueError, AttributeError):
  22. raise ImproperlyConfigured("Cannot find ASGI_APPLICATION setting.")
  23. try:
  24. module = importlib.import_module(path)
  25. except ImportError:
  26. raise ImproperlyConfigured("Cannot import ASGI_APPLICATION module %r" % path)
  27. try:
  28. value = getattr(module, name)
  29. except AttributeError:
  30. raise ImproperlyConfigured(
  31. f"Cannot find {name!r} in ASGI_APPLICATION module {path}"
  32. )
  33. return value
  34. class Command(RunserverCommand):
  35. protocol = "http"
  36. server_cls = Server
  37. def add_arguments(self, parser):
  38. super().add_arguments(parser)
  39. parser.add_argument(
  40. "--noasgi",
  41. action="store_false",
  42. dest="use_asgi",
  43. default=True,
  44. help="Run the old WSGI-based runserver rather than the ASGI-based one",
  45. )
  46. parser.add_argument(
  47. "--http_timeout",
  48. action="store",
  49. dest="http_timeout",
  50. type=int,
  51. default=None,
  52. help=(
  53. "Specify the daphne http_timeout interval in seconds "
  54. "(default: no timeout)"
  55. ),
  56. )
  57. parser.add_argument(
  58. "--websocket_handshake_timeout",
  59. action="store",
  60. dest="websocket_handshake_timeout",
  61. type=int,
  62. default=5,
  63. help=(
  64. "Specify the daphne websocket_handshake_timeout interval in "
  65. "seconds (default: 5)"
  66. ),
  67. )
  68. def handle(self, *args, **options):
  69. self.http_timeout = options.get("http_timeout", None)
  70. self.websocket_handshake_timeout = options.get("websocket_handshake_timeout", 5)
  71. # Check Channels is installed right
  72. if options["use_asgi"] and not hasattr(settings, "ASGI_APPLICATION"):
  73. raise CommandError(
  74. "You have not set ASGI_APPLICATION, which is needed to run the server."
  75. )
  76. # Dispatch upward
  77. super().handle(*args, **options)
  78. def inner_run(self, *args, **options):
  79. # Maybe they want the wsgi one?
  80. if not options.get("use_asgi", True):
  81. if hasattr(RunserverCommand, "server_cls"):
  82. self.server_cls = RunserverCommand.server_cls
  83. return RunserverCommand.inner_run(self, *args, **options)
  84. # Run checks
  85. self.stdout.write("Performing system checks...\n\n")
  86. self.check(display_num_errors=True)
  87. self.check_migrations()
  88. # Print helpful text
  89. quit_command = "CTRL-BREAK" if sys.platform == "win32" else "CONTROL-C"
  90. now = datetime.datetime.now().strftime("%B %d, %Y - %X")
  91. self.stdout.write(now)
  92. self.stdout.write(
  93. (
  94. "Django version %(version)s, using settings %(settings)r\n"
  95. "Starting ASGI/Daphne version %(daphne_version)s development server"
  96. " at %(protocol)s://%(addr)s:%(port)s/\n"
  97. "Quit the server with %(quit_command)s.\n"
  98. )
  99. % {
  100. "version": self.get_version(),
  101. "daphne_version": __version__,
  102. "settings": settings.SETTINGS_MODULE,
  103. "protocol": self.protocol,
  104. "addr": "[%s]" % self.addr if self._raw_ipv6 else self.addr,
  105. "port": self.port,
  106. "quit_command": quit_command,
  107. }
  108. )
  109. # Launch server in 'main' thread. Signals are disabled as it's still
  110. # actually a subthread under the autoreloader.
  111. logger.debug("Daphne running, listening on %s:%s", self.addr, self.port)
  112. # build the endpoint description string from host/port options
  113. endpoints = build_endpoint_description_strings(host=self.addr, port=self.port)
  114. try:
  115. self.server_cls(
  116. application=self.get_application(options),
  117. endpoints=endpoints,
  118. signal_handlers=not options["use_reloader"],
  119. action_logger=self.log_action,
  120. http_timeout=self.http_timeout,
  121. root_path=getattr(settings, "FORCE_SCRIPT_NAME", "") or "",
  122. websocket_handshake_timeout=self.websocket_handshake_timeout,
  123. ).run()
  124. logger.debug("Daphne exited")
  125. except KeyboardInterrupt:
  126. shutdown_message = options.get("shutdown_message", "")
  127. if shutdown_message:
  128. self.stdout.write(shutdown_message)
  129. return
  130. def get_application(self, options):
  131. """
  132. Returns the static files serving application wrapping the default application,
  133. if static files should be served. Otherwise just returns the default
  134. handler.
  135. """
  136. staticfiles_installed = apps.is_installed("django.contrib.staticfiles")
  137. use_static_handler = options.get("use_static_handler", staticfiles_installed)
  138. insecure_serving = options.get("insecure_serving", False)
  139. if use_static_handler and (settings.DEBUG or insecure_serving):
  140. return ASGIStaticFilesHandler(get_default_application())
  141. else:
  142. return get_default_application()
  143. def log_action(self, protocol, action, details):
  144. """
  145. Logs various different kinds of requests to the console.
  146. """
  147. # HTTP requests
  148. if protocol == "http" and action == "complete":
  149. msg = "HTTP %(method)s %(path)s %(status)s [%(time_taken).2f, %(client)s]"
  150. # Utilize terminal colors, if available
  151. if 200 <= details["status"] < 300:
  152. # Put 2XX first, since it should be the common case
  153. logger.info(self.style.HTTP_SUCCESS(msg), details)
  154. elif 100 <= details["status"] < 200:
  155. logger.info(self.style.HTTP_INFO(msg), details)
  156. elif details["status"] == 304:
  157. logger.info(self.style.HTTP_NOT_MODIFIED(msg), details)
  158. elif 300 <= details["status"] < 400:
  159. logger.info(self.style.HTTP_REDIRECT(msg), details)
  160. elif details["status"] == 404:
  161. logger.warning(self.style.HTTP_NOT_FOUND(msg), details)
  162. elif 400 <= details["status"] < 500:
  163. logger.warning(self.style.HTTP_BAD_REQUEST(msg), details)
  164. else:
  165. # Any 5XX, or any other response
  166. logger.error(self.style.HTTP_SERVER_ERROR(msg), details)
  167. # Websocket requests
  168. elif protocol == "websocket" and action == "connected":
  169. logger.info("WebSocket CONNECT %(path)s [%(client)s]", details)
  170. elif protocol == "websocket" and action == "disconnected":
  171. logger.info("WebSocket DISCONNECT %(path)s [%(client)s]", details)
  172. elif protocol == "websocket" and action == "connecting":
  173. logger.info("WebSocket HANDSHAKING %(path)s [%(client)s]", details)
  174. elif protocol == "websocket" and action == "rejected":
  175. logger.info("WebSocket REJECT %(path)s [%(client)s]", details)