.. _request_routing: *************** Request Routing *************** Each incoming HTTP request is processed by a configurable routing engine. The engine matches every request against a list of *virtual hosts*. Subsequently the request is matched against a set of *path based rules*. The matching rule determines the actual *action* that is applied to the request. A request not matching any virtual host or rule is denied with ``404 Not Found``. The routing engine is configured via ``routing.yml``. .. note:: The built-in routing engine can be bypassed based on custom inbound filters. .. _routing_virtual_hosts: Virtual Hosts ============= Virtual hosts are referred to as *vhosts* in ``routing.yml``. The file requires a ``vhosts`` key as top-level directive, under which a list of vhosts are declared. A vhost specification consists of the following attributes: :hostAddress: **Mandatory:** Target IP address to match against incoming request. Can be specified explicitly or as a wildcard (*). The hostAddress can be either an IPv4 or IPv6 address. In case of IPv6 address the compressed and uncompressed notation is allowed. :port: **Optional:** Target port to match against incoming request. Can be specified explicitly or as a wildcard (*). :hostNames: **Optional:** One more more host names, matched against the requested host. Can be specified explicitly or as a wildcard (*), can not be specified with IPv6 notation. :brand: **Optional:** A brand identifier that can be applied as :ref:`header ` to forwarded requests in multi-tenant environments. :rules: **Mandatory:** A list of path matching rules. See :ref:`routing_path_matching`. :hostRewrite: **Optional:** A list of optional hostname rewrites. The requests original ``Host`` will be matched against the value in the ``from`` parameter. Wildcards are allowed as prefix. The ``Host`` and ``X-Forwarded-Host`` headers will be replaced with the value in ``to``, ignoring all present values. .. note:: To enable the host rewrite feature, the property has to be set and containing at least one ``from & to`` key/value pair. Only the hostname is considered when trying to match the requests original host against the ``from`` value, not the port or protocol type. Also, wildcards are allowed for subdomains in the ``from`` value. For example: ``*.example.com`` and ``sub.*.example.com``. Example:: vhosts: - hostAddress: 203.0.113.1 port: 80 hostNames: - example.com - example.org hostRewrite: - from: "*.example.com" to: "rewrite.com" brand: example rules: - path: / ... - hostAddress: 1234:1234:0000:0000:0000:0000:3456:3434 port: 80 hostNames: - example.com - example.org hostRewrite: ... - hostAddress: 1235:1235::3457:3476 port: 80 hostNames: - example.com - example.org hostRewrite: ... Host matching ------------- With the exception of the host address, all matching related attributes are optional. The default value is always ``*``. The mechanism of finding the best matching vhost is based on the combination of matching attributes between request and vhost. These are prioritised as follows: ======== ============ ====== ======== Priority Host address Port Hostname ======== ============ ====== ======== 1 match match \- 2 match \* match 3 \* \* \* 4 \* match match 5 \* match \* 6 \* \* match 7 \* \* \* ======== ============ ====== ======== In general hostAddress > port > hostNames > \*. Hostnames are matched against requests ``Host`` or ``X-Forwarded-Host`` headers. If an ``X-Forwarded-Host`` header was preserved according to :ref:`configuration `, its value is taken for matching and ``Host`` is ignored. Example configuration:: vhosts: - hostAddress: 172.20.30.50 port: 9001 hostNames: - www.example.org brand: default ... - hostAddress: 127.0.0.1 port: "*" hostNames: - localhost brand: default ... - hostAddress: "*" port: 80 hostNames: - webmail.example.com - dav.example.com brand: default ... - hostAddress: "*" port: 80 hostNames: - "*" brand: default ... - hostAddress: "*" port: "*" hostNames: - www.example2.org brand: default ... - hostAddress: "*" port: "*" hostNames: - "*" brand: default ... - hostAddress: "*" brand: default ... Path-based rules ---------------- The ``rules`` attribute of a ``vhost`` contains a list of path matching rules and associated proxy actions. The best match for a given request decides what action is applied to this request. A rule specification consists of these attributes: :path: **Mandatory:** A path match expression :action: **Mandatory:** An :ref:`action ` specification :restrictions: **Optional:** List of :ref:`restrictions ` that are applied to matching requests before the action is taken. Example:: - path: / action: type: redirect location: /other .. _routing_path_matching: Path Matching ````````````` In general it is possible to define the path to match in three different ways: - absolute path matching - extended path matching by using wildcards - regex based path matching Each path is associated to a given rule. The mechanism to identify which rule to chose will be outlined at the end of this page (see :ref:`routing_path_best_match`). Absolute matching ````````````````` The easiest path to set is an absolute path. The matching algorithm will match all path below the configured one. Example:: Configuration: /appsuite/api will match: /appsuite/api/ /appsuite/api/mail Extended absolute matching `````````````````````````` In addition to the absolute path matching it is possible to add wildcards to the configured path. ``*`` defines any number of any character while ``?`` defines one of any character. Example:: Configuration: /*/api/v? will match: /appsuite/api/v1/ /ajax/api/v2/ Regex matching `````````````` For anything that cannot be handled with the matching algorithms above you can use the regex format which provides endless configuration options for your path. Regex path definitions have to start with a tilde (~) followed by the regex path. Example 1:: Configuration: ~ /appsuite/api/([^/]+/)?auth will match: /appsuite/api/auth/ /appsuite/api/example.com/auth/sub/ Example 2:: Configuration: ~ /(extra|special)/data will match /extra/data /special/data/2 .. _routing_path_best_match: Best match `````````` The mechanism to find the best matching rule based on the path evaluates the requested path against all configured paths regardless of they are defined absolute, by using wildcards or regex. The most specific path (which means it matches most path elements) will be used. If two or more paths match the same number of path elements the first defined will be used. Example 1:: Configuration: Path 1: / Path 2: ~ ^/api/.*$ Path 3: /api/v?/books Request: /api/v1/books/by-isbn/12345 => Path 2 matches complete path and will be taken Example 2:: Configuration: Path 1: / Path 2: /api/v1 Path 3: /api/v?/books Request: /api/v1/books/by-isbn/12345 => Path 3 matches 3 path elements and will be taken .. _routing_actions: Routing Actions =============== Routing actions take an incoming request and apply all the configured settings and actions. Whether an action is applied or not, depends on the configured condition. It can either be a path match or another conditional match. The conditional match is tested by a dedicated action, the conditional action. Forwarding ---------- The ``forward`` action actually forwards an incoming HTTP request to a an upstream/origin server. Configuration ````````````` .. code-block:: yaml - path: /appsuite/api action: type: forward rewritePath: /ajax shardDetector: httpApi shardBootstrapHandler: httpApi backendPool: appsuite-mw - path: /appsuite action: type: forward shardDetector: httpApi shardBootstrapHandler: httpApi backendPool: appsuite-ui - path: /Microsoft-Server-ActiveSync action: type: forward shardDetector: httpApi shardBootstrapHandler: eas backendPool: appsuite-mw clientConfig: readTimeout: 1900000 :backendPool: **Mandatory:** Define backend pool to forward the request to. Values are taken from ``name`` values found under the global or shards section in backends.yml :rewritePath: **Optional:** Rewrite request path to specified value before forwarding the request to an origin server. :clientConfig: **Optional:** Additional configuration parameters that can be applied to the client that handles the forwarding. For additional information see :ref:`client configuration ` section. :shardDetector: **Optional:** The shard detector which should be used to determine the route for the forward request. Values are taken from ``name`` values found under the detectors section in sharding.yml. For more information please look at the :ref:`sharding ` section. :shardBootstrapHandler: **Optional:** The shard bootstrap handler which should be used to determine the route for the forward request. Values are taken from ``name`` values found under the bootstrapHandlers section in sharding.yml. This setting becomes mandatory if a ``shardDetector`` is set. For more information please look at the :ref:`sharding ` section. Path rewrite ```````````` Rewriting the request URI path is supported for all types of path definitions. In all cases the part that matches the requested path will be rewritten by the given configuration. Example 1:: Configuration: match: /ajax/ rewrite: /servlets/ajax/ Request: /ajax/chronos Rewrite result: /servlets/ajax/chronos/ Example 2:: Configuration: match: ~ /(appsuite/api|ajax)/ rewrite: /servlets/ Request: /appsuite/api/chronos/ Rewrite result: /servlets/chronos/ Example 3:: Configuration: match: ~ /(appsuite/api|ajax)/ rewrite: /servlets/$1/ Request: /appsuite/api/chronos/accounts Rewrite result: /servlet/appsuite/api/chronos/accounts It is possible to reference capture groups of regex matches in the rewrite configuration as well. This has to be done by using ``$`` followed by a 1-based index (``$1``, ``$2``, ``$3``, ...). Redirect -------- The ``redirect`` action redirects any incoming request to the configured location and provides a corresponding status code. If the location is missing, a 500 server error will be returned. It's also possible to specify placeholders in the location like $scheme, $host, $port, $path and $query. Configuration ````````````` .. code-block:: yaml rules: - path: / action: type: redirect status: 302 location: https://$host$path$query :status: **Optional:** The status code of the response (default value 301 Moved Permanently) :location: **Mandatory:** The location where the request should be redirected to. Conditional ----------- A conditional action matches a list of conditions against the request and applies the first match. If no match could be found, a default action can be configured. Configuration ````````````` .. code-block:: yaml - path: / action: type: conditional defaultAction: type: redirect location: /appsuite/ conditions: - type: user-agent match: anyOf values: - Calendar - Reminders - DataAccess - DAVKit - Lightning action: type: redirect location: https://dav.example.com/ :conditions: **Mandatory:** The conditions that should be checked with the actions that should be applied on a match :defaultAction: **Mandatory:** The default action that should be used if no other action can be applied Conditions `````````` Conditions have a distinct set of attributes that help to identify the condition and the matching algorithm. The properties are: :type: **Mandatory:** The type of the condition handler that should be applied. Possible values: ``user-agent``. :match: **Mandatory:** The matching algorithm that should be applied. Regular expression to match against. Expressions starting with an exclamation mark (!) are negated. Possible match value keys are: ``value:`` Literal value (regex) to match against, ``anyOf:`` List of regular expressions, evaluated in the specified order. Any matching value fulfils the condition., ``allOf:`` List of regular expressions, evaluated in the specified order. All values must match to fulfil the condition. :values: **Mandatory:** A list of values for the matcher :action: **Mandatory:** An action that should be applied. Can be any of the known actions, another conditional action included. User agent condition ```````````````````` Checks the value of the requests user-agent header and applies the configured action. WebSocket Tunnel ---------------- The ``ws-tunnel`` action actually forwards an incoming HTTP request to an upstream/origin server but unlike the forward action this action enables proper handshake handling and establish a tunneled connection between client and server after successful handshake roundtrip. Configuration ````````````` .. code-block:: yaml - path: /socket.io action: type: ws-tunnel backendPool: oxcluster clientConfig: readTimeout: 60000 - path: /appsuite/rt2 action: type: ws-tunnel rewritePath: /rt2 backendPool: oxcluster clientConfig: readTimeout: 60000 :backendPool: **Mandatory:** Define backend pool to forward the request to. Values are taken from ``name`` values found under the global or shards section in backends.yml :rewritePath: **Optional:** Rewrite request path to specified value before forwarding the request to an origin server. :clientConfig: **Optional:** Additional configuration parameters that can be applied to the client that handles the forwarding. For additional information see :ref:`client configuration ` section. After successful establish a WebSocket tunnel the settings did not have any affect any more expect the readTimeout. After exceeding this value without any read operation proxy will terminate the connection. If read timeout is set to 0 proxy hold the connection open until client or origin terminate the connection. .. _routing_restrictions: Restrictions ============ It is possible to limit the access to paths of a vhost for requests that match certain restrictions. A restriction can either allow or deny to handle a request if matching a certain criteria. IP-based access restrictions ---------------------------- Restrict access by client IP. .. code-block:: yaml - path: '/webservices' action: ... restrictions: # Possible restriction types: # client-ip: Client IP address (from client socket, PROXY protocol or X-Forwarded-For header) - type: 'client-ip' # (ALLOW, DENY|DENY, ALLOW) order: 'ALLOW, DENY' # List of IP addresses and subnets in CIDR notation. '*' matches any IP. allowFrom: - '192.168.0.0/24' - '127.0.0.1' - '::1' - 'fd35:8e34:80d5:5fc6::/64' denyFrom: - '*' The order determines how the specified IP addresses are to be checked. In this example, the allowed IP addresses are checked first and afterwards the denied addresses. An asterisk represents all addresses, which in this example means that all addresses that are not explicitly allowed are rejected. In a path without restrictions, all IP addresses are allowed and none are rejected. The check is terminated as soon as a match is found. By specifying the subnet mask (CIDR), it is also possible to filter by individual subnets. If this is omitted, only the specified IP address will be filtered. For a detailed explanation review the developers guide on :ref:`IP-based access restrictions ` .. _response_header: Response Headers ================ It is possible to alter upstream response headers before sending them back to the client. Therefor headers can be added to configuration on vHost and / or rule level. In case of any ambiguous naming the rule level configuration takes precedence over vHost level configuration. Whether changes to response headers take place or not depends on the HTTP response status and the override flag ``always``. If this flag is set to ``true`` changes will always be applied, otherwise (set to false or even empty) changes will only be applied if response status is one of ``200, 201, 204, 206, 301, 302, 303, 304, 307 or 308``. The concrete change which would be applied to origin response headers can be defined by a type attribute: * ``add`` -> would simply add the configured header no matter which ones already exists on original response * ``set`` -> would first remove all headers with the given name from origin and then add a new single header with the configured value * ``remove`` -> would remove all headers with the given name. In case a value is configured only headers with the exact name and value will be removed from origin response If no value is configured the default ``add`` will be used. Example with further comments ----------------------------- .. code-block:: yaml vhosts: - hostNames: - 'webmail.example.com' responseHeaders: - name: 'Access-Control-Allow-Origin' # value is mandatory for types 'add' and 'set' and optional for 'remove' value: 'wss://webmail-ws.example.com' # mandatory for types 'add' and 'set' # type values: add [default], set, remove type: 'add' # default: false always: true rules: - path: / responseHeaders: - name: 'Content-Security-Policy' value: 'connect-src \self\ wss://webmail-ws.example.com' type: 'set' always: true Full Example ============ .. literalinclude:: config-examples/routing_full_commented.yml :language: yaml