Path Matching

The route rules in a Virtual Service can use path matching rules to match requests to routes based on the context path. There are three options that can be used for HTTP path matching. You can specify only one of the following three options within any given route matcher spec:

In this guide, we’re going to take a closer look at each matching type by creating an Upstream and then creating a Virtual Service to route requests to that Upstream based on the path submitted as part of the request.


Setup

This guide assumes that you have deployed Gloo to the gloo-system namespace and that the glooctl command line utility is installed on your machine. glooctl provides several convenient functions to view, manipulate, and debug Gloo resources; in particular, it is worth mentioning the following command, which we will use each time we need to retrieve the URL of the Gloo Gateway that is running inside your cluster:

glooctl proxy url

If you have not yet deployed Gloo, you can start by following the directions contained within the guide Installing Gloo Gateway on Kubernetes.

This guide also assumes that you are running Gloo Gateway in a Kubernetes cluster. Each example can be adapted to alternative deployments, such as using the HashiCorp stack of Nomad, Consul, and Vault.


Create an Upstream

First we are going to create a simple Upstream for testing called json-upstream, that routes to a static site.


apiVersion: gloo.solo.io/v1
kind: Upstream
metadata:
  name: json-upstream
  namespace: gloo-system
spec:
  static:
    hosts:
      - addr: jsonplaceholder.typicode.com
        port: 80

glooctl create upstream static --static-hosts jsonplaceholder.typicode.com:80 --name json-upstream

The site referenced in the Upstream is JSONPlaceholder - an online REST API for testing and prototyping.


Prefix Matching

To see how prefix matching is configured, let’s create a Virtual Service and route to that Upstream.


apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: test-prefix
  namespace: gloo-system
spec:
  virtualHost:
    domains:
      - 'foo'
    routes:
      - matchers:
         - prefix: /posts
        routeAction:
          single:
            upstream:
              name: json-upstream
              namespace: gloo-system
        options:
          autoHostRewrite: true

The prefix specified is /posts, meaning that any requests starting with /posts in the path will match this routing rule.

In the domains portion of the virtualHost spec we are specifying the foo domain, meaning that this Virtual Service will only answer requests made against the host foo. This is useful in case there are other existing Virtual Services using the wildcard (*) domain for matching. If we make a curl request and don’t provide the Host header with the value foo, the response will be a 404 as shown by the request below.

curl -v $(glooctl proxy url)/posts

This will print a 404 response containing:

< HTTP/1.1 404 Not Found
< date: Wed, 31 Jul 2019 17:20:23 GMT
< server: envoy
< content-length: 0

If we pass the Host header, we will successfully get results.

curl -H "Host: foo" $(glooctl proxy url)/posts
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  {
    "userId": 1,
    "id": 3,
    "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
    "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
  },
  ...
]

A request to /posts matches on the prefix /posts and is routed to the Upstream at jsonplaceholder.typicode.com:80. A request sent to / does not match the prefix /posts.

curl -v -H "Host: foo" $(glooctl proxy url)/

A 404 is generated because there is no match for the host foo and the path /.

> GET / HTTP/1.1
> Host: foo
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< date: Tue, 07 Jan 2020 20:10:37 GMT
< server: envoy
< content-length: 0

Let’s clean up this Virtual Service and look at exact matchers next.


kubectl delete vs -n gloo-system test-prefix

glooctl delete vs --name test-prefix

Exact matching

Now let’s configure a Virtual Service using the exact match option to route to our test Upstream. In this first example we are once again using the host foo and matching on the exact path /.


apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: test-exact-1
  namespace: gloo-system
spec:
  virtualHost:
    domains:
      - 'foo'
    routes:
      - matchers:
         - exact: /
        routeAction:
          single:
            upstream:
              name: json-upstream
              namespace: gloo-system
        options:
          autoHostRewrite: true

