Chapter 1 · A request, end to end

1.6 The response returns to the server

What an HttpResponse is, what each of the seven default middleware does to it on the way out, how WSGIHandler.__call__ hands it to the server, and who ends the request.

The view returned an HttpResponse. From here the request is over in all but name, and the response travels back out through the middleware in the reverse of the order they were entered, three of them adding headers, until the handler gives it to the server. The server sends the body and then closes the response, and closing it is what ends the request.

                        HttpResponse.__init__(content)
                          response["Content-Type"] = "text/html; charset=utf-8"
                    BaseHandler.check_response
                  XFrameOptionsMiddleware.process_response  (status 200)
                    response["X-Frame-Options"] = "DENY"
                MessageMiddleware.process_response  (status 200)
                  BaseStorage.update
            CsrfViewMiddleware.process_response  (status 200)
          CommonMiddleware.process_response  (status 200)
            response["Content-Length"] = "148"
        SessionMiddleware.process_response  (status 200)
          SessionBase.is_empty
      SecurityMiddleware.process_response  (status 200)
        response["X-Content-Type-Options"] = "nosniff"
        response["Referrer-Policy"] = "same-origin"
        response["Cross-Origin-Opener-Policy"] = "same-origin"
  start_response("200 OK", [Content-Type, X-Frame-Options, Content-Length, X-Content-Type-Options, Referrer-Policy, Cross-Origin-Opener-Policy])
the server has sent the body: 148 bytes
HttpResponseBase.close
  HttpRequest.close
  signal request_finished -> close_old_connections, close_caches, reset_urlconf
    close_old_connections
      BaseDatabaseWrapper.close_if_unusable_or_obsolete
        DatabaseWrapper.close  (sqlite3)
    close_caches
    reset_urlconf
      set_urlconf(None)

The lines are indented as the recording nests them: the response is made inside the view, inside the innermost layer, and each line further out is one layer nearer the server.

The response object

An HttpResponse is a status, a set of headers, cookies, and a body held in memory. HttpResponseBase.__init__ makes the headers, a ResponseHeaders that keeps each as a pair and looks them up without regard to case, and if no Content-Type was given sets one of "text/html" with the charset of settings.DEFAULT_CHARSET; the status is the class’s status_code, 200, unless one was passed. A header’s name must be ASCII, a value that Latin-1 cannot hold is MIME-encoded, and neither may hold a newline: ResponseHeaders.__setitem__ refuses one that does with BadHeaderError, which is what keeps a value taken from the request from injecting a header.

The body is set by the HttpResponse.content setter. A string is encoded with the response’s charset, bytes are kept, and an iterator is consumed into bytes at once, so that the response can be iterated more than once. What render gave the constructor was the rendered template as a string, and from here on it is bytes. A response that streams is a different class, StreamingHttpResponse, whose body is produced as the server iterates it; The response object and Streaming and file responses have the two.

The way out

Each layer’s process_response is given the response the layer inside it returned, and returns the response the layer outside it will see, which may be a different object. Read in the order they run, which is the reverse of settings.MIDDLEWARE:

XFrameOptionsMiddleware sets X-Frame-Options to the value of settings.X_FRAME_OPTIONS, "DENY" by default, unless the header is already there or the response was marked exempt by the decorator (The security headers: SecurityMiddleware and XFrameOptionsMiddleware).

MessageMiddleware asks the request’s message storage to store what has not been read, into a cookie or the session; here there is nothing to store (Messages: add_message, Message and MessageMiddleware).

AuthenticationMiddleware has no process_response.

CsrfViewMiddleware sets the CSRF cookie only when HttpRequest.META was marked with "CSRF_COOKIE_NEEDS_UPDATE" during the request, which get_token does whenever something asks for a token, a template’s csrf_token tag among them, and which the replacing of a malformed cookie on the way in does too; nothing in this request asked.

CommonMiddleware does two things. To a 404, when settings.APPEND_SLASH is true and the path without its trailing slash would resolve with one added, it answers a permanent redirect to the path with the slash: this is the redirect that makes "/entries/2026" reach "/entries/2026/", and it happens here, on the way out, after the view has not been found. To any response that is not streaming and has no Content-Length, it adds one, the length of the body.

SessionMiddleware reads the session’s two flags and whether it is empty. A session that was modified, or any session when settings.SESSION_SAVE_EVERY_REQUEST is true, is saved and its cookie set, unless it is empty or the response is a server error; a session that was merely accessed makes the response vary on the "Cookie" header; a session that was emptied has its cookie deleted. This request’s session was never touched, so nothing is done (SessionMiddleware: the cookie and the save).

SecurityMiddleware adds the headers that are on by default: X-Content-Type-Options as "nosniff", Referrer-Policy and Cross-Origin-Opener-Policy as "same-origin", each only if the response does not already have it. Strict-Transport-Security is added only to a secure request and only when settings.SECURE_HSTS_SECONDS is set (The security headers: SecurityMiddleware and XFrameOptionsMiddleware).

The outermost layer’s return is what the chain returns. BaseHandler.get_response then registers the request’s own close among the response’s closers, so that closing the response closes any files that were uploaded with the request, and logs the response if its status is 400 or more; a 200 is not logged.

Handing it to the server

Back in WSGIHandler.__call__, the handler stores its own class on the response, for the signal sent later to name as sender, and builds the two things the WSGI interface wants: the status line, from the status code and its standard reason phrase, and the list of headers, which is the response’s headers followed by one Set-Cookie for each cookie. It passes both to the server’s function and returns the response object itself. An HttpResponse is iterable, and iterating it yields its body in one piece. That is all WSGI asks of a body. The recording’s start_response line shows the six headers in the order they were set: the one the constructor set and the five the middleware added.

Who ends the request

The handler has returned, but the request is not over: the server still has to send the body, and when it has, the WSGI specification obliges it to call close on what it was given. HttpResponseBase.close runs each closer registered on the response, the request’s among them, and then sends request_finished. Three receivers are connected in a serving process.

The request has now left no trace in the process but what it was meant to leave: a log line, if any, and whatever the view wrote to the database. The settings, the registry, the handler and its chain, the resolver with its imported URLconf, the template engine with its compiled template, are all as the next request will find them, and the next request is what the recording’s second request shows: the same path, with every first-use step gone.