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.request import url2pathname | ||||
|  | ||||
| from asgiref.sync import sync_to_async | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.contrib.staticfiles import utils | ||||
| from django.contrib.staticfiles.views import serve | ||||
| @@ -52,6 +54,12 @@ class StaticFilesHandlerMixin: | ||||
|         except Http404 as 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): | ||||
|     """ | ||||
|   | ||||
							
								
								
									
										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 sys | ||||
| import threading | ||||
| from pathlib import Path | ||||
| from unittest import skipIf | ||||
|  | ||||
| from asgiref.sync import SyncToAsync | ||||
| from asgiref.testing import ApplicationCommunicator | ||||
|  | ||||
| from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler | ||||
| from django.core.asgi import get_asgi_application | ||||
| from django.core.signals import request_finished, request_started | ||||
| 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 | ||||
|  | ||||
| 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') | ||||
| @override_settings(ROOT_URLCONF='asgi.urls') | ||||
| @@ -79,6 +86,45 @@ class ASGITest(SimpleTestCase): | ||||
|         # Allow response.close() to finish. | ||||
|         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): | ||||
|         application = get_asgi_application() | ||||
|         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