Python Handlers

Python file handlers are Python files which the server executes in response to requests made to the corresponding URL. This is hooked up to a route like ("*", "*.py", python_file_handler), meaning that any .py file will be treated as a handler file (note that this makes it easy to write unsafe handlers, particularly when running the server in a web-exposed setting).

The Python files must define a function named main with the signature:

main(request, response)

…where request is a wptserve Request object and response is a wptserve Response object.

This function must return a value in one of the following four formats:

((status_code, reason), headers, content)
(status_code, headers, content)
(headers, content)
content

Above, headers is a list of (field name, value) pairs, and content is a string or an iterable returning strings.

The main function may also update the response manually. For example, one may use response.headers.set to set a response header, and only return the content. One may even use this kind of handler, but manipulate the output socket directly. The writer property of the response exposes a ResponseWriter object that allows writing specific parts of the request or direct access to the underlying socket. If used, the return value of the main function and the properties of the response object will be ignored.

The wptserver implements a number of Python APIs for controlling traffic.

Python3 compatibility

Even though Python3 is not fully supported at this point, some work is being done to add compatibility for it. This is why you can see in multiple places the use of the six python module which is meant to provide a set of simple utilities that work for both generation of python (see docs). The module is vendored in tools/third_party/six/six.py.

When an handler is added, it should be at least syntax-compatible with Python3. You can check that by running:

python3 -m py_compile <path/to/handler.py>

Example: Dynamic HTTP headers

The following code defines a Python handler that allows the requester to control the value of the Content-Type HTTP response header:

def main(request, response):
    content_type = request.GET.first('content-type')
    headers = [('Content-Type', content_type)]

    return (200, 'my status text'), headers, 'my response content'

If saved to a file named resources/control-content-type.py, the WPT server will respond to requests for resources/control-content-type.py by executing that code.

This could be used from a testharness.js test like so:

<!DOCTYPE html>
<meta charset="utf-8">
<title>Demonstrating the WPT server's Python handler feature</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
promise_test(function() {
  return fetch('resources/control-content-type.py?content-type=text/foobar')
    .then(function(response) {
      assert_equals(response.status, 200);
      assert_equals(response.statusText, 'my status text');
      assert_equals(response.headers.get('Content-Type'), 'text/foobar');
    });
});
</script>