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 HTTPHost
header, or the hostname that you usually see in the browser’s address baruri
– The URI without the query stringmethod
– The HTTP method, such asGET
,POST
, orPUT
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
, orP*
(the last to match thePUT
andPOST
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.
Leave a Reply