wrouter 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wrouter-0.1.0/.clang-format +39 -0
- wrouter-0.1.0/.gitignore +64 -0
- wrouter-0.1.0/LICENSE +28 -0
- wrouter-0.1.0/Makefile +41 -0
- wrouter-0.1.0/NOTES +3 -0
- wrouter-0.1.0/PKG-INFO +266 -0
- wrouter-0.1.0/README.md +218 -0
- wrouter-0.1.0/TODO +6 -0
- wrouter-0.1.0/bindings/cpp/meson.build +30 -0
- wrouter-0.1.0/bindings/cpp/tests/test_cpp.cpp +183 -0
- wrouter-0.1.0/bindings/cpp/wrouter.cpp +233 -0
- wrouter-0.1.0/bindings/cpp/wrouter.hpp +350 -0
- wrouter-0.1.0/bindings/python/TODO +18 -0
- wrouter-0.1.0/bindings/python/builder.c +157 -0
- wrouter-0.1.0/bindings/python/builder.h +22 -0
- wrouter-0.1.0/bindings/python/dispatcher.c +131 -0
- wrouter-0.1.0/bindings/python/dispatcher.h +17 -0
- wrouter-0.1.0/bindings/python/meson.build +18 -0
- wrouter-0.1.0/bindings/python/router.c +38 -0
- wrouter-0.1.0/bindings/python/router.h +16 -0
- wrouter-0.1.0/bindings/python/tests/test_build_router.py +30 -0
- wrouter-0.1.0/bindings/python/tests/test_builder.py +61 -0
- wrouter-0.1.0/bindings/python/tests/test_builder_threading.py +41 -0
- wrouter-0.1.0/bindings/python/tests/test_dispatcher.py +349 -0
- wrouter-0.1.0/bindings/python/tests/test_dispatcher_threading.py +23 -0
- wrouter-0.1.0/bindings/python/tests/test_dispatcher_types.py +27 -0
- wrouter-0.1.0/bindings/python/tests/test_router.py +5 -0
- wrouter-0.1.0/bindings/python/wroutermodule.c +164 -0
- wrouter-0.1.0/bindings/python/wroutermodule.h +4 -0
- wrouter-0.1.0/examples/cpp/callback.cpp +25 -0
- wrouter-0.1.0/examples/cpp/hello.cpp +26 -0
- wrouter-0.1.0/examples/cpp/http_resolve.cpp +53 -0
- wrouter-0.1.0/examples/cpp/meson.build +34 -0
- wrouter-0.1.0/examples/cpp/no_context.cpp +22 -0
- wrouter-0.1.0/examples/cpp/resolve_lambda.cpp +41 -0
- wrouter-0.1.0/examples/libmicrohttpd/README.md +13 -0
- wrouter-0.1.0/examples/libmicrohttpd/main.c +158 -0
- wrouter-0.1.0/examples/libmicrohttpd/meson.build +10 -0
- wrouter-0.1.0/include/wrouter.h +138 -0
- wrouter-0.1.0/meson.build +139 -0
- wrouter-0.1.0/meson_options.txt +6 -0
- wrouter-0.1.0/pyproject.toml +36 -0
- wrouter-0.1.0/requirements.txt +2 -0
- wrouter-0.1.0/src/arena.c +91 -0
- wrouter-0.1.0/src/arena.h +21 -0
- wrouter-0.1.0/src/builder.c +307 -0
- wrouter-0.1.0/src/builder.h +32 -0
- wrouter-0.1.0/src/common.h +6 -0
- wrouter-0.1.0/src/compiler.c +122 -0
- wrouter-0.1.0/src/dispatcher.c +126 -0
- wrouter-0.1.0/src/dispatcher.h +9 -0
- wrouter-0.1.0/src/errors.c +49 -0
- wrouter-0.1.0/src/graph.c +290 -0
- wrouter-0.1.0/src/graph.h +63 -0
- wrouter-0.1.0/src/lexer.c +72 -0
- wrouter-0.1.0/src/lexer.h +16 -0
- wrouter-0.1.0/src/params.c +161 -0
- wrouter-0.1.0/src/params.h +9 -0
- wrouter-0.1.0/src/prelexer.c +134 -0
- wrouter-0.1.0/src/prelexer.h +16 -0
- wrouter-0.1.0/src/router.c +205 -0
- wrouter-0.1.0/src/router.h +23 -0
- wrouter-0.1.0/src/segment.c +127 -0
- wrouter-0.1.0/src/segment.h +41 -0
- wrouter-0.1.0/src/symbol.c +216 -0
- wrouter-0.1.0/src/symbol.h +41 -0
- wrouter-0.1.0/src/terminal.c +50 -0
- wrouter-0.1.0/src/terminal.h +26 -0
- wrouter-0.1.0/src/token.c +14 -0
- wrouter-0.1.0/src/token.h +20 -0
- wrouter-0.1.0/tests/exp/README +1 -0
- wrouter-0.1.0/tests/exp/align.c +62 -0
- wrouter-0.1.0/tests/helpers/error_helpers.h +12 -0
- wrouter-0.1.0/tests/helpers/token_helpers.c +22 -0
- wrouter-0.1.0/tests/helpers/token_helpers.h +14 -0
- wrouter-0.1.0/tests/test_arena.c +136 -0
- wrouter-0.1.0/tests/test_builder.c +483 -0
- wrouter-0.1.0/tests/test_dispatcher.c +16 -0
- wrouter-0.1.0/tests/test_errors.c +43 -0
- wrouter-0.1.0/tests/test_graph.c +17 -0
- wrouter-0.1.0/tests/test_lexer.c +223 -0
- wrouter-0.1.0/tests/test_params.c +105 -0
- wrouter-0.1.0/tests/test_prelexer.c +386 -0
- wrouter-0.1.0/tests/test_router.c +674 -0
- wrouter-0.1.0/tests/test_symbol.c +244 -0
- wrouter-0.1.0/tests/test_terminal.c +82 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
BasedOnStyle: LLVM
|
|
2
|
+
IndentWidth: 4
|
|
3
|
+
TabWidth: 4
|
|
4
|
+
UseTab: Never
|
|
5
|
+
|
|
6
|
+
BreakBeforeBraces: Custom
|
|
7
|
+
BraceWrapping:
|
|
8
|
+
AfterFunction: true
|
|
9
|
+
AfterControlStatement: false
|
|
10
|
+
AfterStruct: false
|
|
11
|
+
AfterEnum: false
|
|
12
|
+
AfterUnion: false
|
|
13
|
+
BeforeElse: false
|
|
14
|
+
BeforeCatch: false
|
|
15
|
+
|
|
16
|
+
AllowShortIfStatementsOnASingleLine: false
|
|
17
|
+
AllowShortLoopsOnASingleLine: false
|
|
18
|
+
AllowShortFunctionsOnASingleLine: Empty
|
|
19
|
+
|
|
20
|
+
PointerAlignment: Right
|
|
21
|
+
ReferenceAlignment: Right
|
|
22
|
+
|
|
23
|
+
ColumnLimit: 100
|
|
24
|
+
|
|
25
|
+
IndentCaseLabels: true
|
|
26
|
+
SpaceBeforeParens: ControlStatements
|
|
27
|
+
|
|
28
|
+
SortIncludes: false
|
|
29
|
+
|
|
30
|
+
AlignConsecutiveAssignments: false
|
|
31
|
+
AlignConsecutiveDeclarations: false
|
|
32
|
+
|
|
33
|
+
Cpp11BracedListStyle: false
|
|
34
|
+
Standard: Latest
|
|
35
|
+
|
|
36
|
+
# Copied from: https://github.com/pytorch/pytorch/blob/main/.clang-format
|
|
37
|
+
StatementMacros:
|
|
38
|
+
- PyObject_HEAD
|
|
39
|
+
- PyVarObject_HEAD_INIT
|
wrouter-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Vim.
|
|
2
|
+
.*.swp
|
|
3
|
+
.*.swo
|
|
4
|
+
|
|
5
|
+
# Test coverage.
|
|
6
|
+
coverage*
|
|
7
|
+
|
|
8
|
+
# Ctags.
|
|
9
|
+
tags
|
|
10
|
+
|
|
11
|
+
# Prerequisites
|
|
12
|
+
*.d
|
|
13
|
+
|
|
14
|
+
# Object files
|
|
15
|
+
*.o
|
|
16
|
+
*.ko
|
|
17
|
+
*.obj
|
|
18
|
+
*.elf
|
|
19
|
+
|
|
20
|
+
# Linker output
|
|
21
|
+
*.ilk
|
|
22
|
+
*.map
|
|
23
|
+
*.exp
|
|
24
|
+
|
|
25
|
+
# Precompiled Headers
|
|
26
|
+
*.gch
|
|
27
|
+
*.pch
|
|
28
|
+
|
|
29
|
+
# Libraries
|
|
30
|
+
*.lib
|
|
31
|
+
*.a
|
|
32
|
+
*.la
|
|
33
|
+
*.lo
|
|
34
|
+
|
|
35
|
+
# Shared objects (inc. Windows DLLs)
|
|
36
|
+
*.dll
|
|
37
|
+
*.so
|
|
38
|
+
*.so.*
|
|
39
|
+
*.dylib
|
|
40
|
+
|
|
41
|
+
# Executables
|
|
42
|
+
*.out
|
|
43
|
+
*.app
|
|
44
|
+
*.i*86
|
|
45
|
+
*.x86_64
|
|
46
|
+
*.hex
|
|
47
|
+
|
|
48
|
+
# Debug files
|
|
49
|
+
*.dSYM/
|
|
50
|
+
*.su
|
|
51
|
+
*.idb
|
|
52
|
+
*.pdb
|
|
53
|
+
|
|
54
|
+
# Kernel Module Compile Results
|
|
55
|
+
*.mod*
|
|
56
|
+
*.cmd
|
|
57
|
+
.tmp_versions/
|
|
58
|
+
modules.order
|
|
59
|
+
Module.symvers
|
|
60
|
+
Mkfile.old
|
|
61
|
+
dkms.conf
|
|
62
|
+
|
|
63
|
+
# debug information files
|
|
64
|
+
*.dwo
|
wrouter-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Damien Bezborodov
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
wrouter-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
.PHONY: build test cpptests ctags format install clean pytest clang-tidy
|
|
2
|
+
|
|
3
|
+
build:
|
|
4
|
+
meson setup build
|
|
5
|
+
meson compile -C build
|
|
6
|
+
|
|
7
|
+
test: build
|
|
8
|
+
meson test -C build --print-errorlogs -v
|
|
9
|
+
|
|
10
|
+
cpptests: build
|
|
11
|
+
meson test -C build cpp --print-errorlogs -v
|
|
12
|
+
|
|
13
|
+
pytest: test
|
|
14
|
+
PYTHONPATH=build/bindings/python pytest -s bindings/python/tests/
|
|
15
|
+
|
|
16
|
+
ctags:
|
|
17
|
+
ctags -R --kinds-C=+stu --extras=+q -f tags .
|
|
18
|
+
|
|
19
|
+
format:
|
|
20
|
+
clang-format -i include/* src/* tests/*.c tests/helpers/* examples/libmicrohttpd/*.c bindings/python/*.c bindings/python/*.h
|
|
21
|
+
|
|
22
|
+
alltest: test cpptests pytest
|
|
23
|
+
|
|
24
|
+
coverage:
|
|
25
|
+
meson setup build-coverage -Db_coverage=true -Db_sanitize=none
|
|
26
|
+
meson compile -C build-coverage
|
|
27
|
+
meson test -C build-coverage --print-errorlogs -v
|
|
28
|
+
lcov --capture --directory build-coverage --output-file coverage.info
|
|
29
|
+
lcov --extract coverage.info "$$(pwd)/src/*" --output-file coverage.filtered.info
|
|
30
|
+
genhtml coverage.filtered.info --output-directory coverage_html
|
|
31
|
+
|
|
32
|
+
clang-tidy:
|
|
33
|
+
clang-tidy src/params.c -p build --fix --checks=misc-include-cleaner
|
|
34
|
+
|
|
35
|
+
clean:
|
|
36
|
+
meson setup --wipe build
|
|
37
|
+
|
|
38
|
+
install:
|
|
39
|
+
meson setup build
|
|
40
|
+
meson compile -C build
|
|
41
|
+
meson install -C build
|
wrouter-0.1.0/NOTES
ADDED
wrouter-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: wrouter
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fast and deterministric symbolic Web URL route resolver.
|
|
5
|
+
Author-Email: Damien Bezborodov <dbezborodov@gmail.com>
|
|
6
|
+
License: BSD 3-Clause License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026, Damien Bezborodov
|
|
9
|
+
|
|
10
|
+
Redistribution and use in source and binary forms, with or without
|
|
11
|
+
modification, are permitted provided that the following conditions are met:
|
|
12
|
+
|
|
13
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
14
|
+
list of conditions and the following disclaimer.
|
|
15
|
+
|
|
16
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
17
|
+
this list of conditions and the following disclaimer in the documentation
|
|
18
|
+
and/or other materials provided with the distribution.
|
|
19
|
+
|
|
20
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
21
|
+
contributors may be used to endorse or promote products derived from
|
|
22
|
+
this software without specific prior written permission.
|
|
23
|
+
|
|
24
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
25
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
26
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
27
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
28
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
29
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
30
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
31
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
32
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
33
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
34
|
+
|
|
35
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
36
|
+
Classifier: Intended Audience :: Developers
|
|
37
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
38
|
+
Classifier: Programming Language :: C
|
|
39
|
+
Classifier: Programming Language :: Python :: 3
|
|
40
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
41
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
42
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
43
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
44
|
+
Project-URL: Homepage, https://github.com/bezborodow/libwrouter
|
|
45
|
+
Project-URL: Bug Tracker, https://github.com/bezborodow/libwrouter/issues
|
|
46
|
+
Requires-Python: >=3.8
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
# Wrouter: Symbolic Web Router
|
|
50
|
+
|
|
51
|
+
Project status: This is in pre-release. PyPI package coming soon. There are
|
|
52
|
+
unit tests for both the C API and the CPython module.
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Python
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from wrouter import build_router, Dispatcher
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
routes = [
|
|
63
|
+
("/account", "account.list"),
|
|
64
|
+
("/account/create", "account.create"),
|
|
65
|
+
("/account/a/:account_id", "account.view")
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
dispatcher = Dispatcher(build_router(routes))
|
|
69
|
+
|
|
70
|
+
endpoint, params = dispatcher.resolve("/account/a/1234")
|
|
71
|
+
|
|
72
|
+
print(f"{endpoint} {params['account_id']}")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Will print: `account.view 1234`.
|
|
76
|
+
|
|
77
|
+
### C++
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
#include "wrouter.hpp"
|
|
81
|
+
|
|
82
|
+
#include <iostream>
|
|
83
|
+
#include <string>
|
|
84
|
+
|
|
85
|
+
struct Response {
|
|
86
|
+
std::string body;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
int main()
|
|
90
|
+
{
|
|
91
|
+
wrouter::Builder<Response> builder;
|
|
92
|
+
|
|
93
|
+
builder.add("/hello/:name", [](Response& response, wrouter::Params params) {
|
|
94
|
+
response.body = "Hello, " + params["name"] + "!";
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
auto router = builder.consume();
|
|
98
|
+
wrouter::Dispatcher dispatcher(router);
|
|
99
|
+
|
|
100
|
+
Response response;
|
|
101
|
+
dispatcher.dispatch("/hello/world", response);
|
|
102
|
+
|
|
103
|
+
std::cout << response.body << "\n";
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### C
|
|
108
|
+
|
|
109
|
+
```c
|
|
110
|
+
#include <stdio.h>
|
|
111
|
+
#include <wrouter.h>
|
|
112
|
+
|
|
113
|
+
static void route_hello(void *dispatch_ctx, const void *route_ctx, const wrouter_params_t *params)
|
|
114
|
+
{
|
|
115
|
+
const wrouter_param_t *addressee = wrouter_param(params, "addressee");
|
|
116
|
+
|
|
117
|
+
if (addressee)
|
|
118
|
+
printf("Hello, %.*s!\n", addressee->length, addressee->value);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
int main(void)
|
|
122
|
+
{
|
|
123
|
+
wrouter_error_t err;
|
|
124
|
+
wrouter_options_t options = { 0 };
|
|
125
|
+
wrouter_builder_t *builder = NULL;
|
|
126
|
+
wrouter_t *router = NULL;
|
|
127
|
+
wrouter_dispatcher_t *dispatcher = NULL;
|
|
128
|
+
|
|
129
|
+
builder = wrouter_builder_create(options);
|
|
130
|
+
if (builder == NULL)
|
|
131
|
+
goto failure;
|
|
132
|
+
|
|
133
|
+
err = wrouter_add_handler(builder, "/hello/:addressee", route_hello);
|
|
134
|
+
if (err)
|
|
135
|
+
goto failure;
|
|
136
|
+
|
|
137
|
+
router = wrouter_consume(&builder, &err);
|
|
138
|
+
if (err)
|
|
139
|
+
goto failure;
|
|
140
|
+
|
|
141
|
+
dispatcher = wrouter_dispatcher_create(router);
|
|
142
|
+
if (dispatcher == NULL)
|
|
143
|
+
goto failure;
|
|
144
|
+
|
|
145
|
+
wrouter_dispatch(dispatcher, "/hello/world", NULL);
|
|
146
|
+
|
|
147
|
+
wrouter_dispatcher_destroy(&dispatcher);
|
|
148
|
+
wrouter_destroy(&router);
|
|
149
|
+
|
|
150
|
+
return 0;
|
|
151
|
+
|
|
152
|
+
failure:
|
|
153
|
+
fprintf(stderr, "%s\n", wrouter_strerror(err));
|
|
154
|
+
wrouter_builder_destroy(&builder);
|
|
155
|
+
wrouter_dispatcher_destroy(&dispatcher);
|
|
156
|
+
wrouter_destroy(&router);
|
|
157
|
+
return 1;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Compile with:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
gcc -o hello hello.c -lwrouter
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Will print: `Hello, world!`
|
|
168
|
+
|
|
169
|
+
## Concepts
|
|
170
|
+
|
|
171
|
+
This project is designed as a router for use in a Web application server that
|
|
172
|
+
is assumed to be behind an HTTP proxy. As such, it makes no attempt to handle
|
|
173
|
+
hostnames, subdomains, or aliases. The router does not handle HTTP parsing or
|
|
174
|
+
request handling, and as such should be used in conjunction with other
|
|
175
|
+
libraries.
|
|
176
|
+
|
|
177
|
+
The router is intended to be fast, cache-efficient, and deterministic. It is
|
|
178
|
+
therefore deliberately restrictive in what forms of routes can be accepted into
|
|
179
|
+
the routing graph (see constraints below). This results in a graph that is
|
|
180
|
+
simple and easy to traverse efficiently. The strict routing graph prevents
|
|
181
|
+
ambiguity in route resolution, avoiding the need for prioritisation or
|
|
182
|
+
resolving the specificity of conflicting routes.
|
|
183
|
+
|
|
184
|
+
The router has no concept of HTTP methods such as GET and POST. Therefore, a
|
|
185
|
+
router must be instantiated for each method supported by the application,
|
|
186
|
+
including a separate router for WebSockets, if desired.
|
|
187
|
+
|
|
188
|
+
The router is immutable, and is therefore thread-safe, and may be shared
|
|
189
|
+
between threads. The dispatcher is mutable and must not be shared between
|
|
190
|
+
threads.
|
|
191
|
+
|
|
192
|
+
## Constraints
|
|
193
|
+
|
|
194
|
+
A URL is divided into segments by the `/` character. A segment maybe either be
|
|
195
|
+
a literal string, parameter, or wildcard. Parameters are denoted by a colon
|
|
196
|
+
(default), angle, or brace; e.g., `:param`, `<param>`, or `{param}`,
|
|
197
|
+
respectively.
|
|
198
|
+
|
|
199
|
+
Wildcards (`*`) may only appear at the end of a route. Wildcards are evaluated
|
|
200
|
+
as a fallback. The most specific wildcard will match. A wildcard will consume
|
|
201
|
+
all segments following it.
|
|
202
|
+
|
|
203
|
+
- `/accounts/user/*/view` (invalid!)
|
|
204
|
+
- `/accounts/downloads/*`
|
|
205
|
+
- `/*`
|
|
206
|
+
|
|
207
|
+
A segment may only be a parameter or a literal.
|
|
208
|
+
|
|
209
|
+
- `/board/:board_id/ticket:ticket_id` (valid, but `ticket:ticket_id` will be parsed as a literal!)
|
|
210
|
+
- `/board/:board_id/ticket/:ticket_id`
|
|
211
|
+
|
|
212
|
+
Routes must not conflict with parameters and literals at the same level.
|
|
213
|
+
|
|
214
|
+
- `/project/list` and - `/project/:project_id` (incompatible!)
|
|
215
|
+
|
|
216
|
+
Wildcards are acceptable within at the same level at the end if they are
|
|
217
|
+
opposing a literal, not a parameter.
|
|
218
|
+
|
|
219
|
+
- `/project/list` and `/project/*`
|
|
220
|
+
- `/project/:project_id` and `/project/*` (incompatible!)
|
|
221
|
+
|
|
222
|
+
Parameters at the same level must share the same name.
|
|
223
|
+
|
|
224
|
+
- `/repos/:user/:repo` and `/repos/:user/:repo/tree/:branch/*`
|
|
225
|
+
- `/project/:id` and `/project/:project_id/accounts/:account_id` (incompatible!)
|
|
226
|
+
|
|
227
|
+
Routes with trailing slashes are distinct. The following are not equivalent:
|
|
228
|
+
|
|
229
|
+
- `/test`
|
|
230
|
+
- `/test/`
|
|
231
|
+
|
|
232
|
+
If it is desired to redirect from `/test` to `/test/`, this must be performed
|
|
233
|
+
explicitly by adding both routes, of which the former will redirect to the
|
|
234
|
+
latter.
|
|
235
|
+
|
|
236
|
+
Flexibility is not a goal of this project.
|
|
237
|
+
|
|
238
|
+
Where these constraints are severely limiting, the HTTP proxy should rewrite
|
|
239
|
+
URLs for the application server to consume. For example, `/new` could be
|
|
240
|
+
rewritten as `/repo/new`, while `/:user/:repo` could be rewritten as
|
|
241
|
+
`/repos/:user/:repo`. A small rewrite engine might be included in the future.
|
|
242
|
+
|
|
243
|
+
## Limitations
|
|
244
|
+
|
|
245
|
+
This is not an HTTP parser; as such, request paths containing query parameters
|
|
246
|
+
such as `/foo/bar?query=hi` will fail to match.
|
|
247
|
+
|
|
248
|
+
## Building
|
|
249
|
+
|
|
250
|
+
Compile:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
make
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Run tests:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
make test
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Install:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
sudo make install
|
|
266
|
+
```
|
wrouter-0.1.0/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Wrouter: Symbolic Web Router
|
|
2
|
+
|
|
3
|
+
Project status: This is in pre-release. PyPI package coming soon. There are
|
|
4
|
+
unit tests for both the C API and the CPython module.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
### Python
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
from wrouter import build_router, Dispatcher
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
routes = [
|
|
15
|
+
("/account", "account.list"),
|
|
16
|
+
("/account/create", "account.create"),
|
|
17
|
+
("/account/a/:account_id", "account.view")
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
dispatcher = Dispatcher(build_router(routes))
|
|
21
|
+
|
|
22
|
+
endpoint, params = dispatcher.resolve("/account/a/1234")
|
|
23
|
+
|
|
24
|
+
print(f"{endpoint} {params['account_id']}")
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Will print: `account.view 1234`.
|
|
28
|
+
|
|
29
|
+
### C++
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
#include "wrouter.hpp"
|
|
33
|
+
|
|
34
|
+
#include <iostream>
|
|
35
|
+
#include <string>
|
|
36
|
+
|
|
37
|
+
struct Response {
|
|
38
|
+
std::string body;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
int main()
|
|
42
|
+
{
|
|
43
|
+
wrouter::Builder<Response> builder;
|
|
44
|
+
|
|
45
|
+
builder.add("/hello/:name", [](Response& response, wrouter::Params params) {
|
|
46
|
+
response.body = "Hello, " + params["name"] + "!";
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
auto router = builder.consume();
|
|
50
|
+
wrouter::Dispatcher dispatcher(router);
|
|
51
|
+
|
|
52
|
+
Response response;
|
|
53
|
+
dispatcher.dispatch("/hello/world", response);
|
|
54
|
+
|
|
55
|
+
std::cout << response.body << "\n";
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### C
|
|
60
|
+
|
|
61
|
+
```c
|
|
62
|
+
#include <stdio.h>
|
|
63
|
+
#include <wrouter.h>
|
|
64
|
+
|
|
65
|
+
static void route_hello(void *dispatch_ctx, const void *route_ctx, const wrouter_params_t *params)
|
|
66
|
+
{
|
|
67
|
+
const wrouter_param_t *addressee = wrouter_param(params, "addressee");
|
|
68
|
+
|
|
69
|
+
if (addressee)
|
|
70
|
+
printf("Hello, %.*s!\n", addressee->length, addressee->value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
int main(void)
|
|
74
|
+
{
|
|
75
|
+
wrouter_error_t err;
|
|
76
|
+
wrouter_options_t options = { 0 };
|
|
77
|
+
wrouter_builder_t *builder = NULL;
|
|
78
|
+
wrouter_t *router = NULL;
|
|
79
|
+
wrouter_dispatcher_t *dispatcher = NULL;
|
|
80
|
+
|
|
81
|
+
builder = wrouter_builder_create(options);
|
|
82
|
+
if (builder == NULL)
|
|
83
|
+
goto failure;
|
|
84
|
+
|
|
85
|
+
err = wrouter_add_handler(builder, "/hello/:addressee", route_hello);
|
|
86
|
+
if (err)
|
|
87
|
+
goto failure;
|
|
88
|
+
|
|
89
|
+
router = wrouter_consume(&builder, &err);
|
|
90
|
+
if (err)
|
|
91
|
+
goto failure;
|
|
92
|
+
|
|
93
|
+
dispatcher = wrouter_dispatcher_create(router);
|
|
94
|
+
if (dispatcher == NULL)
|
|
95
|
+
goto failure;
|
|
96
|
+
|
|
97
|
+
wrouter_dispatch(dispatcher, "/hello/world", NULL);
|
|
98
|
+
|
|
99
|
+
wrouter_dispatcher_destroy(&dispatcher);
|
|
100
|
+
wrouter_destroy(&router);
|
|
101
|
+
|
|
102
|
+
return 0;
|
|
103
|
+
|
|
104
|
+
failure:
|
|
105
|
+
fprintf(stderr, "%s\n", wrouter_strerror(err));
|
|
106
|
+
wrouter_builder_destroy(&builder);
|
|
107
|
+
wrouter_dispatcher_destroy(&dispatcher);
|
|
108
|
+
wrouter_destroy(&router);
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Compile with:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
gcc -o hello hello.c -lwrouter
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Will print: `Hello, world!`
|
|
120
|
+
|
|
121
|
+
## Concepts
|
|
122
|
+
|
|
123
|
+
This project is designed as a router for use in a Web application server that
|
|
124
|
+
is assumed to be behind an HTTP proxy. As such, it makes no attempt to handle
|
|
125
|
+
hostnames, subdomains, or aliases. The router does not handle HTTP parsing or
|
|
126
|
+
request handling, and as such should be used in conjunction with other
|
|
127
|
+
libraries.
|
|
128
|
+
|
|
129
|
+
The router is intended to be fast, cache-efficient, and deterministic. It is
|
|
130
|
+
therefore deliberately restrictive in what forms of routes can be accepted into
|
|
131
|
+
the routing graph (see constraints below). This results in a graph that is
|
|
132
|
+
simple and easy to traverse efficiently. The strict routing graph prevents
|
|
133
|
+
ambiguity in route resolution, avoiding the need for prioritisation or
|
|
134
|
+
resolving the specificity of conflicting routes.
|
|
135
|
+
|
|
136
|
+
The router has no concept of HTTP methods such as GET and POST. Therefore, a
|
|
137
|
+
router must be instantiated for each method supported by the application,
|
|
138
|
+
including a separate router for WebSockets, if desired.
|
|
139
|
+
|
|
140
|
+
The router is immutable, and is therefore thread-safe, and may be shared
|
|
141
|
+
between threads. The dispatcher is mutable and must not be shared between
|
|
142
|
+
threads.
|
|
143
|
+
|
|
144
|
+
## Constraints
|
|
145
|
+
|
|
146
|
+
A URL is divided into segments by the `/` character. A segment maybe either be
|
|
147
|
+
a literal string, parameter, or wildcard. Parameters are denoted by a colon
|
|
148
|
+
(default), angle, or brace; e.g., `:param`, `<param>`, or `{param}`,
|
|
149
|
+
respectively.
|
|
150
|
+
|
|
151
|
+
Wildcards (`*`) may only appear at the end of a route. Wildcards are evaluated
|
|
152
|
+
as a fallback. The most specific wildcard will match. A wildcard will consume
|
|
153
|
+
all segments following it.
|
|
154
|
+
|
|
155
|
+
- `/accounts/user/*/view` (invalid!)
|
|
156
|
+
- `/accounts/downloads/*`
|
|
157
|
+
- `/*`
|
|
158
|
+
|
|
159
|
+
A segment may only be a parameter or a literal.
|
|
160
|
+
|
|
161
|
+
- `/board/:board_id/ticket:ticket_id` (valid, but `ticket:ticket_id` will be parsed as a literal!)
|
|
162
|
+
- `/board/:board_id/ticket/:ticket_id`
|
|
163
|
+
|
|
164
|
+
Routes must not conflict with parameters and literals at the same level.
|
|
165
|
+
|
|
166
|
+
- `/project/list` and - `/project/:project_id` (incompatible!)
|
|
167
|
+
|
|
168
|
+
Wildcards are acceptable within at the same level at the end if they are
|
|
169
|
+
opposing a literal, not a parameter.
|
|
170
|
+
|
|
171
|
+
- `/project/list` and `/project/*`
|
|
172
|
+
- `/project/:project_id` and `/project/*` (incompatible!)
|
|
173
|
+
|
|
174
|
+
Parameters at the same level must share the same name.
|
|
175
|
+
|
|
176
|
+
- `/repos/:user/:repo` and `/repos/:user/:repo/tree/:branch/*`
|
|
177
|
+
- `/project/:id` and `/project/:project_id/accounts/:account_id` (incompatible!)
|
|
178
|
+
|
|
179
|
+
Routes with trailing slashes are distinct. The following are not equivalent:
|
|
180
|
+
|
|
181
|
+
- `/test`
|
|
182
|
+
- `/test/`
|
|
183
|
+
|
|
184
|
+
If it is desired to redirect from `/test` to `/test/`, this must be performed
|
|
185
|
+
explicitly by adding both routes, of which the former will redirect to the
|
|
186
|
+
latter.
|
|
187
|
+
|
|
188
|
+
Flexibility is not a goal of this project.
|
|
189
|
+
|
|
190
|
+
Where these constraints are severely limiting, the HTTP proxy should rewrite
|
|
191
|
+
URLs for the application server to consume. For example, `/new` could be
|
|
192
|
+
rewritten as `/repo/new`, while `/:user/:repo` could be rewritten as
|
|
193
|
+
`/repos/:user/:repo`. A small rewrite engine might be included in the future.
|
|
194
|
+
|
|
195
|
+
## Limitations
|
|
196
|
+
|
|
197
|
+
This is not an HTTP parser; as such, request paths containing query parameters
|
|
198
|
+
such as `/foo/bar?query=hi` will fail to match.
|
|
199
|
+
|
|
200
|
+
## Building
|
|
201
|
+
|
|
202
|
+
Compile:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
make
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Run tests:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
make test
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Install:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
sudo make install
|
|
218
|
+
```
|
wrouter-0.1.0/TODO
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
cpp_sources = files('wrouter.cpp')
|
|
2
|
+
|
|
3
|
+
cpp_include_dirs = include_directories('.')
|
|
4
|
+
c_include_dirs = include_directories('../../include')
|
|
5
|
+
|
|
6
|
+
# 1. First define the library
|
|
7
|
+
wrouter_cpp = library(
|
|
8
|
+
'wrouter_cpp',
|
|
9
|
+
cpp_sources,
|
|
10
|
+
include_directories : [cpp_include_dirs, c_include_dirs],
|
|
11
|
+
dependencies : [libwrouter_dep],
|
|
12
|
+
install : true,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
install_headers('wrouter.hpp')
|
|
16
|
+
|
|
17
|
+
# 2. Then expose dependency
|
|
18
|
+
wrouter_cpp_dep = declare_dependency(
|
|
19
|
+
link_with : wrouter_cpp,
|
|
20
|
+
include_directories : [cpp_include_dirs, c_include_dirs],
|
|
21
|
+
dependencies : [libwrouter_dep],
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
cpp_test = executable(
|
|
25
|
+
'test_cpp',
|
|
26
|
+
'tests/test_cpp.cpp',
|
|
27
|
+
dependencies : wrouter_cpp_dep,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
test('cpp', cpp_test)
|