Search notes:

WSGI Middleware

Modify response body and add Content-Length header

This is an example of a WSGI middleware that modifies a WSGI application's body and adds the Content-Length response header.
Much of the code is inspired by and copied from this Stackoverflow answer.

textWithUmlauts.py

textWithUmlauts.py represents the application. Because the middleware will set the Content-Length, I have added the three character Ä, Ö and Ü to test if the content length value will be set correctly.
def application(environ, start_response):

    start_response(
         '200 OK',
        [('Content-Type', 'text/plain; charset=utf-8')
    ])

    return ['The total length, in bytes, of this text, '     .encode('utf-8'),
            'when encoded in UTF-8, with the three letters ' .encode('utf-8'),
            'Ä, Ö and Ü is 108'                              .encode('utf-8') ]

middleware.py

middleware.py is called from WSGI and calls textWithUmlauts.application. It then adds additional text to the body and calculates Content-Length:
import textWithUmlauts

class addContentLength:

    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):

        appStatus  = None
        appHeaders = []
        appExcInfo = None

        def start_response_callback(status, headers, exc_info=None):

            nonlocal appStatus, appHeaders, appExcInfo

            appStatus  = status
            appHeaders = headers
            appExcInfo = exc_info

        appBody = self.app(environ, start_response_callback)

        body  = (b''.join(appBody)).decode('utf-8')
        body += '\nThis line was added by the middleware "addContentLength"'  # 57 bytes (+108 = 165)

        bodyBytes = body.encode('utf-8')

      # Remove the Content-Length header if provided by the wrapped application 
        headers = [ (k, v) for k, v in appHeaders if k.lower() != 'content-length' ]

      # Calculate and add Content-Length
        headers.append( ('Content-Length', str(len(bodyBytes)) ) )

        start_response(appStatus, headers, appExcInfo)
        return [bodyBytes]

application = addContentLength(textWithUmlauts.application)

Testing

First, we run the middleware (and by extension the application) with Gunicorn
$ gunicorn middleware
This starts a web server that listens on port 8000.
We now use curl to inspect the returned HTTP response header and body:
$ curl -i localhost:8000
HTTP/1.1 200 OK
Server: gunicorn
Date: Thu, 28 Dec 2023 19:14:12 GMT
Connection: close
Content-Type: text/plain; charset=utf-8
Content-Length: 165

The total length, in bytes, of this text, when encoded in UTF-8, with the three letters Ä, Ö and Ü is 108
This line was added by the middleware "addContentLength"
It turns out that the Content-Length is calculated correctly.

See also

WSGI

Index