glooctl create vs --name test-exact-1 --namespace gloo-system --domains foo
glooctl add route --name test-exact-1 --path-exact / --dest-name json-upstream

A request to the path /posts is not an exact match to /, and should return a 404.

curl -v -H "Host: foo" $(glooctl proxy url)/posts

Let’s delete that Virtual Service and create a new one that works with the /posts path.


kubectl delete vs -n gloo-system test-exact-1

glooctl delete vs --name test-exact-1

We’re going to create a Virtual Service with a route that has an exact match on the path /posts.


apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: test-exact-2
  namespace: gloo-system
spec:
  virtualHost:
    domains:
      - 'foo'
    routes:
      - matchers:
         - exact: /posts
        routeAction:
          single:
            upstream:
              name: json-upstream
              namespace: gloo-system
        options:
          autoHostRewrite: true

glooctl create vs --name test-exact-2 --namespace gloo-system --domains foo
glooctl add route --name test-exact-2 --path-exact /posts --dest-name json-upstream

Let’s test the new Virtual Service by sending a request to the /posts path.

curl -v -H "Host: foo" $(glooctl proxy url)/posts

It will now return results.

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  {
    "userId": 1,
    "id": 3,
    "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
    "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
  },
  ...
]

You can try any number of different combination to see how the exact match option works. When you’re done, let’s clean up the exact match Virtual Server and check out the regex matcher.


kubectl delete vs -n gloo-system test-exact-2

glooctl delete vs --name test-exact-2

Regex Matching

Regex matching provides the most flexibility when using path matching, but it also adds complexity. Be sure to fully test your regex expressions before using them in production. Let’s create a route that uses a regex matcher to match any path of five characters in the set [a-z].

The complexity of the regex is constrained by the regex engine’s “program size” setting. If your regex is too complex, you may need to adjust the regexMaxProgramSize field in the GlooOptions section of your Settings resource.


apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: test-regex
  namespace: gloo-system
spec:
  virtualHost:
    domains:
      - 'foo'
    routes:
      - matchers:
         - regex: /[a-z]{5}
        routeAction:
          single:
            upstream:
              name: json-upstream
              namespace: gloo-system
        options:
          autoHostRewrite: true

glooctl create vs --name test-regex --namespace gloo-system --domains foo
glooctl add route --name test-regex --path-regex /[a-z]{5} --dest-name json-upstream

The regex matcher should work on the path /posts, but not on the path /comments or /list. The path /comments is over five characters and the path /list is less than five characters. Let’s test out the path /comments.

curl -v -H "Host: foo" $(glooctl proxy url)/comments

The result is a 404 response.

> GET /comments HTTP/1.1
> Host: foo
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 404 Not Found

However, the following requests will both succeed since they have exactly five characters in them:

curl -v -H "Host: foo" $(glooctl proxy url)/posts
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  {
    "userId": 1,
    "id": 3,
    "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
    "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
  },
  ...
]
curl -v -H "Host: foo" $(glooctl proxy url)/todos
[
  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "quis ut nam facilis et officia qui",
    "completed": false
  },
  {
    "userId": 1,
    "id": 3,
    "title": "fugiat veniam minus",
    "completed": false
  },
  {
    "userId": 1,
    "id": 4,
    "title": "et porro tempora",
    "completed": true
  },
  ...

You can replace this Virtual Service with other regex expressions to see how they react. When you are finished, let’s delete this virtual service.


kubectl delete vs -n gloo-system test-regex

glooctl delete vs --name test-regex

Summary

In this tutorial, we created a static Upstream and added a route on a Virtual Service to point to it. We learned how to use all 3 types of matchers allowed by Gloo when determining if a route configuration matches a request path: prefix, exact, and regex.

Let’s cleanup the test Upstream we used.


kubectl delete upstream -n gloo-system json-upstream

glooctl delete upstream json-upstream

Next Steps

Path matching rules are not the only rules available for routing decisions. We recommend checking out any of the following guides next: