if data is too large socket will choke so write chunks no larger than 32mb at a time the rationale behind the 32mb can be found in 5596comment 4 write callable as specified by pep 3333 assert isinstancedata bytes write argument must be bytestring if not self status raise assertionerrorwrite before startresponse elif not self headerssent before the first output send the stored headers self bytessent lendata make sure we know contentlength self sendheaders else self bytessent lendata xxx check contentlength and truncate if too many bytes written data bytesiodata for chunk in iterlambda data readmaxsocketchunksize b self writechunk self flush def erroroutputself environ startresponse super erroroutputenviron startresponse return n jointraceback formatexceptionsys excinfo class dummyhandler def logrequestself args kwargs pass class filewrapperhandlerserverhandler def initself args kwargs super initargs kwargs self requesthandler dummyhandler self usedsendfile false def sendfileself self usedsendfile true return true def wsgiappenviron startresponse startresponse200 ok contenttype textplain return bhello world def wsgiappfilewrapperenviron startresponse startresponse200 ok contenttype textplain return environwsgi filewrapperbytesiobfoo class wsgifilewrapperteststestcase def testfilewrapperusessendfileself env serverprotocol http1 0 handler filewrapperhandlerbytesio bytesio bytesio env handler runwsgiappfilewrapper self asserttruehandler usedsendfile self assertequalhandler stdout getvalue b self assertequalhandler stderr getvalue b def testfilewrappernosendfileself env serverprotocol http1 0 handler filewrapperhandlerbytesio bytesio bytesio env handler runwsgiapp self assertfalsehandler usedsendfile self assertequalhandler stdout getvalue splitlines1 bhello world self assertequalhandler stderr getvalue b overridesettingsrooturlconfbuiltinserver urls def testfileresponseclosingself env requestfactory getfileresponse environ handler filewrapperhandlerbytesio bytesio bytesio env handler rungetinternalwsgiapplication sendfile is used only when filewrapper has been used self asserttruehandler usedsendfile fetch the original response object self assertinresponse fileresponseholder response fileresponseholderresponse the response and file buffers are closed self assertisresponse closed true buf1 buf2 fileresponseholderbuffers self assertisbuf1 closed true self assertisbuf2 closed true fileresponseholder clear overridesettingsrooturlconfbuiltinserver urls def testfileresponsecallrequestfinishedself env requestfactory getfileresponse environ handler filewrapperhandlerbytesio bytesio bytesio env with mock magicmock as signalhandler requestfinished connectsignalhandler handler rungetinternalwsgiapplication self assertequalsignalhandler callcount 1 class writechunkcounterhandlerserverhandler def initself args kwargs super initargs kwargs self requesthandler dummyhandler self headerswritten false self writechunkcounter 0 def sendheadersself super sendheaders self headerswritten true def writeself data if self headerswritten self writechunkcounter 1 self stdout writedata def sendbigdataappenviron startresponse startresponse200 ok contenttype textplain return a blob of data that is 1 5 times the maximum chunk size return bx maxsocketchunksize maxsocketchunksize 2 class serverhandlerchunksproperlytestcase def testchunkeddataself env serverprotocol http1 0 handler writechunkcounterhandlernone bytesio bytesio env handler runsendbigdataapp self assertequalhandler writechunkcounter 2 if data is too large socket will choke so write chunks no larger than 32mb at a time the rationale behind the 32mb can be found in 5596 comment 4 32 mb write callable as specified by pep 3333 before the first output send the stored headers make sure we know content length xxx check content length and truncate if too many bytes written the wsgi file_wrapper works for the builtin server tests for 9659 wsgi file_wrapper in the builtin server we need to mock a couple of handlers and keep track of what gets called when using a couple kinds of wsgi apps view returning a fileresponse properly closes the file and http response when file_wrapper is used sendfile is used only when file_wrapper has been used fetch the original response object the response and file buffers are closed server handler that counts the number of chunks written after headers were sent used to make sure large response body chunking works properly return a blob of data that is 1 5 times the maximum chunk size the serverhandler chunks data properly tests for 18972 the logic that performs the math to break data into 32mb max_socket_chunk_size chunks was flawed but it didn t actually cause any problems
import sys
import traceback
from io import BytesIO
from unittest import TestCase, mock
from wsgiref import simple_server

from django.core.servers.basehttp import get_internal_wsgi_application
from django.core.signals import request_finished
from django.test import RequestFactory, override_settings

