Crates.io | squall-router |
lib.rs | squall-router |
version | 0.2.2 |
source | src |
created_at | 2021-12-18 22:58:56.467918 |
updated_at | 2022-01-15 11:42:34.391867 |
description | HTTP router with path parameters extraction |
homepage | |
repository | https://github.com/mtag-dev/rs-squall-router |
max_upload_size | |
id | 500162 |
size | 116,491 |
Initially created as routing subsystem for Python's Squall API Framework.
Designed to mitigate the overhead of large-scale routing tables.
Squall router avoids massive Regex processing and performs validation only in those places where it is strictly necessary
The primary concept is finding relevant handlers using the HashMap-based database. Then perform computing only suitable entities and not the entire table. Regex matching is performed only for those fields which have defined data validators.
It is suitable for:
API Gateways
API services where routing cache not an option because of a lot of different parameters values
Just to ensure that routing is not a point of performance drop after adding a batch of new endpoints
Benchmark code based on matchit and actix-router code, so they are also here for reference.
use squall_router::SquallRouter;
fn main() {
let mut router = SquallRouter::new();
router.set_ignore_trailing_slashes();
router
.add_validator("int".to_string(), r"[0-9]+".to_string())
.unwrap();
router
.add_validator(
"uuid".to_string(),
r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}".to_string(),
)
.unwrap();
router.add_route(
"GET".to_string(),
"/route/without/dynamic/octets".to_string(),
0,
);
router.add_route(
"GET".to_string(),
"/route/aaa/{string_param}/bbb/{num_param:int}/ccc/{uuid_param:uuid}".to_string(),
1,
);
router.add_location("GET".to_string(), "/files/css".to_string(), 2);
let (handler_0, _parameters_0) = router
.resolve("GET", "/route/without/dynamic/octets")
.unwrap();
assert_eq!(handler_0, 0);
let (handler_1, parameters_1) = router
.resolve(
"GET",
"/route/aaa/aaa_value/bbb/1234/ccc/4bea5a51-1b80-4433-be06-d52726015591",
)
.unwrap();
assert_eq!(handler_1, 1);
assert_eq!(parameters_1, vec![
("string_param", "aaa_value"),
("num_param", "1234"),
("uuid_param", "4bea5a51-1b80-4433-be06-d52726015591")]
);
let (handler_2, _parameters_2) = router
.resolve("GET", "/files/css/vendor/style.css")
.unwrap();
assert_eq!(handler_2, 2);
}
Crate's routing database based on rustc_hash::FxHashMap which is non-cryptographic hash.
It is applicable and safe here because the database is filled only by endpoints registration and not during requests handling.
At the moment, there are two well known limitations
dynamic
and static
parameters in the same position/api/v1/user/{user_id}/info/full
/api/v1/user/parameter/{prm}/details
In case of such API design request to /api/v1/user/parameter/info/full
will not find the handler.
It is applicable only for routes with at least one dynamic parameter and with the same amount of octets.
We have checked how many APIs have such behavior, it is less than 1% which can be easily aligned with this contract.
In the next releases, it will be covered by assertion to prevent a bad user experience.
There are a few ways under discussion how to make such limitations not applicable.
/static/{path:.*}
- Dynamic routing part depends on octets splitting so it is not applicable.
Instead, you should use location api.