User's browser formualtes the following request:
GET / HTTP/1.1
Host: www.example.com
The request is parsed by something (FELB like Nginx, etc.) and passed through to the application process in some manner, e.g. HTTP reverse proxy, FastCGI binary socket, uWSGI, etc. Upon hitting the WSGI application, the Python side host reformulates the request:
def application(environ, start_response):
start_response(b'200 OK', [(b'Content-Type', b'text/plain')])
return [b'Hello, World\n']
The first argument, environ
, contains the "CGI" environment context, populated according to the standard. In our example, environ
would literally equal:
{
'REQUEST_METHOD': "GET",
'SCRIPT_NAME': '',
'PATH_INFO': '/',
'QUERY_STRING'
'CONTENT_TYPE': ""
'CONTENT_LENGTH': ""
'SERVER_NAME': "",
'SERVER_PORT': "8080"
'SERVER_PROTOCOL': "HTTP/1.1"
'HTTP_HOST': "www.example.com"
}
The function is invoked, and reaches this line:
start_response(b'200 OK', [(b'Content-Type', b'text/plain')])
Whereupon immediately the HTTP response line is sent, followed by the headers serialized into the correct form for HTTP. It then waits until the WSGI application callable returns, and consumes that while delivering each element of the iterable in its own sock.write()
call, typically.
Our example returns a list. Any "iterable" object will do, and we can leverage the built-in "generator" approach to save some space:
def application(environ, start_response):
start_response(b'200 OK', [(b'Content-Type', b'text/plain')])
yield b'Hello, World\n'
This works because calling a generator function doesn't actually execute it; it executes up to the first yield and returns the instantiated "generator" object, which on each iteration runs up to the next yield, and the value passed back to the iterator is the value of that yield.
This would result in two packets being sent:
HTTP/1.1 200 OK
Date: Mon, 23 May 2005 22:38:34 GMT
Content-Type: text/plain
And:
Hello, World