mirror of
				https://github.com/django/daphne.git
				synced 2025-10-29 06:47:32 +03:00 
			
		
		
		
	Updated to use ASGI v3 applications internally. (#275)
Used guarantee_single_callable(). Removed unneeded --asgi-protocol CLI option. Updated tests. Co-authored-by: Carlton Gibson <carlton.gibson@noumenal.es>
This commit is contained in:
		
							parent
							
								
									e1b77e930b
								
							
						
					
					
						commit
						15ba5c6776
					
				|  | @ -1,10 +1,9 @@ | |||
| import argparse | ||||
| import functools | ||||
| import logging | ||||
| import sys | ||||
| from argparse import ArgumentError, Namespace | ||||
| 
 | ||||
| from asgiref.compatibility import is_double_callable | ||||
| from asgiref.compatibility import guarantee_single_callable | ||||
| 
 | ||||
| from .access import AccessLogGenerator | ||||
| from .endpoints import build_endpoint_description_strings | ||||
|  | @ -17,19 +16,6 @@ DEFAULT_HOST = "127.0.0.1" | |||
| DEFAULT_PORT = 8000 | ||||
| 
 | ||||
| 
 | ||||
| class ASGI3Middleware: | ||||
|     def __init__(self, app): | ||||
|         self.app = app | ||||
| 
 | ||||
|     def __call__(self, scope): | ||||
|         scope.setdefault("asgi", {}) | ||||
|         scope["asgi"]["version"] = "3.0" | ||||
|         return functools.partial(self.asgi, scope=scope) | ||||
| 
 | ||||
|     async def asgi(self, receive, send, scope): | ||||
|         await self.app(scope, receive, send) | ||||
| 
 | ||||
| 
 | ||||
| class CommandLineInterface(object): | ||||
|     """ | ||||
|     Acts as the main CLI entry point for running the server. | ||||
|  | @ -129,13 +115,6 @@ class CommandLineInterface(object): | |||
|             help="The WebSocket protocols you wish to support", | ||||
|             default=None, | ||||
|         ) | ||||
|         self.parser.add_argument( | ||||
|             "--asgi-protocol", | ||||
|             dest="asgi_protocol", | ||||
|             help="The version of the ASGI protocol to use", | ||||
|             default="auto", | ||||
|             choices=["asgi2", "asgi3", "auto"], | ||||
|         ) | ||||
|         self.parser.add_argument( | ||||
|             "--root-path", | ||||
|             dest="root_path", | ||||
|  | @ -247,16 +226,11 @@ class CommandLineInterface(object): | |||
|                 access_log_stream = open(args.access_log, "a", 1) | ||||
|         elif args.verbosity >= 1: | ||||
|             access_log_stream = sys.stdout | ||||
| 
 | ||||
|         # Import application | ||||
|         sys.path.insert(0, ".") | ||||
|         application = import_by_path(args.application) | ||||
| 
 | ||||
|         asgi_protocol = args.asgi_protocol | ||||
|         if asgi_protocol == "auto": | ||||
|             asgi_protocol = "asgi2" if is_double_callable(application) else "asgi3" | ||||
| 
 | ||||
|         if asgi_protocol == "asgi3": | ||||
|             application = ASGI3Middleware(application) | ||||
|         application = guarantee_single_callable(application) | ||||
| 
 | ||||
|         # Set up port/host bindings | ||||
|         if not any( | ||||
|  |  | |||
|  | @ -199,15 +199,17 @@ class Server(object): | |||
|         assert "application_instance" not in self.connections[protocol] | ||||
|         # Make an instance of the application | ||||
|         input_queue = asyncio.Queue() | ||||
|         application_instance = self.application(scope=scope) | ||||
|         scope.setdefault("asgi", {"version": "3.0"}) | ||||
|         application_instance = self.application( | ||||
|             scope=scope, | ||||
|             receive=input_queue.get, | ||||
|             send=lambda message: self.handle_reply(protocol, message), | ||||
|         ) | ||||
|         # Run it, and stash the future for later checking | ||||
|         if protocol not in self.connections: | ||||
|             return None | ||||
|         self.connections[protocol]["application_instance"] = asyncio.ensure_future( | ||||
|             application_instance( | ||||
|                 receive=input_queue.get, | ||||
|                 send=lambda message: self.handle_reply(protocol, message), | ||||
|             ), | ||||
|             application_instance, | ||||
|             loop=asyncio.get_event_loop(), | ||||
|         ) | ||||
|         return input_queue | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ import pickle | |||
| import tempfile | ||||
| import traceback | ||||
| from concurrent.futures import CancelledError | ||||
| from functools import partial | ||||
| 
 | ||||
| 
 | ||||
| class DaphneTestingInstance: | ||||
|  | @ -43,7 +42,7 @@ class DaphneTestingInstance: | |||
|         # Start up process | ||||
|         self.process = DaphneProcess( | ||||
|             host=self.host, | ||||
|             application=partial(TestApplication, lock=self.lock), | ||||
|             application=TestApplication(lock=self.lock), | ||||
|             kwargs=kwargs, | ||||
|             setup=self.process_setup, | ||||
|             teardown=self.process_teardown, | ||||
|  | @ -173,12 +172,12 @@ class TestApplication: | |||
|     setup_storage = os.path.join(tempfile.gettempdir(), "setup.testio") | ||||
|     result_storage = os.path.join(tempfile.gettempdir(), "result.testio") | ||||
| 
 | ||||
|     def __init__(self, scope, lock): | ||||
|         self.scope = scope | ||||
|     def __init__(self, lock): | ||||
|         self.lock = lock | ||||
|         self.messages = [] | ||||
| 
 | ||||
|     async def __call__(self, send, receive): | ||||
|     async def __call__(self, scope, receive, send): | ||||
|         self.scope = scope | ||||
|         # Receive input and send output | ||||
|         logging.debug("test app coroutine alive") | ||||
|         try: | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ class TestHTTPRequest(DaphneTestCase): | |||
|         # Check overall keys | ||||
|         self.assert_key_sets( | ||||
|             required_keys={ | ||||
|                 "asgi", | ||||
|                 "type", | ||||
|                 "http_version", | ||||
|                 "method", | ||||
|  | @ -35,6 +36,7 @@ class TestHTTPRequest(DaphneTestCase): | |||
|             optional_keys={"scheme", "root_path", "client", "server"}, | ||||
|             actual_keys=scope.keys(), | ||||
|         ) | ||||
|         self.assertEqual(scope["asgi"]["version"], "3.0") | ||||
|         # Check that it is the right type | ||||
|         self.assertEqual(scope["type"], "http") | ||||
|         # Method (uppercased unicode string) | ||||
|  | @ -120,10 +122,7 @@ class TestHTTPRequest(DaphneTestCase): | |||
|         self.assert_valid_http_scope(scope, "GET", request_path, params=request_params) | ||||
|         self.assert_valid_http_request_message(messages[0], body=b"") | ||||
| 
 | ||||
|     @given( | ||||
|         request_path=http_strategies.http_path(), | ||||
|         chunk_size=integers(min_value=1), | ||||
|     ) | ||||
|     @given(request_path=http_strategies.http_path(), chunk_size=integers(min_value=1)) | ||||
|     @settings(max_examples=5, deadline=5000) | ||||
|     def test_request_body_chunking(self, request_path, chunk_size): | ||||
|         """ | ||||
|  |  | |||
|  | @ -23,10 +23,18 @@ class TestWebsocket(DaphneTestCase): | |||
|         """ | ||||
|         # Check overall keys | ||||
|         self.assert_key_sets( | ||||
|             required_keys={"type", "path", "raw_path", "query_string", "headers"}, | ||||
|             required_keys={ | ||||
|                 "asgi", | ||||
|                 "type", | ||||
|                 "path", | ||||
|                 "raw_path", | ||||
|                 "query_string", | ||||
|                 "headers", | ||||
|             }, | ||||
|             optional_keys={"scheme", "root_path", "client", "server", "subprotocols"}, | ||||
|             actual_keys=scope.keys(), | ||||
|         ) | ||||
|         self.assertEqual(scope["asgi"]["version"], "3.0") | ||||
|         # Check that it is the right type | ||||
|         self.assertEqual(scope["type"], "websocket") | ||||
|         # Path | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user