mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	[3.1.x] Fixed #31594 -- Added ASGIStaticFilesHandler.get_response_async().
Backport of 92309e53d9 from master
			
			
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						parent
						
							1ac45e619d
						
					
				
				
					commit
					3fb69756ea
				
			| @@ -1,6 +1,8 @@ | |||||||
| from urllib.parse import urlparse | from urllib.parse import urlparse | ||||||
| from urllib.request import url2pathname | from urllib.request import url2pathname | ||||||
|  |  | ||||||
|  | from asgiref.sync import sync_to_async | ||||||
|  |  | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.contrib.staticfiles import utils | from django.contrib.staticfiles import utils | ||||||
| from django.contrib.staticfiles.views import serve | from django.contrib.staticfiles.views import serve | ||||||
| @@ -52,6 +54,12 @@ class StaticFilesHandlerMixin: | |||||||
|         except Http404 as e: |         except Http404 as e: | ||||||
|             return response_for_exception(request, e) |             return response_for_exception(request, e) | ||||||
|  |  | ||||||
|  |     async def get_response_async(self, request): | ||||||
|  |         try: | ||||||
|  |             return await sync_to_async(self.serve)(request) | ||||||
|  |         except Http404 as e: | ||||||
|  |             return await sync_to_async(response_for_exception)(request, e) | ||||||
|  |  | ||||||
|  |  | ||||||
| class StaticFilesHandler(StaticFilesHandlerMixin, WSGIHandler): | class StaticFilesHandler(StaticFilesHandlerMixin, WSGIHandler): | ||||||
|     """ |     """ | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								tests/asgi/project/static/file.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/asgi/project/static/file.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | test | ||||||
| @@ -1,18 +1,25 @@ | |||||||
| import asyncio | import asyncio | ||||||
| import sys | import sys | ||||||
| import threading | import threading | ||||||
|  | from pathlib import Path | ||||||
| from unittest import skipIf | from unittest import skipIf | ||||||
|  |  | ||||||
| from asgiref.sync import SyncToAsync | from asgiref.sync import SyncToAsync | ||||||
| from asgiref.testing import ApplicationCommunicator | from asgiref.testing import ApplicationCommunicator | ||||||
|  |  | ||||||
|  | from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler | ||||||
| from django.core.asgi import get_asgi_application | from django.core.asgi import get_asgi_application | ||||||
| from django.core.signals import request_finished, request_started | from django.core.signals import request_finished, request_started | ||||||
| from django.db import close_old_connections | from django.db import close_old_connections | ||||||
| from django.test import AsyncRequestFactory, SimpleTestCase, override_settings | from django.test import ( | ||||||
|  |     AsyncRequestFactory, SimpleTestCase, modify_settings, override_settings, | ||||||
|  | ) | ||||||
|  | from django.utils.http import http_date | ||||||
|  |  | ||||||
| from .urls import test_filename | from .urls import test_filename | ||||||
|  |  | ||||||
|  | TEST_STATIC_ROOT = Path(__file__).parent / 'project' / 'static' | ||||||
|  |  | ||||||
|  |  | ||||||
| @skipIf(sys.platform == 'win32' and (3, 8, 0) < sys.version_info < (3, 8, 1), 'https://bugs.python.org/issue38563') | @skipIf(sys.platform == 'win32' and (3, 8, 0) < sys.version_info < (3, 8, 1), 'https://bugs.python.org/issue38563') | ||||||
| @override_settings(ROOT_URLCONF='asgi.urls') | @override_settings(ROOT_URLCONF='asgi.urls') | ||||||
| @@ -79,6 +86,45 @@ class ASGITest(SimpleTestCase): | |||||||
|         # Allow response.close() to finish. |         # Allow response.close() to finish. | ||||||
|         await communicator.wait() |         await communicator.wait() | ||||||
|  |  | ||||||
|  |     @modify_settings(INSTALLED_APPS={'append': 'django.contrib.staticfiles'}) | ||||||
|  |     @override_settings( | ||||||
|  |         STATIC_URL='/static/', | ||||||
|  |         STATIC_ROOT=TEST_STATIC_ROOT, | ||||||
|  |         STATICFILES_DIRS=[TEST_STATIC_ROOT], | ||||||
|  |         STATICFILES_FINDERS=[ | ||||||
|  |             'django.contrib.staticfiles.finders.FileSystemFinder', | ||||||
|  |         ], | ||||||
|  |     ) | ||||||
|  |     async def test_static_file_response(self): | ||||||
|  |         application = ASGIStaticFilesHandler(get_asgi_application()) | ||||||
|  |         # Construct HTTP request. | ||||||
|  |         scope = self.async_request_factory._base_scope(path='/static/file.txt') | ||||||
|  |         communicator = ApplicationCommunicator(application, scope) | ||||||
|  |         await communicator.send_input({'type': 'http.request'}) | ||||||
|  |         # Get the file content. | ||||||
|  |         file_path = TEST_STATIC_ROOT / 'file.txt' | ||||||
|  |         with open(file_path, 'rb') as test_file: | ||||||
|  |             test_file_contents = test_file.read() | ||||||
|  |         # Read the response. | ||||||
|  |         stat = file_path.stat() | ||||||
|  |         response_start = await communicator.receive_output() | ||||||
|  |         self.assertEqual(response_start['type'], 'http.response.start') | ||||||
|  |         self.assertEqual(response_start['status'], 200) | ||||||
|  |         self.assertEqual( | ||||||
|  |             set(response_start['headers']), | ||||||
|  |             { | ||||||
|  |                 (b'Content-Length', str(len(test_file_contents)).encode('ascii')), | ||||||
|  |                 (b'Content-Type', b'text/plain'), | ||||||
|  |                 (b'Content-Disposition', b'inline; filename="file.txt"'), | ||||||
|  |                 (b'Last-Modified', http_date(stat.st_mtime).encode('ascii')), | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |         response_body = await communicator.receive_output() | ||||||
|  |         self.assertEqual(response_body['type'], 'http.response.body') | ||||||
|  |         self.assertEqual(response_body['body'], test_file_contents) | ||||||
|  |         # Allow response.close() to finish. | ||||||
|  |         await communicator.wait() | ||||||
|  |  | ||||||
|     async def test_headers(self): |     async def test_headers(self): | ||||||
|         application = get_asgi_application() |         application = get_asgi_application() | ||||||
|         communicator = ApplicationCommunicator( |         communicator = ApplicationCommunicator( | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								tests/staticfiles_tests/test_handlers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tests/staticfiles_tests/test_handlers.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler | ||||||
|  | from django.core.handlers.asgi import ASGIHandler | ||||||
|  | from django.test import AsyncRequestFactory | ||||||
|  |  | ||||||
|  | from .cases import StaticFilesTestCase | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestASGIStaticFilesHandler(StaticFilesTestCase): | ||||||
|  |     async_request_factory = AsyncRequestFactory() | ||||||
|  |  | ||||||
|  |     async def test_get_async_response(self): | ||||||
|  |         request = self.async_request_factory.get('/static/test/file.txt') | ||||||
|  |         handler = ASGIStaticFilesHandler(ASGIHandler()) | ||||||
|  |         response = await handler.get_response_async(request) | ||||||
|  |         response.close() | ||||||
|  |         self.assertEqual(response.status_code, 200) | ||||||
|  |  | ||||||
|  |     async def test_get_async_response_not_found(self): | ||||||
|  |         request = self.async_request_factory.get('/static/test/not-found.txt') | ||||||
|  |         handler = ASGIStaticFilesHandler(ASGIHandler()) | ||||||
|  |         response = await handler.get_response_async(request) | ||||||
|  |         self.assertEqual(response.status_code, 404) | ||||||
		Reference in New Issue
	
	Block a user