Routing

wfront.router

wfront.route(mapping, **kw)

Create a routing middleware matching hostname, port and path.

Parameters:
  • mapping – A sequence of mapping tuples, see Mappings
  • default – Optional. Invoked if there are no hits in the map. May either be a WSGI callable or a 2-tuple of (app, filter).
  • host_resolver – a callable that receives an environ and returns a string of 'hostname:port:path'. Defaults to default_resolver().
  • abort404 – a WSGI app that will be run if there are no map hits and no default is provided. Default abort404 is a very simple 404 WSGI application.
  • cleanup_syntax – may be verbose or shortcut. If shortcut, enables shortcut filter specifications. See Built-In Cleanup Filter.
  • **kw – passed through to WFront.__init__().

Returns a WFront WSGI callable:

router = wfront.route([('www.my.host:80', app, None)])

Routing by URL Scheme

wfront.by_scheme(mapping, schemes={'http': (80, ), 'https': (443, )}, **kw)

Create a routing middleware matching hostname, URL scheme and path.

Parameters:
  • mapping – A sequence of mapping tuples, see Mappings
  • schemes – A mapping of scheme names to sequences of port numbers. By default, http on port 80 and https on 443.
  • proxied_host_is_accurate – optional. If False, a Host: header will not be considered if a X-Forwarded-For header is also present. Only required for some non-transparent front-end proxy configurations.
  • default – Optional. Invoked if there are no hits in the map. May either be a WSGI callable or a 2-tuple of (app, filter).
  • abort404 – a WSGI app that will be run if there are no map hits and no default is provided. Default abort404 is a very simple 404 WSGI application.
  • cleanup_syntax – may be verbose or shortcut. If shortcut, enables shortcut filter specifications.
  • **kw – passed through to WFront.__init__().

If you have a server that speaks HTTP on 80, 8000 and 8001, you might specify:

router = wfront.by_scheme([('www.my.host:http', app, None)],
                          schemes={'http': (80, 8000, 8001)})

This would match on a request for www.my.host made to port 80, 8000 or 8001.

Custom Routers

The base implementation can be extended for custom functionality.

class wfront.WFront(mapping, default=None, host_resolver=None, abort404=None, cleanup_syntax='verbose')
__init__(mapping, default=None, host_resolver=None, abort404=None, cleanup_syntax='verbose')

Create a routing middleware that directs to one or more WSGI apps.

Parameters:
  • mapping – a mapping specification.
  • default – Optional. Invoked if there are no hits in the map. May either be a WSGI callable or a 2-tuple of (app, filter).
  • host_resolver – a callable that receives an environ and returns a string of ':hostname:port:path'. Defaults to default_resolver().
  • abort404 – a WSGI app that will be run if there are no map hits and no default is provided. Default is a very simple 404 handler.
  • cleanup_syntax – may be verbose or shortcut. If shortcut, enables shortcut filter specifications.

A simple router that answers any request for localhost:

front = WFront([('localhost', myapp, None)])

A more complex router:

mapping = [ ('www.example.com', myapp),
            ('www.example.com:443', sec_app, 'mymodule.filter'),
            ('wap.example.mobi', myapp, {'mode': 'wap'}), ]

router = WFront(mapping)

By default, the requested host is taken from HTTP_HOST and guaranteed to contain a port number. You can customize this behavior by supplying ‘host_resolver’ as a function that takes the environ and returns a string of ‘hostname:port:path’

add(spec, app, filter=None)

Add a new mapping.

Spec may be a regex in which case it is used as-is, otherwise spec is regex-escaped and converted into a host:port:path matcher.

If a regex is supplied and it contains a ‘pathinfo’ named group, that group will be used in PATH_INFO / SCRIPT_NAME manipulation.

dump_mappings()
Debugging, dump decompiled regex patterns.

Virtual Host Resolution

wfront.default_resolver(environ)
Extracts ‘host:portnumber:path’ from an environ.
wfront.modproxy_resolver(environ)
Extracts ‘host:portnumber:path’ from an environ. This resolver will attempt to use X-Forwarded-Host if present and will ignore any Host: header offered by the server.

In many cases, this function will ‘just work’ and retrieve the correct ‘hostname’ and ‘path’ from the environ with no fuss. If it is not working for your web server and/or WSGI container, the implementation is listed below.

        path = environ.get('REQUEST_URI', None)
        if path is None:
            path = environ.get('SCRIPT_NAME', '') + environ.get('PATH_INFO')

        # mod_proxy-style forward?
        if 'HTTP_X_FORWARDED_FOR' in environ:
            # Some pass it in an x-header
            if 'HTTP_X_FORWARDED_HOST' in environ:
                host = environ['HTTP_X_FORWARDED_HOST']
            # some others pass along the original Host:
            elif self.proxied_host_is_accurate and 'HTTP_HOST' in environ:
                host = environ['HTTP_HOST']
            # Or, default to remote server name if the client didn't
            # supply Host:
            elif 'HTTP_X_FORWARDED_SERVER' in environ:
                host = environ['HTTP_X_FORWARDED_SERVER']
            # Otherwise ignore Host: and default to our server name.
            else:
                host = environ['SERVER_NAME']
            # In a proxy situation, the original request port is only
            # known if there is a Host: or forwarded Host: header AND
            # the request is either on a non-standard port or is http
            # on port 80.  There's no way to distinguish a
            # simply-proxied HTTP and HTTPS connection!  Given that,
            # default to 80.  Ignore wsgi.url_scheme until such a time
            # as the WSGI spec makes it clear if this is variable
            # describes the *user's* URL scheme or the application
            # container's URL scheme.  It's safer to assume the
            # latter.
            if ':' not in host:
                host += ':' + environ.get('HTTP_X_FORWARDED_PORT', '80')
            return host + ':' + path
        # Not mod_proxy-style proxied- assume direct connection.
        else:
            host = environ.get('HTTP_HOST', None)
            if host is None:
                host = environ['SERVER_NAME'] + ':' + environ['SERVER_PORT']
            elif ':' not in host:
                ssl = environ.get('HTTPS', environ['wsgi.url_scheme']).lower()
                if ssl in _ssl_flags:
                    host += ':443'
                else:
                    host += ':80'
            return host + ':' + path

Table Of Contents

Previous topic

Mappings

Next topic

Built-In Cleanup Filter

This Page

Quick search

Enter search terms or a module, class or function name.