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 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 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; 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 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 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:
XFrameOptionsMiddleware sets X-Frame-Options to the value of settings, "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 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 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 is set (The security headers: SecurityMiddleware and XFrameOptionsMiddleware).
The outermost layer’s return is what the chain returns. BaseHandler 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, 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 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.
close_old_connectionsagain, which now has a connection to consider. It closes a connection whose autocommit state was not restored, one on which an error occurred and which no longer works, or one past its age. The age comes from the database’s"CONN_MAX_AGE", and the default is zero: the connection’sclose_atwas set to the moment it was opened, so at the end of every request it is past its age and is closed. A positive age keeps it for the next request on the same thread.close_cachescallscloseon every cache that has been made, which for most backends does nothing.reset_urlconfclears the URLconf set for the thread at the start of the request.
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.