from .views import FILE_RESPONSE_HOLDER



MAX_SOCKET_CHUNK_SIZE = 32 * 1024 * 1024  


class ServerHandler(simple_server.ServerHandler):
    error_status = "500 INTERNAL SERVER ERROR"

    def write(self, data):
        

        assert isinstance(data, bytes), "write() argument must be bytestring"

        if not self.status:
            raise AssertionError("write() before start_response()")

        elif not self.headers_sent:
            
            self.bytes_sent = len(data)  
            self.send_headers()
        else:
            self.bytes_sent += len(data)

        
        data = BytesIO(data)
        for chunk in iter(lambda: data.read(MAX_SOCKET_CHUNK_SIZE), b""):
            self._write(chunk)
            self._flush()

    def error_output(self, environ, start_response):
        super().error_output(environ, start_response)
        return ["\n".join(traceback.format_exception(*sys.exc_info()))]


class DummyHandler:
    def log_request(self, *args, **kwargs):
        pass


class FileWrapperHandler(ServerHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request_handler = DummyHandler()
        self._used_sendfile = False

    def sendfile(self):
        self._used_sendfile = True
        return True


def wsgi_app(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")])
    return [b"Hello World!"]


def wsgi_app_file_wrapper(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")])
    return environ["wsgi.file_wrapper"](BytesIO(b"foo"))


class WSGIFileWrapperTests(TestCase):
    

    def test_file_wrapper_uses_sendfile(self):
        env = {"SERVER_PROTOCOL": "HTTP/1.0"}
        handler = FileWrapperHandler(BytesIO(), BytesIO(), BytesIO(), env)
        handler.run(wsgi_app_file_wrapper)
        self.assertTrue(handler._used_sendfile)
        self.assertEqual(handler.stdout.getvalue(), b"")
        self.assertEqual(handler.stderr.getvalue(), b"")

    def test_file_wrapper_no_sendfile(self):
        env = {"SERVER_PROTOCOL": "HTTP/1.0"}
        handler = FileWrapperHandler(BytesIO(), BytesIO(), BytesIO(), env)
        handler.run(wsgi_app)
        self.assertFalse(handler._used_sendfile)
        self.assertEqual(handler.stdout.getvalue().splitlines()[-1], b"Hello World!")
        self.assertEqual(handler.stderr.getvalue(), b"")

    @override_settings(ROOT_URLCONF="builtin_server.urls")
    def test_file_response_closing(self):
        
        env = RequestFactory().get("/fileresponse/").environ
        handler = FileWrapperHandler(BytesIO(), BytesIO(), BytesIO(), env)
        handler.run(get_internal_wsgi_application())
        
        self.assertTrue(handler._used_sendfile)
        
        self.assertIn("response", FILE_RESPONSE_HOLDER)
        response = FILE_RESPONSE_HOLDER["response"]
        
        self.assertIs(response.closed, True)
        buf1, buf2 = FILE_RESPONSE_HOLDER["buffers"]
        self.assertIs(buf1.closed, True)
        self.assertIs(buf2.closed, True)
        FILE_RESPONSE_HOLDER.clear()

    @override_settings(ROOT_URLCONF="builtin_server.urls")
    def test_file_response_call_request_finished(self):
        env = RequestFactory().get("/fileresponse/").environ
        handler = FileWrapperHandler(BytesIO(), BytesIO(), BytesIO(), env)
        with mock.MagicMock() as signal_handler:
            request_finished.connect(signal_handler)
            handler.run(get_internal_wsgi_application())
            self.assertEqual(signal_handler.call_count, 1)


class WriteChunkCounterHandler(ServerHandler):
    

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request_handler = DummyHandler()
        self.headers_written = False
        self.write_chunk_counter = 0

    def send_headers(self):
        super().send_headers()
        self.headers_written = True

    def _write(self, data):
        if self.headers_written:
            self.write_chunk_counter += 1
        self.stdout.write(data)


def send_big_data_app(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")])
    
    return [b"x" * (MAX_SOCKET_CHUNK_SIZE + MAX_SOCKET_CHUNK_SIZE // 2)]


class ServerHandlerChunksProperly(TestCase):
    

    def test_chunked_data(self):
        env = {"SERVER_PROTOCOL": "HTTP/1.0"}
        handler = WriteChunkCounterHandler(None, BytesIO(), BytesIO(), env)
        handler.run(send_big_data_app)
        self.assertEqual(handler.write_chunk_counter, 2)