https://ruslanspivak.com/lsbaws-part1/
https://ruslanspivak.com/lsbaws-part2/
Before a HTTP request can be sent, a TCP connection must be established with the webserver. Then over HTTP a request can be sent an over TCP and a response is awaited (Hello, World! in this case).
- Start webserver on port 8888
- Create telnet session
telnet localhost 8888 - Make HTTP request
GET /hello HTTP/1.1
HTTP Request:
+-------------------+----------------+-------------------+
| HTTP Method | Path | HTTP Version |
+-------------------+----------------+-------------------+
| GET | /hello | HTTP/1.1 |
+-------------------+----------------+-------------------+
HTTP Response:
+-------------------+----------------+-------------------+
| HTTP Version | Status Code | Status Message |
+-------------------+----------------+-------------------+
| HTTP/1.1 | 200 | OK |
+-------------------+----------------+-------------------+
| Response Body |
+--------------------------------------------------------+
| Hello, World! |
+--------------------------------------------------------+
WSGI allows to use any webframework with any webserver. It works like this:
- The framework provides an ‘application’ callable (The WSGI specification doesn’t prescribe how that should be implemented)
- The server invokes the ‘application’ callable for each request it receives from an HTTP client. It passes a dictionary ‘environ’ containing WSGI/CGI variables and a ‘start_response’ callable as arguments to the ‘application’ callable.
- The framework/application generates an HTTP status and HTTP response headers and passes them to the ‘start_response’ callable for the server to store them. The framework/application also returns a response body.
- The server combines the status, the response headers, and the response body into an HTTP response and transmits it to the client (This step is not part of the specification but it’s the next logical step in the flow and I added it for clarity)
This is what an implementation of a framework would need to do:
def run_application(application):
"""Server code."""
# This is where an application/framework stores
# an HTTP status and HTTP response headers for the server
# to transmit to the client
headers_set = []
# Environment dictionary with WSGI/CGI variables
environ = {}
def start_response(status, response_headers, exc_info=None):
headers_set[:] = [status, response_headers]
# Server invokes the ‘application' callable and gets back the
# response body
result = application(environ, start_response)
# Server builds an HTTP response and transmits it to the client
...
def app(environ, start_response):
"""A barebones WSGI app."""
start_response("200 OK", [("Content-Type", "text/plain")])
return [b"Hello world!"]
run_application(app)HTTP response (e.g., pyramid app):
+-------------------+----------------+-------------------+
| HTTP Version | Status Code | Status Message |
+-------------------+----------------+-------------------+
| HTTP/1.1 | 200 | OK |
+-------------------+----------------+-------------------+
| Header Name | Header Value |
+-------------------+------------------------------------+
| Content-Type | ... |
| Content-Length | ... |
| Date | ... |
| Server | ... |
+-------------------+------------------------------------+
| Response Body |
+--------------------------------------------------------+
| Hello world from Pyramid! |
+--------------------------------------------------------+
The environ dictionary is a Python dictionary containing several WSGI and CGI variables prescribed by the WSGI specification. A Web framework uses the information from that dictionary to decide which view to use based on the specified route, request method etc., where to read the request body from and where to write errors, if any.
Recap of flow:
- First, the server starts and loads an ‘application’ callable provided by your Web framework/application
- Then, the server reads a request
- Then, the server parses it
- Then, it builds an ‘environ’ dictionary using the request data
- Then, it calls the ‘application’ callable with the ‘environ’ dictionary and a ‘start_response’ callable as parameters and gets back a response body.
- Then, the server constructs an HTTP response using the data returned by the call to the ‘application’ object and the status and response headers set by the ‘start_response’ callable.
- And finally, the server transmits the HTTP response back to the client