An Adventure Story

The Request-Response Cycle in the
Nginx/Gunicon/WSGI/Werkzeug/Flask

Stack

ngw2f

About Me

The Stack

  • Nginx - proxy / web server
  • Gunicorn - web server
  • WSGI - server - app interface
  • Werkzeug - WSGI wrapper
  • Flask - app framework

HTTP

Request

Response

1991 - HTTP 0.9

Hypertext Transfer Protocol -- HTTP/0.9 (http://www.w3.org/Protocols/HTTP/AsImplemented.html)

May 1996 - HTTP 1.0
Hypertext Transfer Protocol -- HTTP/1.0 (http://tools.ietf.org/html/rfc1945)

June 1999 - HTTP 1.1
Hypertext Transfer Protocol -- HTTP/1.1 (http://tools.ietf.org/html/rfc2616)

June 2014 - HTTP 1.1, you know, for clarity
Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing (http://tools.ietf.org/html/rfc7230)
Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content (http://tools.ietf.org/html/rfc7231)
Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests (http://tools.ietf.org/html/rfc7232)
Hypertext Transfer Protocol (HTTP/1.1): Range Requests (http://tools.ietf.org/html/rfc7233)
Hypertext Transfer Protocol (HTTP/1.1): Caching (http://tools.ietf.org/html/rfc7234)
Hypertext Transfer Protocol (HTTP/1.1): Authentication (http://tools.ietf.org/html/rfc7235)

May 2015 - HTTP 2.0, SPDY made standard
Hypertext Transfer Protocol Version 2 (HTTP/2) (http://tools.ietf.org/html/rfc7540)

The Request

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /path/to/resource HTTP/1.0

The Response

HTTP/1.1 200 OK
Server: nginx/1.8.0
Date: Tue, 02 Jun 2015 22:42:54 GMT
Content-Type: text/html
Content-Length: 12
Last-Modified: Tue, 02 Jun 2015 22:37:02 GMT
Connection: close
ETag: "556e300e-c"
Accept-Ranges: bytes

Hello World
Connection closed by foreign host.

Nginx

Nginx Overview

  • Basic web server
  • Web proxy
  • SMTP/POP3/IMAP proxy
  • Load balancing

Nginx Architecture

Setup

Ubuntu Install

$ sudo apt-get install nginx

Mac Install

$ brew install nginx

Windows Install

$ lol

Config

worker_processes  1;

events {
    worker_connections  1024;
}


http {
    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    include mime.types;
    default_type application/octet-stream;

    ##
    # Logging Settings
    ##

    access_log /Users/crempp/projects/ngw2f/nginx/logs/access.log;
    error_log /Users/crempp/projects/ngw2f/nginx/logs/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

    server {
        listen 8080 default_server;
        listen [::]:8080 default_server ipv6only=on;

        root /Users/crempp/projects/ngw2f/nginx/html;
        index index.html index.htm;

        # Make site accessible from http://localhost/
        server_name localhost;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ /index.html;
                # Uncomment to enable naxsi on this location
                # include /etc/nginx/naxsi.rules
        }
    }
}
include servers/*;

Demo

Gunicorn

Gunicorn Overview

  • WSGI compliant
  • Multiple workers
  • Worker management
  • Sync, Async, Tornado, Python3 Async

Gunicorn Architecture

Gunicorn Hooks

Gunicorn Sample App

def app(environ, start_response):
    """Simplest possible WSGI application object"""
    data = 'Hello, World! (from Gunicorn)\n'
    status = '200 OK'

    response_headers = [
        ('Content-type','text/plain'),
        ('Content-Length', str(len(data)))
    ]

    start_response(status, response_headers)

    return iter([data])

Setup

$ virtualenv env
$ source env/bin/activate
(env)$ pip install gunicorn

Run (standalone)

$ gunicorn --workers=2 app:app

Run (through nginx)

$ gunicorn --workers=2 app:app

Demo

WSGI

WSGI Specification

PEP 333 / PEP 3333

  • Must provide environ, a dictionary of environment variables

  • Must provide start_response(status, headers), a callable accepting two positional arguments

WSGI Server Requirements

WSGI App Requirements

  • Must provide app(environ, start_response), a callable accepting two positional argument

  • Must call start_response(status, headers) before any content is yielded
  • Any data returned must be iterable

WSGI Server Dispatch

iterable = application(environ, start_response)
for data in iterable:
   # send data to client

WSGI Application

def application(environ, start_response):

    status = '200 OK'
    response_headers = []

    # Tell server about the response
    start_response(status, response_headers)
    
    # Return body content
    return ['Body content']

Demo

WSGI Middleware

def middleware(environ, start_response):

    # Do stuff to environ

    # Choose application to handle request

    response_iter = application(environ, start_response)
    
    # Do stuff to response

    return response_iter

Werkzeug / Flask

Werkzeug

  • WSGI helpers
  • URL routing
  • HTTP utilities
  • Data Structures
  • Middleware
  • A development server
  • Lots of other utilities

Werkzeug Example

from werkzeug.wrappers import Request, Response

def application(environ, start_response):
    request = Request(environ)
    text = 'Hello %s!' % request.args.get('name', 'World')
    response = Response(text, mimetype='text/plain')
    return response(environ, start_response)
from werkzeug.wrappers import Request, Response

@Request.application
def application(request):
    result = ['Hello World']
    return Response(result, mimetype='text/html')

or

Demo

Flask Overview

  • Routing
  • Blueprints
  • Jinja template support
  • Class based views
  • Session handling
  • Signals
  • Secure cookies
  • Integrated unit tests
  • 100% WSGI 1.0 compliant
  • Development server
  • a few more helpful utilities

Flask Application

from flask import Flask
app = Flask(__name__)
 
@app.route("/")
def hello():
    return "Hello World (from Flask)"
 
if __name__ == "__main__":
    app.run()

Demo

Questions

Thank You!

If any of this interests you visit

https://www.truecar.com/hiring.html

References

  1. RFC Hypertext Transfer Protocol -- HTTP/1.0
    http://tools.ietf.org/html/rfc1945
  2. Nginx Documentation​
    http://nginx.org/en/docs/
  3. The Architecture of Open Source Applications Volume II, Chapter 14 Nginx
    http://www.aosabook.org/en/nginx.html
  4. Gunicorn Documentation
    http://gunicorn-docs.readthedocs.org/en/latest/index.html
  5. PEP 0333 - Python Web Server Gateway Interface v1.0
    https://www.python.org/dev/peps/pep-0333/
  6. Werkzeug Documentation
    http://werkzeug.readthedocs.org/en/latest/