Skip to content

Instantly share code, notes, and snippets.

@amcgregor
Last active August 12, 2019 19:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amcgregor/1aed57a512ed9856cfc8470a14be429c to your computer and use it in GitHub Desktop.
Save amcgregor/1aed57a512ed9856cfc8470a14be429c to your computer and use it in GitHub Desktop.
A very, very brief explanation of the WSGI request/response flow.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment