Pages

Friday, 10 January 2020

Python and Web --- (2) Python as WSGI scripts

0 WSGI cheat-sheet

In the last blog,  I demonstrated python as a CGI application. Even CGI is so pervasive, it has drawbacks. Every CGI app has its own process, which means every time the client accesses your python script, the server has to spawn a new process in which a python virtual machine is running. As a result, the performance of CGI applications is not tolerant.  Welcome to WSGI, Web Server Gateway Interface, an interface specially designed for Python. The official document can be found at PEP 3333.

WSGI requires a compatible Web Server App call a python callable object named "application" with special-meaning arguments. The most common format is a function like:

application(environ, start_response)->string

Here, 
  • "environ" is a dictionary that contains everything the Web Server App collects, including what the client puts in via HTTP
  • "start_response" is a function reference for sending HTTP headers.
  • Whatever this function returns will be collected by the Web Server app and sent to the client.
Simply put, CGI uses stdout and environment, while WSGI uses function arguments and function returns, as communication media between Web Server Apps and Web applications.

CGI is for every application written in any language, while WSGI is for Python only.

1 Apache mod_wsgi configuration

Apache supports WSGI through the mod_wsgi module. So first it needs to be installed.

$ yum install mod_wsgi

During the installing process, /etc/httpd/conf.modules.d/10-wsgi.conf was created automatically.

$ cat /etc/httpd/conf.modules.d/10-wsgi.conf
LoadModule wsgi_module modules/mod_wsgi.so

Now, let's configure a WSGI application the simplest way.

$ grep WSGI /etc/httpd/conf/httpd.conf
WSGIScriptAlias /wsgi /var/www/wsgi/app.wsgi

mod_wsgi has a similar syntax to CGI. Instead of "ScriptAlias", mod_wsgi uses "WSGIScriptAlias".

2 An example WSGI application

The following example WSGI app prints out the dictionary Apache provided to it.

$ cat /var/www/wsgi/app.wsgi
def application(environ, start_response):
        response_body = 'I am a WSGI script\n'
        for k,v in environ.items():
                response_body += '%s: %s\n' % (k,v)

        status = '200 OK'
        response_headers = [
                        ('Content-Type', 'text/plain'),
                        ('Content-Length', str(len(response_body)))
                        ]
        start_response(status, response_headers)
        return [response_body]

3 Test from a client

$ curl http://127.0.0.1/wsgi
I am a WSGI script
mod_wsgi.listener_port: 80
CONTEXT_DOCUMENT_ROOT: /var/www/html
SERVER_SOFTWARE: Apache/2.4.6 (CentOS) mod_wsgi/3.4 Python/2.7.5
SCRIPT_NAME: /wsgi
mod_wsgi.enable_sendfile: 0
mod_wsgi.handler_script:
SERVER_SIGNATURE:
REQUEST_METHOD: GET
PATH_INFO:
SERVER_PROTOCOL: HTTP/1.1
QUERY_STRING:
HTTP_USER_AGENT: curl/7.29.0
SERVER_NAME: 127.0.0.1
REMOTE_ADDR: 127.0.0.1
mod_wsgi.queue_start: 1578670748114076
mod_wsgi.request_handler: wsgi-script
wsgi.url_scheme: http
mod_wsgi.callable_object: application
SERVER_PORT: 80
wsgi.multiprocess: True
mod_wsgi.input_chunked: 0
SERVER_ADDR: 127.0.0.1
DOCUMENT_ROOT: /var/www/html
mod_wsgi.process_group:
SCRIPT_FILENAME: /var/www/wsgi/app.wsgi
SERVER_ADMIN: root@localhost
wsgi.input: <mod_wsgi.Input object at 0x7fd1922b2bf0>
HTTP_HOST: 127.0.0.1
CONTEXT_PREFIX:
wsgi.multithread: False
REQUEST_URI: /wsgi
HTTP_ACCEPT: */*
wsgi.version: (1, 0)
GATEWAY_INTERFACE: CGI/1.1
wsgi.run_once: False
wsgi.errors: <mod_wsgi.Log object at 0x7fd1922b2d30>
REMOTE_PORT: 32806
mod_wsgi.listener_host:
REQUEST_SCHEME: http
mod_wsgi.version: (3, 4)
mod_wsgi.application_group: fe80::f2d:cd77:6930:5a08|/wsgi
mod_wsgi.script_reloading: 1
wsgi.file_wrapper: <built-in method file_wrapper of mod_wsgi.Adapter object at 0x7fd1922b8be8>
UNIQUE_ID: XhianDNo9BBQWx5vOyLpegAAAAE

4 "application" can be more

As said before, besides functions, WSGI works for any callable object. e.g.

$ cat app.wsgi
class MyWSGI():
        def __call__(self, environ, start_response):
                start_response('200 OK', [('Content-Type', 'text/plain')])
                return ['Hello World!']

application = MyWSGI()


No comments:

Post a Comment