Introducing Internal Routing in NGINX Unit 1.8.0

Introducing Internal Routing in NGINX Unit 1.8.0

h4 {
font-weight:bolder;
font-size:110%;
}
h5 {
font-weight:bolder;
font-size:110%;
}

Today we’re announcing NGINX Unit 1.8.0, with a different approach than in the past: this time it’s example first, description after:

"routes": {
    "blogs": [
        {
            "match": {
                "uri": ["/phpmyadmin/*", "/wp-admin/*"]
            },
            "action": {
                "pass": "applications/blogs-admin"
            }
        },
        {
            "match": {
                "host": "!blog.example.com"
            },
            "action": {
                "pass":"applications/goaway"
            }
        },
        {
            "match": {
                "method": "POST",
                "uri": "/media/upload"
            },
            "action": {
                "pass":"applications/uploader"
            }
        }
    ]
}

Now, the details.

Internal Routing

When we released the initial version of NGINX Unit, some of our users wondered why the API included a separate listener object – it perhaps wasn’t clear why it was necessary. In NGINX Unit 1.8.0, we’re introducing the internal routing feature that makes use of the listener object in a more sophisticated way and enables new functionality in the application server.

Internal routing enables granular control over the target application based on the sets of policies that you define. Sample use cases include:

  • Requests to administrative URLs need a different security group and fewer application processes than the main application.
  • Requests to a wrong hostname need to be handled by a separate application that does not consume system resources.
  • POST requests are handled by a special app, maybe written in a different language.

Full documentation for internal routing is available at https://unit.nginx.org/configuration/#routes.

Introducing the routes Object

The new routes object is accessible through the NGINX Unit API, and located inside the /config object.

If you only need one set of routing rules for all available listeners, you can define the routes object as a JSON array:

"routes": [ { ... }, { ... }, { ... } ]

If you need more than one set of rules, define routes as a JSON object. Each named object inside of it is an array of one or more rules:

"routes": {
    "blogs-routes": [ { ... } , { ... } ],
    "security-routes": [ { ... }, { ... } ],
    "misc-routes": [ { ... } ]
}

You use the route names defined here when referring to the routes in other sections of the configuration.

Defining Routing Rules

Each rule in the route object consists of a match object and an action object.

The match Object

The match object defines the characteristics a request must have for the corresponding action to happen. You can omit the match if the rule is unconditional (“match always”). Version 1.8.0 supports three match objects:

  • host – The HTTP Host header, or the hostname that you usually see in the browser’s address bar
  • uri – The URI without the query string
  • method – The HTTP method, such as GET, POST, or PUT

Version 1.8.0 supports three types of matching:

  • Exact match.
  • Partial match. Use the * character (asterisk) to match any number of characters at the beginning or the end of the value, for example /api/*, *.php, or P* (the last to match the PUT and POST methods).
  • Negative match. Use the ! character (exclamation point) in front of the value, for example !api.example.com.

In future releases, we plan to support partial match at any position in the value, and after that regular expressions.

A match object can be defined either as a string or as an array.

The action Object

The other object in routing rules, action, is mandatory. NGINX Unit 1.8.0 supports only one action, pass, which passes the request to the specified application or route. We plan to add other actions, such as redirects, HTTP return codes, static files, and maybe more.

You can pass requests to either an application:

"action": {
    "pass": "applications/blogs-general"
}

or a route:

"action": {
    "pass": "routes/blogs"
}

Creating Routing Chains

Because the action object in a route object is the same as the action object in a listener object, you can chain routes together, as in the following example. Make sure you don’t loop them!

"routes": {
    "host-method": [
        {
            "match": {
                "host": "example.com",
                "method": ["GET", "HEAD"]
            },
            "action": {
                "pass": "routes/url-only"
            }
        },
        {
            "match": {
                "host": "blog.example.com"
            },
            "action": {
                "pass": "applications/blog"
            }
        }
    ],
    "url-only": [
        {
            "match": {
                "uri": "/api/*"
            },
            "action": {
                "pass": "applications/myapp"
            }
        }
    ]
}

Dynamically Changing Routing Rules

Like all other configuration objects in NGINX Unit, the routes object is accessed through the NGINX Unit API and stored in memory rather than in a file. This means you don’t have to edit a master configuration file to change a routing rule, nor do you need to restart NGINX Unit.

As previously mentioned, a route is represented as an array of one or more rules. When you want to change a rule, you reference it by its numerical index in the array, starting with 0.

In the following examples, we change each of the three routing rules in the example from the beginning of the post. Here’s the example again:

"routes": {
    "blogs": [
        {
            "match": {
                "uri": ["/phpmyadmin/*", "/wp-admin/*"]
            },
            "action": {
                "pass": "applications/blogs-admin"
            }
        },
        {
            "match": {
                "host": "!blog.example.com"
            },
            "action": {
                "pass":"applications/goaway"
            }
        },
        {
            "match": {
                "method": "POST",
                "uri": "/media/upload"
            },
            "action": {
                "pass":"applications/uploader"
            }
        }
    ]
}

In the first rule (index 0), we set the application that processes the main blogs route to applications/cms, replacing applications/blogs-admin:

curl -X PUT -d '"applications/cms"' --unix-socket=/run/control.unit.sock http://localhost/config/routes/blogs/0/action/pass

In the second rule (index 1), we set the matched hostname to www.example.com, replacing !blog.example.com:

curl -X PUT -d '"www.example.com"' --unix-socket=/run/control.unit.sock http://localhost/config/routes/blogs/1/match/host

In the third rule (index 2), we set the matched methods to both POST and PUT, instead of just POST:

curl -X PUT -d '["POST", "PUT"]' --unix-socket=/run/control.unit.sock http://localhost/config/routes/blogs/2/match/method

Minimal Config

There are different approaches to system configuration. Many engineers prefer to read full documentation first and create correct configuration from scratch. I usually build my configurations iteratively, starting with the minimal mandatory config and gradually expanding it with the values I want for the options. For this blog, I started with this minimal config (yours might be different):

{
    "certificates": {},
    "config": {
        "listeners": {
            "*:8400": {
                "pass": "routes/example"
            }
        },

        "applications": {
            "blogs": {
                "type": "php",
                "root": "/home/nick/demo/routing",
                "script": "demo.php"
            }
        },

        "routes": {
            "example": [
                {
                    "match": {
                        "host": "*",
                        "uri": "*",
                        "method": "*"
                    },

                    "action": {
                        "pass": "applications/blogs"
                    }
                }
            ]
        }
    }
}

Conclusions and Next Steps

Internal routing makes your applications more configurable and dynamic. We plan to extend this functionality in future releases to other Layer 7 objects such as HTTP headers and cookies, and to Layer 3 objects such as IP addresses and ports. Try it in your web application configuration, and let us know what configs you come up with. You can ask questions in our GitHub issue tracker (https://github.com/nginx/unit/issues) or the NGINX Unit mailing list (https://mailman.nginx.org/mailman/listinfo/unit).

Full documentation for internal routing is available at https://unit.nginx.org/configuration/#routes.

Want to create the next big feature for NGINX Unit? We are hiring! Check out the C engineer openings at https://www.nginx.com/careers/current-openings/?job_id=975599.

The post Introducing Internal Routing in NGINX Unit 1.8.0 appeared first on NGINX.

Source: Introducing Internal Routing in NGINX Unit 1.8.0

About KENNETH 10614 Articles
지락문화예술공작단

Be the first to comment

Leave a Reply

Your email address will not be published.


*


This site uses Akismet to reduce spam. Learn how your comment data is processed.