Web Server

A simple web server might only serve static files. The client makes an HTTP request to a URL handled by the server, say

http://foo.com/todo/list.html

and the website maps the path of the URL, todo/list.html, to a file on the file system, say

/home/ubuntu/foo_server/static/todo/list.html

The file is then read by the server and the contents of the file are returned to the client as the body of the HTTP response.

CGI

An early way to create a dynamic web server was to use the CGI protocol. The client makes an HTTP request to a URL handled by the server, say

http://foo.com/todo

and the website maps the path of the URL to an executable on the file system, say

/home/ubuntu/foo_server/cgi/todo.py

The executable is called the CGI script though in general it could be an executable binary.

The server runs the CGI script and returns its standard output to the client as the body of the response. In the case of a POST request, the CGI script reads the form data via standard input. The server sets environment variables to communicate metadata about the request to the CGI script. Two of the most important are:

  • REQUEST_METHOD
  • REQUEST_URI

For example, the CGI script might inspect REQUEST_METHOD to see if the request is a GET request, and if so it might parse the URL in REQUEST_URI to get URL parameters.

uwsgi (protocol)

The CGI protocol requires launching a new process for each HTTP request. Faster methods for handling dynamic HTTP requests have been developed.

The uwsgi protocol, which is distinct from the uWSGI server described below, is one such replacement method. The web server communicates via a binary protocol over TCP with a uwsgi server. The uwsgi server acts like the CGI script and generates the content to be returned to the client as the body of the HTTP request.

Nginx

Nginx is a webserver which can serve static content and knows the uwsgi protocol.

When I ran the following on my Ubuntu server

$ sudo apt-get install nginx

an executable was installed at /usr/sbin/nginx. It looks like nginx is installed as a service and will already be running when the installation finished. The service can be stopped or started with:

$ systemctl nginx start|stop

Show how to...

  • run another nginx server
  • set the port
  • serve static content
  • talk to a uwsgi server

uWSGI (server)

When you run

$ pip install uwsgi

a command line executable called uwsgi is installed on your system. If I run

$ uwsgi --help

it lists over 900 command line options. Here are some of the more useful options:

Usage: /Users/clark/ve/bin/uwsgi [options...]
    -s|--socket                             bind to the specified UNIX/TCP socket using default protocol
    -s|--uwsgi-socket                       bind to the specified UNIX/TCP socket using uwsgi protocol
    --http-socket                           bind to the specified UNIX/TCP socket using HTTP protocol
    --http                                  add an http router/server on the specified address
    --httprouter                            add an http router/server on the specified address
    -p|--processes                          spawn the specified number of workers/processes
    -p|--workers                            spawn the specified number of workers/processes
    --threads                               run each worker in prethreaded mode with the specified number of threads
    -M|--master                             enable master process
    --wsgi-file                             load .wsgi file
    -w|--module                             load a WSGI module
    -w|--wsgi                               load a WSGI module
    --file                                  load .wsgi file
    --stats                                 enable the stats server on the specified address
    --stats-server                          enable the stats server on the specified address
    --chdir                                 chdir to specified directory before apps loading
    --pythonpath                            add directory (or glob) to pythonpath
    --python-path                           add directory (or glob) to pythonpath
    --pp                                    add directory (or glob) to pythonpath
    --env                                   set environment variable
    --callable                              set default WSGI callable name
    -R|--max-requests                       reload workers after the specified amount of managed requests
    -t|--harakiri                           set harakiri timeout
    --logger                                set/append a logger
    --vacuum                                try to remove all of the generated file/sockets
    --post-buffering                        set size in bytes after which will buffer to disk instead of memory
    --post-buffering-bufsize                set buffer size for read() in post buffering mode

One can use a uwsgi.ini file instead of command line arguments. Thus, instead of running uswgi this way:

$ uwsgi --wsgi-file application.py --http-socket 0.0.0.0:9190 --master --processes 2

You could run it this way:

$ cat uwsgi.ini
[uwsgi]
http-socket = 0.0.0.0:9190
processes = 2
master = true
wsgi-file = application.py

$ usgi uwsgi.ini

The uWSGI server is implemented in C, but it designed for running application code in other languages, such as Python. uWSGI must be recompiled to change the application languages which are supported.

If Python is supported, the application developer implements a Python function called application. When uWSGI is run, the --wsgi-file --flag is used to specify the file containing the application function:

$ cat hello.py
def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"]

$ ./ve/bin/uwsgi --http 127.0.0.1:9090  --wsgi-file hello.py

The example uses the --http --flag to run the uWSGI server as a stand-alone web server without another web server in front of it.

The example also shows how the uWSGI server interfaces with the Python code. It passes two arguments to the application function, the second of which is a callback function which the Python code uses to specify the status code and the response headers. The body of the response is an array of byte strings. The env is a dictionary: https://www.python.org/dev/peps/pep-0333/#environ-variables

uWSGI runs with an optional master process and 1 or more worker processes. The worker processes listen on a common socket with the SO_REUSEADDR flag so each request is assigned to one of them by the operating system.

The master process is optional, but is usually used in production. You can read the code to get an idea of what services it provides: https://github.com/unbit/uwsgi/blob/master/core/master.c

Flask

Flask is a Python package for implementing web application. A Flask application can act as a uWSGI application.

When setting up a Flask application, one imports the Flask class and instantiates an object. This object presumably has a call method which allows it to be called as the application function describes above.

Thus, to use Flask with uWSGI, one just needs to put something like this in the --wsgi-file--:

import flask

application = Flask(__name__, template_folder='layout')

However, it is customary to put the Flask object in a variable named app since it makes declaring routes less verbose. One could assign the Flask object to both app and application, or one could use the uWSGI --callable --option to change the name of the variable containing the uWSGI application.